From 0c049c585e55c57c5674d373e6ba342df539d253 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sat, 8 Apr 2023 17:46:47 +0800 Subject: [PATCH 1/3] add: initial plugin manager --- src/api.rs | 31 +++++++++++++++++++++++++++ src/lib.rs | 5 +++++ src/plugins.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/api.rs create mode 100644 src/plugins.rs diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 000000000..d62dc4d65 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,31 @@ +use std::{ffi::CStr, os::raw::c_char}; + +use crate::plugins::PLUGIN_REGISTRAR; + +pub type LoadPluginFunc = fn(*const i8) -> i32; +pub type UnloadPluginFunc = fn(*const i8) -> i32; + +pub struct RustDeskApiTable { + pub register_plugin: LoadPluginFunc, + pub unload_plugin: UnloadPluginFunc, +} + +#[no_mangle] +fn load_plugin(path: *const i8) -> i32 { + PLUGIN_REGISTRAR.load_plugin(path) +} + +#[no_mangle] +fn unload_plugin(path: *const i8) -> i32 { + PLUGIN_REGISTRAR.unload_plugin(path) +} + +impl Default for RustDeskApiTable { + fn default() -> Self { + let f = load_plugin; + Self { + register_plugin: load_plugin, + unload_plugin: unload_plugin, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5dcd6389c..af9f773ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,11 @@ mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +mod plugins; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +mod api; + mod tray; mod ui_cm_interface; diff --git a/src/plugins.rs b/src/plugins.rs new file mode 100644 index 000000000..bca77f8cc --- /dev/null +++ b/src/plugins.rs @@ -0,0 +1,57 @@ +use std::{collections::HashMap, path::Path, sync::Arc, ffi::CStr}; + +use hbb_common::anyhow::{anyhow, Error}; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref PLUGIN_REGISTRAR: Arc> = + Arc::new(PluginRegistar::::default()); +} + +pub trait Plugin { + // Return: the unique ID which identifies this plugin. + fn plugin_id(&self) -> String; + // Return: the name which is human-readable. + fn plugin_name(&self) -> String; +} + +#[derive(Default, Clone)] +pub struct PluginImpl { + id: String, + name: String, +} + +impl Plugin for PluginImpl { + fn plugin_id(&self) -> String { + self.id.to_owned() + } + + fn plugin_name(&self) -> String { + self.name.to_owned() + } +} + +#[derive(Default, Clone)] +pub struct PluginRegistar { + plugins: HashMap, +} + +impl PluginRegistar

