From 1bc472e7e1dcc5aa3c20b06750b2514577f538d2 Mon Sep 17 00:00:00 2001 From: R Aadarsh Date: Thu, 14 May 2026 18:47:42 +0530 Subject: [PATCH] Add `posix_spawn` and `posix_spawnp` for RedoxOS --- redox-rt/src/proc.rs | 6 +- src/header/spawn/file_actions.rs | 29 ++++----- src/header/spawn/mod.rs | 100 ++++++++++++++++++++++++++++--- src/header/spawn/spawn_attr.rs | 12 ++-- src/platform/pal/mod.rs | 9 +++ src/platform/redox/mod.rs | 69 ++++++++++++++++++++- 6 files changed, 190 insertions(+), 35 deletions(-) diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index c79d475d75..2682e2443b 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -1086,10 +1086,10 @@ pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { } pub struct NewChildProc { - proc_fd: Option, + pub proc_fd: Option, - thr_fd: FdGuardUpper, - pid: usize, + pub thr_fd: FdGuardUpper, + pub pid: usize, } pub fn new_child_process(args: &ForkArgs<'_>) -> Result { diff --git a/src/header/spawn/file_actions.rs b/src/header/spawn/file_actions.rs index 3602b08daf..2d1bcb05ac 100644 --- a/src/header/spawn/file_actions.rs +++ b/src/header/spawn/file_actions.rs @@ -20,7 +20,7 @@ const FCHDIR: c_char = 4; const DUP2: c_char = 5; #[repr(C)] -enum Operation { +pub enum Operation { Open { fd: c_int, path: *const c_char, @@ -76,14 +76,13 @@ fn copy_op(file_actions: *mut posix_spawn_file_actions_t, op: Operation) -> Resu pub unsafe extern "C" fn posix_spawn_file_actions_init( file_actions: *mut posix_spawn_file_actions_t, ) -> c_int { - if file_actions.is_null() { - return EINVAL; - } + let file_actions = match unsafe { file_actions.as_mut().ok_or(Errno(EINVAL)) } { + Ok(v) => v, + Err(_) => return EINVAL, + }; - unsafe { - (*file_actions).operation = null(); - (*file_actions).size = 0; - } + (*file_actions).operation = null(); + (*file_actions).size = 0; 0 } @@ -92,15 +91,13 @@ pub unsafe extern "C" fn posix_spawn_file_actions_init( pub unsafe extern "C" fn posix_spawn_file_actions_destroy( file_actions: *mut posix_spawn_file_actions_t, ) -> c_int { - if file_actions.is_null() { - return EINVAL; - } + let file_actions = match unsafe { file_actions.as_mut().ok_or(Errno(EINVAL)) } { + Ok(v) => v, + Err(_) => return EINVAL, + }; - unsafe { - free((*file_actions).operation as *mut c_void); - (*file_actions).operation = null(); - (*file_actions).size = 0; - } + (*file_actions).operation = null(); + (*file_actions).size = 0; 0 } diff --git a/src/header/spawn/mod.rs b/src/header/spawn/mod.rs index 76d237fb93..2d079f128a 100644 --- a/src/header/spawn/mod.rs +++ b/src/header/spawn/mod.rs @@ -5,21 +5,105 @@ mod file_actions; mod spawn_attr; -use core::ffi::c_char; +pub use file_actions::{Operation, posix_spawn_file_actions_t}; +pub use spawn_attr::{Flags, posix_spawnattr_t}; use crate::{ - header::spawn::{file_actions::posix_spawn_file_actions_t, spawn_attr::posix_spawnattr_t}, - platform::types::{c_int, pid_t}, + c_str::CStr, + error::{Errno, Result}, + header::{errno::ENOENT, stdlib::getenv}, + platform::{ + self, Pal, + types::{c_char, c_int, pid_t}, + }, }; +fn spawn( + mut program: &str, + file_actions: Option<&posix_spawn_file_actions_t>, + spawn_attr: Option<&posix_spawnattr_t>, + argv: *const *mut c_char, + envp: *const *mut c_char, + use_path: bool, +) -> Result { + if use_path { + let path_env = unsafe { + CStr::from_ptr(getenv("PATH".as_ptr() as *const c_char)) + .to_str() + .unwrap() + }; + let path_elements = path_env.split(':'); + let program_name = program.split("/").last().unwrap(); + let mut flag = false; + + for element in path_elements { + if element.split("/").last().unwrap() == program_name { + flag = true; + program = element; + break; + } + if !flag { + return Err(Errno(ENOENT)); + } + } + } + + unsafe { + platform::Sys::spawn( + CStr::from_bytes_with_nul_unchecked(program.as_bytes()), + file_actions, + spawn_attr, + argv, + envp, + use_path, + ) + } +} + #[unsafe(no_mangle)] -pub fn posix_spawn( +pub extern "C" fn posix_spawn( pid: *const pid_t, - path: *const c_char, + program: *const c_char, file_actions: *const posix_spawn_file_actions_t, spawn_attr: *const posix_spawnattr_t, - argv: *const *const c_char, - envp: *const *const c_char, + argv: *const *mut c_char, + envp: *const *mut c_char, ) -> c_int { - todo!() + let program = unsafe { CStr::from_ptr(program).to_str().unwrap() }; + + match spawn( + program, + unsafe { file_actions.as_ref() }, + unsafe { spawn_attr.as_ref() }, + argv, + envp, + false, + ) { + Ok(v) => v, + Err(e) => e.0, + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn posix_spawnp( + pid: *const pid_t, + program: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + spawn_attr: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + let program = unsafe { CStr::from_ptr(program).to_str().unwrap() }; + + match spawn( + program, + unsafe { file_actions.as_ref() }, + unsafe { spawn_attr.as_ref() }, + argv, + envp, + true, + ) { + Ok(v) => v, + Err(e) => e.0, + } } diff --git a/src/header/spawn/spawn_attr.rs b/src/header/spawn/spawn_attr.rs index 397cbe1f08..c109c4f261 100644 --- a/src/header/spawn/spawn_attr.rs +++ b/src/header/spawn/spawn_attr.rs @@ -13,7 +13,7 @@ use crate::header::{ }; bitflags::bitflags! { - struct Flags: c_short + pub struct Flags: c_short { const POSIX_SPAWN_RESETIDS = 1; const POSIX_SPAWN_SETPGROUP = 2; @@ -34,11 +34,11 @@ pub const POSIX_SPAWN_SETSCHEDULER: c_short = 6; #[repr(C)] pub struct posix_spawnattr_t { pub(crate) param: sched_param, - flags: c_short, - pgroup: c_int, + pub(crate) flags: c_short, + pub(crate) pgroup: c_int, policy: c_int, sigdefault: sigset_t, - sigmask: sigset_t, + pub(crate) sigmask: sigset_t, } #[unsafe(no_mangle)] @@ -217,9 +217,7 @@ pub unsafe extern "C" fn posix_spawnattr_getflags( return EINVAL; } - unsafe { - *flags = (*attr).flags; - } + unsafe { *flags = (*attr).flags } 0 } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index f3ed9595b3..a6c7be22dc 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -433,6 +433,15 @@ pub trait Pal { /// Platform implementation of [`setsid()`](crate::header::unistd::setsid) from [`unistd.h`](crate::header::unistd). fn setsid() -> Result; + unsafe fn spawn( + program: CStr, + fac: Option<&crate::header::spawn::posix_spawn_file_actions_t>, + fat: Option<&crate::header::spawn::posix_spawnattr_t>, + argv: *const *mut c_char, + envp: *const *mut c_char, + use_path: bool, + ) -> Result; + /// Platform implementation of [`symlink()`](crate::header::unistd::symlink) from [`unistd.h`](crate::header::unistd). fn symlink(path1: CStr, path2: CStr) -> Result<()> { Self::symlinkat(path1, AT_FDCWD, path2) diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 54967ade95..549891c9d2 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -39,6 +39,7 @@ use crate::{ signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent}, stdio::RENAME_NOREPLACE, stdlib::posix_memalign, + string::strlen, sys_file, sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE}, sys_random, @@ -67,7 +68,7 @@ pub use redox_rt::proc::FdGuard; mod epoll; mod event; -mod exec; +pub(crate) mod exec; mod extra; mod libcscheme; mod libredox; @@ -1192,6 +1193,72 @@ impl Pal for Sys { Ok(()) } + unsafe fn spawn( + program: CStr, + fac: Option<&crate::header::spawn::posix_spawn_file_actions_t>, + fat: Option<&crate::header::spawn::posix_spawnattr_t>, + mut argv: *const *mut c_char, + mut envp: *const *mut c_char, + use_path: bool, + ) -> Result { + let child = redox_rt::proc::new_child_process(&redox_rt::proc::ForkArgs::Managed)?; + let executable = File::open(program, fcntl::O_RDONLY)?; + let cwd = path::clone_cwd().unwrap(); + let proc_fd = child.proc_fd.unwrap(); + + let extra_info = redox_rt::proc::ExtraInfo { + cwd: Some(cwd.as_bytes()), + sigignmask: redox_rt::signal::get_sigignmask_to_inherit(), + sigprocmask: if let Some(fat) = fat + && crate::header::spawn::Flags::from_bits(fat.flags) + .unwrap() + .contains(crate::header::spawn::Flags::POSIX_SPAWN_SETSIGMASK) + { + fat.sigmask + } else { + redox_rt::signal::get_sigmask().unwrap() + }, + umask: redox_rt::sys::get_umask(), + thr_fd: child.thr_fd.as_raw_fd(), + proc_fd: proc_fd.as_raw_fd(), + ns_fd: redox_rt::current_namespace_fd().ok(), + cwd_fd: path::current_dir() + .ok() + .map(|fd| fd.as_ref().unwrap().fd.as_raw_fd()), + }; + + let mut args = Vec::new(); + let mut envs = Vec::new(); + + while unsafe { !(*argv).is_null() } { + let arg = unsafe { *argv }; + let len = unsafe { strlen(arg) }; + args.push(unsafe { slice::from_raw_parts(arg as *const u8, len) }); + argv = unsafe { argv.add(1) }; + } + + while unsafe { !(*envp).is_null() } { + let env = unsafe { *envp }; + let len = unsafe { strlen(env) }; + envs.push(unsafe { slice::from_raw_parts(env as *const u8, len) }); + envp = unsafe { envp.add(1) }; + } + + redox_rt::proc::fexec_impl( + FdGuard::new(executable.fd as usize).to_upper().unwrap(), + &child.thr_fd, + &proc_fd, + program.to_bytes(), + args.as_slice(), + envs.as_slice(), + &extra_info, + None, + ) + .map_err(|_| syscall::error::Error::new(syscall::error::ENOEXEC))?; + + Ok(i32::try_from(child.pid).unwrap()) + } + fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> { let mut file = File::createat( fd,