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"]
[features]
default = ["client", "server"]
default = ["client", "server", "log_debug"]
client = ["unshell-lib/client"]
server = ["unshell-lib/server"]
log_debug = ["unshell-lib/log_debug"]
obfuscate = ["unshell-lib/obfuscate"]
[dependencies]
unshell-lib = {path = "../unshell-lib"}
unshell-lib = {path = "../unshell-lib", default-featues = false}
[profile.release]
strip = true # Strip symbols from the binary
# strip = "debuginfo"
opt-level = "s" # Optimize for size
lto = true
codegen-units = 1
panic = "abort"
debug = false
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]
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>> {
let mut serverruntime = unshell_lib::server::ListenerRuntime::new();
loop {
print!("> ");
stdout().flush().expect("Failed to flush stdout");
let mut input = String::new();
stdin().read_line(&mut input).expect("Failed to read line");
// loop {
// print!("> ");
// stdout().flush().expect("Failed to flush stdout");
// let mut input = String::new();
// 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] {
"" => {}
"test" => {
if let Some(arg) = args.get(1) {
println!("Test with argument: {}", arg);
serverruntime
.send(&Announcement::TestAnnouncement(arg.to_string()))
.unwrap();
} else {
println!("Test without argument");
}
}
_ => {
println!("Invalid Command: '{}'", args[0]);
}
}
// match args[0] {
// "" => {}
// "test" => {
// if let Some(arg) = args.get(1) {
// println!("Test with argument: {}", arg);
// serverruntime
// .send(&Announcement::TestAnnouncement(arg.to_string()))
// .unwrap();
// } else {
// println!("Test without argument");
// }
// }
// _ => {
// 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 crate::config::RuntimeConfig;
#[derive(Debug, Encode, Decode)]
pub enum Announcement {
TestAnnouncement(String),
GetRuntimes,
GetRuntimesAck(usize),
StartRuntime(RuntimeConfig),
StartRuntimeAck(bool),
}
const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
+18 -11
View File
@@ -2,7 +2,7 @@ use std::{
io::Read,
net::TcpStream,
sync::{
Arc,
Arc, Mutex,
atomic::{AtomicBool, Ordering},
},
thread::{self, JoinHandle},
@@ -43,16 +43,6 @@ impl ClientRuntime {
}
};
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) {
let mut size_buf = [0u8; 4];
@@ -69,12 +59,29 @@ impl ClientRuntime {
Announcement::TestAnnouncement(s) => {
println!("Received test announcement: {}", s)
}
_ => {}
}
}
}),
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 {
-12
View File
@@ -4,20 +4,8 @@ use crate::config::NamedComponent;
#[obfuscated_symbol]
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![
#[cfg(feature = "client")]
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 serde::{Deserialize, Serialize};
use bincode::{Decode, Encode};
use crate::{ModuleError, ModuleRuntime};
// /// Payload config that is instantiated
@@ -19,11 +21,11 @@ pub struct PayloadConfig {
pub runtime_config: Vec<RuntimeConfig>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Encode, Decode)]
pub struct RuntimeConfig {
pub parent_component: &'static str,
pub name: &'static str,
pub config: HashMap<&'static str, String>,
pub parent_component: String,
pub name: String,
pub config: HashMap<String, String>,
}
#[derive(Clone)]
+1 -6
View File
@@ -10,15 +10,10 @@ mod components;
pub use components::get_components;
mod announcement;
use std::{
fmt,
// sync::{Arc, Mutex},
};
use std::fmt;
pub use announcement::Announcement;
// use crate::module::{Interface, Manager};
///Generic error type for module-related operations.
#[derive(Debug)]
pub enum ModuleError {
+18 -10
View File
@@ -19,18 +19,17 @@ pub struct Manager {
pub modules: Vec<Module>,
active_runtimes: Vec<Box<dyn ModuleRuntime>>,
// runtime_config: Vec<RuntimeConfig>,
components: HashMap<String, NamedComponent>,
}
// static mut MANAGER_RUNTIME: Option<Arc<Mutex<Manager>>> = None;
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 {
id,
modules,
components: config
components: components
.into_iter()
.map(|c| (c.name.to_string(), c))
.collect(),
@@ -44,6 +43,9 @@ impl Manager {
// Construct self
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
this.load_components();
@@ -60,7 +62,7 @@ impl Manager {
for module in &self.modules {
// Load get_components function from shared object library
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,
Err(_) => {
@@ -70,7 +72,7 @@ impl Manager {
};
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);
@@ -88,11 +90,11 @@ impl Manager {
for runtime in runtimes {
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,
None => {
warn!(
"Could not find component {} which is referenced by runtime {}",
"Could not find component '{}' which is referenced by runtime: {}",
runtime.parent_component, runtime.name
);
continue;
@@ -119,12 +121,18 @@ impl Manager {
let mut this_lock = this.lock().unwrap();
if this_lock.active_runtimes.len() <= 0 {
debug!("There are no more runtimes! Exiting...");
break;
}
this_lock
.active_runtimes
.retain(|runtime| runtime.is_running());
this_lock.active_runtimes.retain(|runtime| {
if runtime.is_running() {
true
} else {
debug!("Runtime exited!"); //TODO: Make this better
false
}
});
drop(this_lock);
+19 -1
View File
@@ -1,5 +1,5 @@
use std::{
io::Write,
io::{Read, Write},
net::{TcpListener, TcpStream},
sync::{Arc, Mutex},
thread::{self, JoinHandle},
@@ -52,6 +52,24 @@ impl ListenerRuntime {
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 {
+5 -3
View File
@@ -7,7 +7,10 @@ edition = "2024"
[features]
obfuscate = ["unshell-obfuscate/obfuscate"]
log_debug = ["unshell-lib/log_debug"]
# default = ["obfuscate"]
client = ["unshell-lib/client"]
server = ["unshell-lib/server"]
[dependencies]
env_logger = "0.11.8"
@@ -15,7 +18,7 @@ lazy_static = "1.5.0"
libloading = "0.8.9"
proc-macro2 = "1.0.103"
unshell-lib = {path = "../unshell-lib"}
unshell-lib = {path = "../unshell-lib", default-features = false}
unshell-obfuscate = {path = "../unshell-obfuscate"}
[profile.release]
@@ -26,4 +29,3 @@ codegen-units = 1
panic = "abort"
debug = false # Remove debug
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"),
components: unshell_lib::get_components(),
runtime_config: vec![RuntimeConfig {
parent_component: symbol!("client"),
name: symbol!("client runtime"),
config: HashMap::from([(symbol!("host"), obs!("localhost:1234"))]),
parent_component: symbol!("client").to_string(),
name: symbol!("client runtime").to_string(),
config: HashMap::from([(symbol!("host").to_string(), obs!("localhost:1234"))]),
}],
};
}
fn main() {
// #[cfg(not(feature = "obfuscate"))]
#[cfg(not(feature = "obfuscate"))]
unshell_lib::logger::PrettyLogger::init();
debug!("Initialized");