From 2051dca5b483209f0a231414e628130a20e46f57 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 25 Apr 2023 15:35:48 +0800 Subject: [PATCH] plugin_framework, support log callback Signed-off-by: fufesou --- .../desktop/pages/desktop_setting_page.dart | 7 +- flutter/lib/plugin/model.dart | 7 +- src/plugin/callback_msg.rs | 3 +- src/plugin/config.rs | 7 +- src/plugin/mod.rs | 1 + src/plugin/plog.rs | 34 ++++++ src/plugin/plugins.rs | 110 ++++++++++-------- 7 files changed, 118 insertions(+), 51 deletions(-) create mode 100644 src/plugin/plog.rs diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 9d5ab47da..68c6c7463 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1466,7 +1466,11 @@ class PluginCardState extends State { final children = [ _Button( 'Reload', - () => bind.pluginReload(id: widget.pluginId), + () async { + clearPlugin(widget.pluginId); + await bind.pluginReload(id: widget.pluginId); + setState(() {}); + }, ), _Checkbox( label: 'Enable', @@ -1476,6 +1480,7 @@ class PluginCardState extends State { clearPlugin(widget.pluginId); } await bind.pluginIdEnable(id: widget.pluginId, v: v); + setState(() {}); }, ), ]; diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart index aeb3072a2..24bcd560c 100644 --- a/flutter/lib/plugin/model.dart +++ b/flutter/lib/plugin/model.dart @@ -51,6 +51,11 @@ class LocationModel with ChangeNotifier { notifyListeners(); } + void remove(PluginId id) { + pluginModels.remove(id); + notifyListeners(); + } + bool get isEmpty => pluginModels.isEmpty; } @@ -68,7 +73,7 @@ PluginModel? getPluginModel(String location, PluginId id) => void clearPlugin(PluginId pluginId) { for (var element in _locationModels.values) { - element.pluginModels.remove(pluginId); + element.remove(pluginId); } } diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 1ab19e180..554ab595a 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -60,7 +60,8 @@ struct MsgToConfig { /// id: The id of this plugin. /// content: The content. /// len: The length of the content. -pub fn cb_msg( +#[no_mangle] +pub extern "C" fn cb_msg( peer: *const c_char, target: *const c_char, id: *const c_char, diff --git a/src/plugin/config.rs b/src/plugin/config.rs index a0b288486..076a70a17 100644 --- a/src/plugin/config.rs +++ b/src/plugin/config.rs @@ -20,6 +20,7 @@ lazy_static::lazy_static! { Arc::new(Mutex::new(conf)) }; } +use crate::ui_interface::get_id; pub(super) const CONFIG_TYPE_SHARED: &str = "shared"; pub(super) const CONFIG_TYPE_PEER: &str = "peer"; @@ -343,8 +344,12 @@ impl ManagerConfig { } } +pub(super) extern "C" fn cb_get_local_peer_id() -> *const c_char { + str_to_cstr_ret(&get_id()) +} + // Return shared config if peer is nullptr. -pub(super) fn cb_get_conf( +pub(super) extern "C" fn cb_get_conf( peer: *const c_char, id: *const c_char, key: *const c_char, diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 678feb071..ee93c19fd 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -5,6 +5,7 @@ mod callback_msg; mod config; pub mod desc; mod errno; +mod plog; pub mod ipc; mod plugins; diff --git a/src/plugin/plog.rs b/src/plugin/plog.rs new file mode 100644 index 000000000..9ef215d74 --- /dev/null +++ b/src/plugin/plog.rs @@ -0,0 +1,34 @@ +use hbb_common::log; +use std::ffi::c_char; + +const LOG_LEVEL_TRACE: &[u8; 6] = b"trace\0"; +const LOG_LEVEL_DEBUG: &[u8; 6] = b"debug\0"; +const LOG_LEVEL_INFO: &[u8; 5] = b"info\0"; +const LOG_LEVEL_WARN: &[u8; 5] = b"warn\0"; +const LOG_LEVEL_ERROR: &[u8; 6] = b"error\0"; + +#[inline] +fn is_level(level: *const c_char, level_bytes: &[u8]) -> bool { + level_bytes == unsafe { std::slice::from_raw_parts(level as *const u8, level_bytes.len()) } +} + +#[no_mangle] +pub(super) extern "C" fn log(level: *const c_char, msg: *const c_char) { + if level.is_null() || msg.is_null() { + return; + } + + if let Ok(msg) = super::cstr_to_string(msg) { + if is_level(level, LOG_LEVEL_TRACE) { + log::trace!("{}", msg); + } else if is_level(level, LOG_LEVEL_DEBUG) { + log::debug!("{}", msg); + } else if is_level(level, LOG_LEVEL_INFO) { + log::info!("{}", msg); + } else if is_level(level, LOG_LEVEL_WARN) { + log::warn!("{}", msg); + } else if is_level(level, LOG_LEVEL_ERROR) { + log::error!("{}", msg); + } + } +} diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index a69ea4052..e2eae87cf 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -1,7 +1,7 @@ use super::{desc::Desc, errno::*, *}; #[cfg(not(debug_assertions))] use crate::common::is_server; -use crate::{flutter, ui_interface::get_id}; +use crate::flutter; use hbb_common::{ allow_err, bail, dlopen::symbor::Library, @@ -21,8 +21,7 @@ const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0"; lazy_static::lazy_static! { static ref PLUGIN_INFO: Arc>> = Default::default(); - pub static ref PLUGINS: Arc>> = Default::default(); - pub static ref LOCAL_PEER_ID: Arc> = Default::default(); + static ref PLUGINS: Arc>> = Default::default(); } struct PluginInfo { @@ -32,25 +31,29 @@ struct PluginInfo { /// Initialize the plugins. /// -/// Return null ptr if success. -/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. -/// The plugin allocate memory with `libc::malloc` and return the pointer. -pub type PluginFuncInit = fn() -> *const c_void; -/// Reset the plugin. +/// data: The initialize data. /// /// Return null ptr if success. /// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. /// The plugin allocate memory with `libc::malloc` and return the pointer. -pub type PluginFuncReset = fn() -> *const c_void; +type PluginFuncInit = extern "C" fn(data: *const InitData) -> *const c_void; +/// Reset the plugin. +/// +/// data: The initialize data. +/// +/// Return null ptr if success. +/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. +/// The plugin allocate memory with `libc::malloc` and return the pointer. +type PluginFuncReset = extern "C" fn(data: *const InitData) -> *const c_void; /// Clear the plugin. /// /// Return null ptr if success. /// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. /// The plugin allocate memory with `libc::malloc` and return the pointer. -pub type PluginFuncClear = fn() -> *const c_void; +type PluginFuncClear = extern "C" fn() -> *const c_void; /// Get the description of the plugin. /// Return the description. The plugin allocate memory with `libc::malloc` and return the pointer. -pub type PluginFuncDesc = fn() -> *const c_char; +type PluginFuncDesc = extern "C" fn() -> *const c_char; /// Callback to send message to peer or ui. /// peer, target, id are utf8 strings(null terminated). /// @@ -59,17 +62,13 @@ pub type PluginFuncDesc = fn() -> *const c_char; /// id: The id of this plugin. /// content: The content. /// len: The length of the content. -type CallbackMsg = fn( +type CallbackMsg = extern "C" fn( peer: *const c_char, target: *const c_char, id: *const c_char, content: *const c_void, len: usize, ); -/// Callback to get the id of local peer id. -/// The returned string is utf8 string(null terminated). -/// Don't free the returned ptr. -type CallbackGetId = fn() -> *const c_char; /// Callback to get the config. /// peer, key are utf8 strings(null terminated). /// @@ -79,7 +78,17 @@ type CallbackGetId = fn() -> *const c_char; /// /// The returned string is utf8 string(null terminated) and must be freed by caller. type CallbackGetConf = - fn(peer: *const c_char, id: *const c_char, key: *const c_char) -> *const c_char; + extern "C" fn(peer: *const c_char, id: *const c_char, key: *const c_char) -> *const c_char; +/// Get local peer id. +/// +/// The returned string is utf8 string(null terminated) and must be freed by caller. +type CallbackGetId = extern "C" fn() -> *const c_char; +/// Callback to log. +/// +/// level, msg are utf8 strings(null terminated). +/// level: "error", "warn", "info", "debug", "trace". +/// msg: The message. +type CallbackLog = extern "C" fn(level: *const c_char, msg: *const c_char); /// The main function of the plugin. /// method: The method. "handle_ui" or "handle_peer" /// peer: The peer id. @@ -88,20 +97,41 @@ type CallbackGetConf = /// Return null ptr if success. /// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. /// The plugin allocate memory with `libc::malloc` and return the pointer. -pub type PluginFuncCall = fn( +type PluginFuncCall = extern "C" fn( method: *const c_char, peer: *const c_char, args: *const c_void, len: usize, ) -> *const c_void; +/// The plugin callbacks. +/// msg: The callback to send message to peer or ui. +/// get_conf: The callback to get the config. +/// log: The callback to log. #[repr(C)] +#[derive(Copy, Clone)] struct Callbacks { msg: CallbackMsg, - get_id: CallbackGetId, get_conf: CallbackGetConf, + get_id: CallbackGetId, + log: CallbackLog, +} + +/// The plugin initialize data. +/// version: The version of the plugin, can't be nullptr. +/// local_peer_id: The local peer id, can't be nullptr. +/// cbs: The callbacks. +#[repr(C)] +struct InitData { + version: *const c_char, + cbs: Callbacks, +} + +impl Drop for InitData { + fn drop(&mut self) { + free_c_ptr(self.version as _); + } } -type PluginFuncSetCallbacks = fn(Callbacks); macro_rules! make_plugin { ($($field:ident : $tp:ty),+) => { @@ -148,8 +178,8 @@ macro_rules! make_plugin { desc } - fn init(&self, path: &str) -> ResultType<()> { - let init_ret = (self.init)(); + fn init(&self, data: &InitData, path: &str) -> ResultType<()> { + let init_ret = (self.init)(data as _); if !init_ret.is_null() { let (code, msg) = get_code_msg_from_ret(init_ret); free_c_ptr(init_ret as _); @@ -192,8 +222,7 @@ make_plugin!( reset: PluginFuncReset, clear: PluginFuncClear, desc: PluginFuncDesc, - call: PluginFuncCall, - set_cbs: PluginFuncSetCallbacks + call: PluginFuncCall ); #[cfg(target_os = "windows")] @@ -260,21 +289,6 @@ pub fn reload_plugin(id: &str) -> ResultType<()> { load_plugin(Some(&path), Some(id)) } -#[no_mangle] -fn cb_get_local_peer_id() -> *const c_char { - let mut id = (*LOCAL_PEER_ID.read().unwrap()).clone(); - if id.is_empty() { - let mut lock = LOCAL_PEER_ID.write().unwrap(); - id = (*lock).clone(); - if id.is_empty() { - id = get_id(); - id.push('\0'); - *lock = id.clone(); - } - } - id.as_ptr() as _ -} - fn load_plugin_path(path: &str) -> ResultType<()> { let plugin = Plugin::new(path)?; let desc = plugin.desc()?; @@ -282,19 +296,21 @@ fn load_plugin_path(path: &str) -> ResultType<()> { // to-do validate plugin // to-do check the plugin id (make sure it does not use another plugin's id) - plugin.init(path)?; + let init_data = InitData { + version: str_to_cstr_ret(crate::VERSION), + cbs: Callbacks { + msg: callback_msg::cb_msg, + get_conf: config::cb_get_conf, + get_id: config::cb_get_local_peer_id, + log: super::plog::log, + }, + }; + plugin.init(&init_data, path)?; if change_manager() { super::config::ManagerConfig::add_plugin(desc.id())?; } - // set callbacks - (plugin.set_cbs)(Callbacks { - msg: callback_msg::cb_msg, - get_id: cb_get_local_peer_id, - get_conf: config::cb_get_conf, - }); - // update ui // Ui may be not ready now, so we need to update again once ui is ready. update_ui_plugin_desc(&desc, None);