Get dynamic component loading working

This commit is contained in:
Michael Mikovsky
2025-11-14 09:43:41 -07:00
parent cc2b2960e8
commit f34ac017ce
13 changed files with 119 additions and 82 deletions
+5 -5
View File
@@ -9,24 +9,24 @@ edition = "2024"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[features] [features]
default = ["client", "server"] default = ["client", "server", "log_debug"]
client = ["unshell-lib/client"] client = ["unshell-lib/client"]
server = ["unshell-lib/server"] server = ["unshell-lib/server"]
log_debug = ["unshell-lib/log_debug"]
obfuscate = ["unshell-lib/obfuscate"] obfuscate = ["unshell-lib/obfuscate"]
[dependencies] [dependencies]
unshell-lib = {path = "../unshell-lib"} unshell-lib = {path = "../unshell-lib", default-featues = false}
[profile.release] [profile.release]
strip = true # Strip symbols from the binary strip = true # Strip symbols from the binary
# strip = "debuginfo"
opt-level = "s" # Optimize for size opt-level = "s" # Optimize for size
lto = true lto = true
codegen-units = 1 codegen-units = 1
panic = "abort" panic = "abort"
debug = false debug = false
trim-paths = "all" trim-paths = "all"
# crate-type = ["cdylib"]
+2 -2
View File
@@ -1,5 +1,5 @@
// Behold! The world's most sophisticated shared library! (it has no code)
#![no_main] #![no_main]
pub use unshell_lib::get_components; pub use unshell_lib::get_components;
// Behold! The world's most sophisticated shared library!
+30 -24
View File
@@ -5,31 +5,37 @@ use unshell_lib::Announcement;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut serverruntime = unshell_lib::server::ListenerRuntime::new(); let mut serverruntime = unshell_lib::server::ListenerRuntime::new();
loop { // loop {
print!("> "); // print!("> ");
stdout().flush().expect("Failed to flush stdout"); // stdout().flush().expect("Failed to flush stdout");
let mut input = String::new(); // let mut input = String::new();
stdin().read_line(&mut input).expect("Failed to read line"); // stdin().read_line(&mut input).expect("Failed to read line");
let args = input.trim().split(" ").collect::<Vec<&str>>(); // let args = input.trim().split(" ").collect::<Vec<&str>>();
match args[0] { // match args[0] {
"" => {} // "" => {}
"test" => { // "test" => {
if let Some(arg) = args.get(1) { // if let Some(arg) = args.get(1) {
println!("Test with argument: {}", arg); // println!("Test with argument: {}", arg);
serverruntime // serverruntime
.send(&Announcement::TestAnnouncement(arg.to_string())) // .send(&Announcement::TestAnnouncement(arg.to_string()))
.unwrap(); // .unwrap();
} else { // } else {
println!("Test without argument"); // println!("Test without argument");
} // }
} // }
_ => { // _ => {
println!("Invalid Command: '{}'", args[0]); // println!("Invalid Command: '{}'", args[0]);
} // }
} // }
// println!("{:?}", args); // // println!("{:?}", args);
} // }
serverruntime.send(&Announcement::GetRuntimes)?;
// let response = serverruntime.
Ok(())
} }
+8
View File
@@ -1,8 +1,16 @@
use bincode::{Decode, Encode}; use bincode::{Decode, Encode};
use crate::config::RuntimeConfig;
#[derive(Debug, Encode, Decode)] #[derive(Debug, Encode, Decode)]
pub enum Announcement { pub enum Announcement {
TestAnnouncement(String), TestAnnouncement(String),
GetRuntimes,
GetRuntimesAck(usize),
StartRuntime(RuntimeConfig),
StartRuntimeAck(bool),
} }
const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard(); const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
+18 -11
View File
@@ -2,7 +2,7 @@ use std::{
io::Read, io::Read,
net::TcpStream, net::TcpStream,
sync::{ sync::{
Arc, Arc, Mutex,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
}, },
thread::{self, JoinHandle}, thread::{self, JoinHandle},
@@ -43,16 +43,6 @@ impl ClientRuntime {
} }
}; };
info!("Connected"); info!("Connected");
// let reader = BufReader::new(stream.try_clone().unwrap());
// let mut writer = BufWriter::new(stream.try_clone().unwrap());
// let (a, b) = crossbeam_channel::unbounded();
// a.
// if join_receiver.len() == 0 {
// join_receiver.recv().unwrap();
// }
while !join_clone.load(Ordering::Relaxed) { while !join_clone.load(Ordering::Relaxed) {
let mut size_buf = [0u8; 4]; let mut size_buf = [0u8; 4];
@@ -69,12 +59,29 @@ impl ClientRuntime {
Announcement::TestAnnouncement(s) => { Announcement::TestAnnouncement(s) => {
println!("Received test announcement: {}", s) println!("Received test announcement: {}", s)
} }
_ => {}
} }
} }
}), }),
join_signal, join_signal,
}) })
} }
// pub fn send(&mut self, announcement: &Announcement) -> Result<(), ModuleError> {
// let bytes = announcement.encode();
// let mut streams = self.stream.lock().unwrap();
// for stream in streams.iter_mut() {
// stream.write_all(&u32::to_be_bytes(bytes.len() as u32))?;
// stream.write_all(&bytes)?;
// stream.flush()?;
// }
// println!("Announcement {:?} sent", announcement);
// Ok(())
// }
} }
impl ModuleRuntime for ClientRuntime { impl ModuleRuntime for ClientRuntime {
-12
View File
@@ -4,20 +4,8 @@ use crate::config::NamedComponent;
#[obfuscated_symbol] #[obfuscated_symbol]
pub fn get_components() -> Vec<NamedComponent> { pub fn get_components() -> Vec<NamedComponent> {
// let mut components: HashMap<&'static str, Box<dyn Component>> = HashMap::new();
// let a = crate::client::get_interface;
return vec![ return vec![
#[cfg(feature = "client")] #[cfg(feature = "client")]
crate::client::get_named_component(), crate::client::get_named_component(),
]; ];
// components
// vec![
// Feature::Client,
// #[cfg(feature = "server")]
// Feature::Server,
// ]
} }
+6 -4
View File
@@ -3,6 +3,8 @@ use std::collections::HashMap;
// use bincode::{Decode, Encode}; // use bincode::{Decode, Encode};
// use serde::{Deserialize, Serialize}; // use serde::{Deserialize, Serialize};
use bincode::{Decode, Encode};
use crate::{ModuleError, ModuleRuntime}; use crate::{ModuleError, ModuleRuntime};
// /// Payload config that is instantiated // /// Payload config that is instantiated
@@ -19,11 +21,11 @@ pub struct PayloadConfig {
pub runtime_config: Vec<RuntimeConfig>, pub runtime_config: Vec<RuntimeConfig>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Encode, Decode)]
pub struct RuntimeConfig { pub struct RuntimeConfig {
pub parent_component: &'static str, pub parent_component: String,
pub name: &'static str, pub name: String,
pub config: HashMap<&'static str, String>, pub config: HashMap<String, String>,
} }
#[derive(Clone)] #[derive(Clone)]
+1 -6
View File
@@ -10,15 +10,10 @@ mod components;
pub use components::get_components; pub use components::get_components;
mod announcement; mod announcement;
use std::{ use std::fmt;
fmt,
// sync::{Arc, Mutex},
};
pub use announcement::Announcement; pub use announcement::Announcement;
// use crate::module::{Interface, Manager};
///Generic error type for module-related operations. ///Generic error type for module-related operations.
#[derive(Debug)] #[derive(Debug)]
pub enum ModuleError { pub enum ModuleError {
+18 -10
View File
@@ -19,18 +19,17 @@ pub struct Manager {
pub modules: Vec<Module>, pub modules: Vec<Module>,
active_runtimes: Vec<Box<dyn ModuleRuntime>>, active_runtimes: Vec<Box<dyn ModuleRuntime>>,
// runtime_config: Vec<RuntimeConfig>,
components: HashMap<String, NamedComponent>, components: HashMap<String, NamedComponent>,
} }
// static mut MANAGER_RUNTIME: Option<Arc<Mutex<Manager>>> = None; // static mut MANAGER_RUNTIME: Option<Arc<Mutex<Manager>>> = None;
impl Manager { impl Manager {
fn new(id: &'static str, config: Vec<NamedComponent>, modules: Vec<Module>) -> Self { fn new(id: &'static str, components: Vec<NamedComponent>, modules: Vec<Module>) -> Self {
Self { Self {
id, id,
modules, modules,
components: config components: components
.into_iter() .into_iter()
.map(|c| (c.name.to_string(), c)) .map(|c| (c.name.to_string(), c))
.collect(), .collect(),
@@ -44,6 +43,9 @@ impl Manager {
// Construct self // Construct self
let mut this = Self::new(&config.id, config.components.clone(), modules); let mut this = Self::new(&config.id, config.components.clone(), modules);
debug!("Imported {} base components", this.components.len());
debug!("Imported {} base runtimes", &config.runtime_config.len());
// Load each of the pre-prepared modules // Load each of the pre-prepared modules
this.load_components(); this.load_components();
@@ -60,7 +62,7 @@ impl Manager {
for module in &self.modules { for module in &self.modules {
// Load get_components function from shared object library // Load get_components function from shared object library
let component_func = match module let component_func = match module
.get_symbol::<fn() -> Vec<NamedComponent>>(symbol!(b"get_components")) .get_symbol::<fn() -> Vec<NamedComponent>>(symbol!("get_components").as_bytes())
{ {
Ok(func) => func, Ok(func) => func,
Err(_) => { Err(_) => {
@@ -70,7 +72,7 @@ impl Manager {
}; };
let components = component_func(); let components = component_func();
let component_name = "TODO"; let component_name = "TODO"; //TODO: Make this actually load component name
debug!("{} - Retrieved payload metadata", component_name); debug!("{} - Retrieved payload metadata", component_name);
@@ -88,11 +90,11 @@ impl Manager {
for runtime in runtimes { for runtime in runtimes {
let mut this_lock = this.lock().unwrap(); let mut this_lock = this.lock().unwrap();
let component = match this_lock.components.get(runtime.parent_component) { let component = match this_lock.components.get(&runtime.parent_component) {
Some(component) => component, Some(component) => component,
None => { None => {
warn!( warn!(
"Could not find component {} which is referenced by runtime {}", "Could not find component '{}' which is referenced by runtime: {}",
runtime.parent_component, runtime.name runtime.parent_component, runtime.name
); );
continue; continue;
@@ -119,12 +121,18 @@ impl Manager {
let mut this_lock = this.lock().unwrap(); let mut this_lock = this.lock().unwrap();
if this_lock.active_runtimes.len() <= 0 { if this_lock.active_runtimes.len() <= 0 {
debug!("There are no more runtimes! Exiting...");
break; break;
} }
this_lock this_lock.active_runtimes.retain(|runtime| {
.active_runtimes if runtime.is_running() {
.retain(|runtime| runtime.is_running()); true
} else {
debug!("Runtime exited!"); //TODO: Make this better
false
}
});
drop(this_lock); drop(this_lock);
+19 -1
View File
@@ -1,5 +1,5 @@
use std::{ use std::{
io::Write, io::{Read, Write},
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread::{self, JoinHandle}, thread::{self, JoinHandle},
@@ -52,6 +52,24 @@ impl ListenerRuntime {
Ok(()) Ok(())
} }
pub fn recv(&mut self) -> Result<Announcement, ModuleError> {
let stream = &mut self.streams.lock().unwrap()[0];
let mut size_buf = [0u8; 4];
stream.read_exact(&mut size_buf).unwrap();
let size = u32::from_be_bytes(size_buf);
let mut buf = vec![0u8; size as usize];
stream.read_exact(&mut buf).unwrap();
if let Some(announcement) = Announcement::decode(&buf) {
Ok(announcement)
} else {
Err(ModuleError::Error("Failed to decode announcement".into()))
}
}
} }
impl ModuleRuntime for ListenerRuntime { impl ModuleRuntime for ListenerRuntime {
+5 -3
View File
@@ -7,7 +7,10 @@ edition = "2024"
[features] [features]
obfuscate = ["unshell-obfuscate/obfuscate"] obfuscate = ["unshell-obfuscate/obfuscate"]
log_debug = ["unshell-lib/log_debug"] log_debug = ["unshell-lib/log_debug"]
# default = ["obfuscate"]
client = ["unshell-lib/client"]
server = ["unshell-lib/server"]
[dependencies] [dependencies]
env_logger = "0.11.8" env_logger = "0.11.8"
@@ -15,7 +18,7 @@ lazy_static = "1.5.0"
libloading = "0.8.9" libloading = "0.8.9"
proc-macro2 = "1.0.103" proc-macro2 = "1.0.103"
unshell-lib = {path = "../unshell-lib"} unshell-lib = {path = "../unshell-lib", default-features = false}
unshell-obfuscate = {path = "../unshell-obfuscate"} unshell-obfuscate = {path = "../unshell-obfuscate"}
[profile.release] [profile.release]
@@ -26,4 +29,3 @@ codegen-units = 1
panic = "abort" panic = "abort"
debug = false # Remove debug debug = false # Remove debug
trim-paths="all" trim-paths="all"
# panic=immediate-abort
+3
View File
@@ -0,0 +1,3 @@
OBFUSCATION_KEY=abc123abc \
RUST_LOG=info \
cargo run --no-default-features $@ --release # $(ls ../*/target/release/*.so)
+4 -4
View File
@@ -16,15 +16,15 @@ lazy_static! {
id: symbol!("Test ID"), id: symbol!("Test ID"),
components: unshell_lib::get_components(), components: unshell_lib::get_components(),
runtime_config: vec![RuntimeConfig { runtime_config: vec![RuntimeConfig {
parent_component: symbol!("client"), parent_component: symbol!("client").to_string(),
name: symbol!("client runtime"), name: symbol!("client runtime").to_string(),
config: HashMap::from([(symbol!("host"), obs!("localhost:1234"))]), config: HashMap::from([(symbol!("host").to_string(), obs!("localhost:1234"))]),
}], }],
}; };
} }
fn main() { fn main() {
// #[cfg(not(feature = "obfuscate"))] #[cfg(not(feature = "obfuscate"))]
unshell_lib::logger::PrettyLogger::init(); unshell_lib::logger::PrettyLogger::init();
debug!("Initialized"); debug!("Initialized");