{ + pub fn load_plugin(&self, path: *const i8) -> i32 { + let p = unsafe { CStr::from_ptr(path) }; + 0 + } + + pub fn unload_plugin(&self, path: *const i8) -> i32 { + let p = unsafe { CStr::from_ptr(path) }; + 0 + } +} + +impl TryFrom<&Path> for PluginImpl { + type Error = Error; + + fn try_from(value: &Path) -> Result { + Err(anyhow!("Not implemented yet.")) + } +} From 4e7e9406f5eead3715aced7c9b2e26de82b3d5de Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sat, 8 Apr 2023 18:17:13 +0800 Subject: [PATCH 2/3] feat: add vt --- Cargo.lock | 1 + Cargo.toml | 1 + src/api.rs | 8 ++++++- src/plugins.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d2984abe..5e14f72bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5146,6 +5146,7 @@ dependencies = [ "include_dir", "jni 0.19.0", "lazy_static", + "libloading", "libpulse-binding", "libpulse-simple-binding", "mac_address", diff --git a/Cargo.toml b/Cargo.toml index 48bd16045..5410ee9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4" cidr-utils = "0.5" +libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.14" diff --git a/src/api.rs b/src/api.rs index d62dc4d65..c8432f112 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,9 +2,11 @@ use std::{ffi::CStr, os::raw::c_char}; use crate::plugins::PLUGIN_REGISTRAR; +// API provided by RustDesk. pub type LoadPluginFunc = fn(*const i8) -> i32; pub type UnloadPluginFunc = fn(*const i8) -> i32; +#[repr(C)] pub struct RustDeskApiTable { pub register_plugin: LoadPluginFunc, pub unload_plugin: UnloadPluginFunc, @@ -20,9 +22,13 @@ fn unload_plugin(path: *const i8) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } +#[no_mangle] +fn get_api_table() -> RustDeskApiTable { + RustDeskApiTable::default() +} + impl Default for RustDeskApiTable { fn default() -> Self { - let f = load_plugin; Self { register_plugin: load_plugin, unload_plugin: unload_plugin, diff --git a/src/plugins.rs b/src/plugins.rs index bca77f8cc..42c74e27f 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -1,22 +1,42 @@ -use std::{collections::HashMap, path::Path, sync::Arc, ffi::CStr}; +use std::{ + collections::HashMap, + ffi::CStr, + path::Path, + sync::{Arc, RwLock}, +}; use hbb_common::anyhow::{anyhow, Error}; use lazy_static::lazy_static; +use libloading::Library; lazy_static! { pub static ref PLUGIN_REGISTRAR: Arc> = Arc::new(PluginRegistar::::default()); } +// API needed to be implemented by plugins. +pub type PluginInitFunc = fn() -> i32; +// API needed to be implemented by plugins. +pub type PluginDisposeFunc = fn() -> i32; pub trait Plugin { // Return: the unique ID which identifies this plugin. fn plugin_id(&self) -> String; // Return: the name which is human-readable. fn plugin_name(&self) -> String; + // Return: the virtual table of the plugin. + fn plugin_vt(&self) -> &RustDeskPluginTable; +} + +#[repr(C)] +#[derive(Default, Clone)] +pub struct RustDeskPluginTable { + pub init: Option, + pub dispose: Option, } #[derive(Default, Clone)] pub struct PluginImpl { + vt: RustDeskPluginTable, id: String, name: String, } @@ -29,29 +49,57 @@ impl Plugin for PluginImpl { fn plugin_name(&self) -> String { self.name.to_owned() } + + fn plugin_vt(&self) -> &RustDeskPluginTable { + &self.vt + } } #[derive(Default, Clone)] pub struct PluginRegistar { - plugins: HashMap, + plugins: Arc>>, } impl PluginRegistar

{ pub fn load_plugin(&self, path: *const i8) -> i32 { let p = unsafe { CStr::from_ptr(path) }; - 0 + let lib_path = p.to_str().unwrap_or("").to_owned(); + let lib = unsafe { libloading::Library::new(lib_path.as_str()) }; + match lib { + Ok(lib) => match lib.try_into() { + Ok(plugin) => { + PLUGIN_REGISTRAR + .plugins + .write() + .unwrap() + .insert(lib_path, plugin); + return 0; + } + Err(err) => { + eprintln!("Load plugin failed: {}", err); + } + }, + Err(err) => { + eprintln!("Load plugin failed: {}", err); + } + } + -1 } pub fn unload_plugin(&self, path: *const i8) -> i32 { let p = unsafe { CStr::from_ptr(path) }; - 0 + let lib_path = p.to_str().unwrap_or("").to_owned(); + match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) { + Some(_) => 0, + None => -1, + } } } -impl TryFrom<&Path> for PluginImpl { +impl TryFrom for PluginImpl { type Error = Error; - fn try_from(value: &Path) -> Result { - Err(anyhow!("Not implemented yet.")) + fn try_from(library: Library) -> Result { + todo!() } } From 34852224e9f5554c086bca47e4e71bbf719cb9b3 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 00:07:26 +0800 Subject: [PATCH 3/3] feat: add plugins and api table --- src/api.rs | 2 -- src/plugins.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/api.rs b/src/api.rs index c8432f112..f737243e7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,5 +1,3 @@ -use std::{ffi::CStr, os::raw::c_char}; - use crate::plugins::PLUGIN_REGISTRAR; // API provided by RustDesk. diff --git a/src/plugins.rs b/src/plugins.rs index 42c74e27f..7356380d3 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -1,13 +1,12 @@ use std::{ collections::HashMap, - ffi::CStr, - path::Path, + ffi::{c_char, CStr}, sync::{Arc, RwLock}, }; -use hbb_common::anyhow::{anyhow, Error}; +use hbb_common::{anyhow::Error, log::debug}; use lazy_static::lazy_static; -use libloading::Library; +use libloading::{Library, Symbol}; lazy_static! { pub static ref PLUGIN_REGISTRAR: Arc> = @@ -16,6 +15,10 @@ lazy_static! { // API needed to be implemented by plugins. pub type PluginInitFunc = fn() -> i32; // API needed to be implemented by plugins. +pub type PluginIdFunc = fn() -> *const c_char; +// API needed to be implemented by plugins. +pub type PluginNameFunc = fn() -> *const c_char; +// API needed to be implemented by plugins. pub type PluginDisposeFunc = fn() -> i32; pub trait Plugin { @@ -34,11 +37,22 @@ pub struct RustDeskPluginTable { pub dispose: Option, } -#[derive(Default, Clone)] pub struct PluginImpl { vt: RustDeskPluginTable, - id: String, - name: String, + pub id: String, + pub name: String, + _inner: Option, +} + +impl Default for PluginImpl { + fn default() -> Self { + Self { + _inner: None, + vt: Default::default(), + id: Default::default(), + name: Default::default(), + } + } } impl Plugin for PluginImpl { @@ -100,6 +114,73 @@ impl TryFrom for PluginImpl { type Error = Error; fn try_from(library: Library) -> Result { - todo!() + let init: Symbol = unsafe { library.get(b"plugin_init")? }; + let dispose: Symbol = unsafe { library.get(b"plugin_dispose")? }; + let id_func: Symbol = unsafe { library.get(b"plugin_id")? }; + let id_string = unsafe { + std::ffi::CStr::from_ptr(id_func()) + .to_str() + .unwrap_or("") + .to_owned() + }; + let name_func: Symbol = unsafe { library.get(b"plugin_name")? }; + let name_string = unsafe { + std::ffi::CStr::from_ptr(name_func()) + .to_str() + .unwrap_or("") + .to_owned() + }; + debug!( + "Successfully loaded the plugin called {} with id {}.", + name_string, id_string + ); + Ok(Self { + vt: RustDeskPluginTable { + init: Some(*init), + dispose: Some(*dispose), + }, + id: id_string, + name: name_string, + _inner: Some(library), + }) } } + +#[test] +#[cfg(target_os = "linux")] +fn test_plugin() { + use std::io::Write; + + let code = " + const char* plugin_name(){return \"test_name\";}; + const char* plugin_id(){return \"test_id\"; } + int plugin_init() {return 0;} + int plugin_dispose() {return 0;} + "; + let mut f = std::fs::File::create("test.c").unwrap(); + f.write_all(code.as_bytes()).unwrap(); + f.flush().unwrap(); + let mut cmd = std::process::Command::new("cc"); + cmd.arg("-fPIC") + .arg("-shared") + .arg("test.c") + .arg("-o") + .arg("libtest.so"); + // Spawn the compiler process. + let mut child = cmd.spawn().unwrap(); + // Wait for the compiler to finish. + let status = child.wait().unwrap(); + assert!(status.success()); + // Load the library. + let lib = unsafe { Library::new("./libtest.so").unwrap() }; + let plugin: PluginImpl = lib.try_into().unwrap(); + assert!(plugin._inner.is_some()); + assert!(plugin.name == "test_name"); + assert!(plugin.id == "test_id"); + assert!(PLUGIN_REGISTRAR + .plugins + .write() + .unwrap() + .insert("test".to_owned(), plugin) + .is_none()); +}