diff --git a/src/header/mod.rs b/src/header/mod.rs index 006f5a6a87..7d70798519 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -124,7 +124,7 @@ pub mod setjmp; pub mod sgtty; pub mod shadow; pub mod signal; -// TODO: spawn.h +pub mod spawn; // TODO: stdalign.h (likely C implementation) // stdarg.h implemented in C // stdatomic.h implemented in C diff --git a/src/header/spawn/cbindgen.toml b/src/header/spawn/cbindgen.toml new file mode 100644 index 0000000000..0ebe663c2b --- /dev/null +++ b/src/header/spawn/cbindgen.toml @@ -0,0 +1,10 @@ +sys_includes = ["sched.h", "bits/sigset-t.h"] +include_guard = "_RELIBC_SPAWN_H" +language = "C" +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"sched_param" = "struct sched_param" \ No newline at end of file diff --git a/src/header/spawn/file_actions.rs b/src/header/spawn/file_actions.rs new file mode 100644 index 0000000000..3602b08daf --- /dev/null +++ b/src/header/spawn/file_actions.rs @@ -0,0 +1,200 @@ +use core::{ + ffi::{c_char, c_int}, + ptr::null, +}; + +use crate::{ + error::{Errno, Result}, + header::{ + errno::{EBADF, EINVAL, ENOMEM}, + stdlib::{free, malloc}, + string::memcpy, + }, + platform::types::{c_void, mode_t}, +}; + +const OPEN: c_char = 1; +const CLOSE: c_char = 2; +const CHDIR: c_char = 3; +const FCHDIR: c_char = 4; +const DUP2: c_char = 5; + +#[repr(C)] +enum Operation { + Open { + fd: c_int, + path: *const c_char, + flag: c_int, + mode: mode_t, + }, + Close(c_int), + Chdir(*const c_char), + FChdir(c_int), + Dup2(c_int, c_int), +} + +#[repr(C)] +pub struct posix_spawn_file_actions_t { + size: usize, + operation: *const Operation, +} + +fn copy_op(file_actions: *mut posix_spawn_file_actions_t, op: Operation) -> Result<()> { + if file_actions.is_null() { + return Err(Errno(EINVAL)); + } + + let new = unsafe { malloc((*file_actions).size + size_of::()) }; + + if new == -1isize as *mut c_void { + return Err(Errno(ENOMEM)); + } + + unsafe { + if (*file_actions).size >= size_of::() { + memcpy( + new, + (*file_actions).operation as *const c_void, + (*file_actions).size / size_of::(), + ); + + free((*file_actions).operation as *mut c_void); + } + + (*file_actions).operation = new as *const Operation; + + let new = new as *mut Operation; + + (*new.add((*file_actions).size)) = op; + (*file_actions).size += size_of::() + } + + Ok(()) +} + +#[unsafe(no_mangle)] +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; + } + + unsafe { + (*file_actions).operation = null(); + (*file_actions).size = 0; + } + + 0 +} + +#[unsafe(no_mangle)] +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; + } + + unsafe { + free((*file_actions).operation as *mut c_void); + (*file_actions).operation = null(); + (*file_actions).size = 0; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn_file_actions_addopen( + file_actions: *mut posix_spawn_file_actions_t, + fd: c_int, + path: *const c_char, + oflag: c_int, + mode: mode_t, +) -> c_int { + if fd < 0 { + return EBADF; + } + + let open_op = Operation::Open { + fd, + path, + flag: oflag, + mode, + }; + + if let Err(e) = copy_op(file_actions, open_op) { + return e.0; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn_file_actions_addclose( + file_actions: *mut posix_spawn_file_actions_t, + fd: c_int, +) -> c_int { + if fd < 0 { + return EBADF; + } + + let close_op = Operation::Close(fd); + + if let Err(e) = copy_op(file_actions, close_op) { + return e.0; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn_file_actions_addchdir( + file_actions: *mut posix_spawn_file_actions_t, + path: *const c_char, +) -> c_int { + let chdir_op = Operation::Chdir(path); + + if let Err(e) = copy_op(file_actions, chdir_op) { + return e.0; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn_file_actions_addfchdir( + file_actions: *mut posix_spawn_file_actions_t, + fd: c_int, +) -> c_int { + if fd < 0 { + return EBADF; + } + + let fchdir_op = Operation::FChdir(fd); + + if let Err(e) = copy_op(file_actions, fchdir_op) { + return e.0; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn_file_actions_adddup2( + file_actions: *mut posix_spawn_file_actions_t, + fd: c_int, + new: c_int, +) -> c_int { + if fd < 0 || new < 0 { + return EBADF; + } + + let dup2_op = Operation::Dup2(fd, new); + + if let Err(e) = copy_op(file_actions, dup2_op) { + return e.0; + } + + 0 +} diff --git a/src/header/spawn/mod.rs b/src/header/spawn/mod.rs new file mode 100644 index 0000000000..76d237fb93 --- /dev/null +++ b/src/header/spawn/mod.rs @@ -0,0 +1,25 @@ +//! `spawn.h` implementation +//! +//! See . + +mod file_actions; +mod spawn_attr; + +use core::ffi::c_char; + +use crate::{ + header::spawn::{file_actions::posix_spawn_file_actions_t, spawn_attr::posix_spawnattr_t}, + platform::types::{c_int, pid_t}, +}; + +#[unsafe(no_mangle)] +pub fn posix_spawn( + pid: *const pid_t, + path: *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, +) -> c_int { + todo!() +} diff --git a/src/header/spawn/spawn_attr.rs b/src/header/spawn/spawn_attr.rs new file mode 100644 index 0000000000..397cbe1f08 --- /dev/null +++ b/src/header/spawn/spawn_attr.rs @@ -0,0 +1,254 @@ +use core::{ + ffi::{c_int, c_short}, + mem::zeroed, +}; + +use bitflags; + +use crate::header::{ + bits_sigset_t::sigset_t, + errno::EINVAL, + sched::{SCHED_FIFO, SCHED_OTHER, SCHED_RR, sched_param}, + sys_types_internal::pid_t, +}; + +bitflags::bitflags! { + struct Flags: c_short + { + const POSIX_SPAWN_RESETIDS = 1; + const POSIX_SPAWN_SETPGROUP = 2; + const POSIX_SPAWN_SETSIGDEF = 3; + const POSIX_SPAWN_SETSIGMASK = 4; + const POSIX_SPAWN_SETSCHEDPARAM = 5; + const POSIX_SPAWN_SETSCHEDULER = 6; + } +} + +pub const POSIX_SPAWN_RESETIDS: c_short = 1; +pub const POSIX_SPAWN_SETPGROUP: c_short = 2; +pub const POSIX_SPAWN_SETSIGDEF: c_short = 3; +pub const POSIX_SPAWN_SETSIGMASK: c_short = 4; +pub const POSIX_SPAWN_SETSCHEDPARAM: c_short = 5; +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, + policy: c_int, + sigdefault: sigset_t, + sigmask: sigset_t, +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> c_int { + if attr.is_null() { + return EINVAL; + } + + unsafe { + *attr = zeroed(); + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> c_int { + if attr.is_null() { + return EINVAL; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setschedparam( + attr: *mut posix_spawnattr_t, + schedparam: *const sched_param, +) -> c_int { + if attr.is_null() || schedparam.is_null() { + return EINVAL; + } + + unsafe { + (*attr).param = *schedparam; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getschedparam( + attr: *const posix_spawnattr_t, + schedparam: *mut sched_param, +) -> c_int { + if attr.is_null() || schedparam.is_null() { + return EINVAL; + } + + unsafe { + *schedparam = (*attr).param; + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setschedpolicy( + attr: *mut posix_spawnattr_t, + schedpolicy: c_int, +) -> c_int { + if attr.is_null() { + return EINVAL; + } + + match schedpolicy { + SCHED_FIFO | SCHED_RR | SCHED_OTHER => unsafe { + (*attr).policy = schedpolicy; + }, + _ => return EINVAL, + } + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getschedpolicy( + attr: *const posix_spawnattr_t, + schedpolicy: *mut c_int, +) -> c_int { + if attr.is_null() || schedpolicy.is_null() { + return EINVAL; + } + + unsafe { + *schedpolicy = (*attr).policy; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setsigdefault( + attr: *mut posix_spawnattr_t, + sigdefault: *const sigset_t, +) -> c_int { + if attr.is_null() || sigdefault.is_null() { + return EINVAL; + } + + unsafe { + (*attr).sigdefault = *sigdefault; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getsigdefault( + attr: *const posix_spawnattr_t, + sigdefault: *mut sigset_t, +) -> c_int { + if attr.is_null() || sigdefault.is_null() { + return EINVAL; + } + + unsafe { + *sigdefault = (*attr).sigdefault; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setsigmask( + attr: *mut posix_spawnattr_t, + sigmask: *const sigset_t, +) -> c_int { + if attr.is_null() || sigmask.is_null() { + return EINVAL; + } + + unsafe { + (*attr).sigmask = *sigmask; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getsigmask( + attr: *const posix_spawnattr_t, + sigmask: *mut sigset_t, +) -> c_int { + if attr.is_null() || sigmask.is_null() { + return EINVAL; + } + + unsafe { + *sigmask = (*attr).sigmask; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setflags( + attr: *mut posix_spawnattr_t, + flags: c_short, +) -> c_int { + if attr.is_null() { + return EINVAL; + } + + unsafe { + match Flags::from_bits(flags) { + Some(v) => (*attr).flags = v.bits(), + None => { + return EINVAL; + } + } + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getflags( + attr: *const posix_spawnattr_t, + flags: *mut c_short, +) -> c_int { + if attr.is_null() || flags.is_null() { + return EINVAL; + } + + unsafe { + *flags = (*attr).flags; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setpgroup( + attr: *mut posix_spawnattr_t, + pgroup: pid_t, +) -> c_int { + if attr.is_null() { + return EINVAL; + } + + unsafe { + (*attr).pgroup = pgroup; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getpgroup( + attr: *const posix_spawnattr_t, + pgroup: *mut pid_t, +) -> c_int { + if attr.is_null() || pgroup.is_null() { + return EINVAL; + } + + unsafe { + *pgroup = (*attr).pgroup; + } + 0 +} diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk index e82236799e..dddebeed2d 100644 --- a/tests/Makefile.tests.mk +++ b/tests/Makefile.tests.mk @@ -77,6 +77,7 @@ EXPECT_NAMES=\ sigaltstack \ signal \ sigsetjmp \ + spawn \ stdio/all \ stdio/buffer \ stdio/dprintf \ diff --git a/tests/spawn.c b/tests/spawn.c new file mode 100644 index 0000000000..b55f1d4286 --- /dev/null +++ b/tests/spawn.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define CHECK(cond, msg) \ + if (!cond) \ + { \ + fprintf(stderr, "Error in spawn.h: %s\n", msg); \ + _exit(EXIT_FAILURE); \ + } + +#define CHECK_WHEN_NULL(call, name) \ + if (!(call)) \ + { \ + fprintf(stderr, "Error in spawn.h: %s expected to fail when passing NULL\n", name); \ + _exit(EXIT_FAILURE); \ + } + +int main() +{ + posix_spawn_file_actions_t fa_t; + CHECK((posix_spawn_file_actions_init(&fa_t) == 0), "posix_spawn_file_actions_init failed") + CHECK_WHEN_NULL(posix_spawn_file_actions_init(NULL), "posix_spawn_file_actions_init") + CHECK((posix_spawn_file_actions_addopen(&fa_t, 1, ".", O_RDONLY, 1) == 0), "Error while adding open action") + CHECK((posix_spawn_file_actions_addclose(&fa_t, 1) == 0), "Error while adding close action") + CHECK((posix_spawn_file_actions_adddup2(&fa_t, -1, 3) == EBADF), "Adding dup2 with negative fd must fail") + CHECK((fa_t.size == 64), "Size remains unchanged") + CHECK_WHEN_NULL(posix_spawn_file_actions_destroy(NULL), "posix_spawn_file_actions_destroy") + CHECK_WHEN_NULL(posix_spawn_file_actions_addclose(NULL, 1), "posix_spawn_file_actions_addclose") + CHECK((posix_spawn_file_actions_destroy(&fa_t) == 0), "posix_spawn_file_actions_destroy failed") + CHECK((fa_t.size == 0), "Expected 0 size in posix_spawn_file_actions_t after destruction") + + posix_spawnattr_t sa; + CHECK((posix_spawnattr_init(&sa) == 0), "posix_spawnattr_init failed") + CHECK((sa.param.sched_priority == 0 && sa.flags == 0 && sa.pgroup == 0 && sa.policy == 0 && sa.sigdefault == 0 && sa.sigmask == 0), "All fields expected to be zero after init in posix_spawnattr_t") + CHECK_WHEN_NULL(posix_spawnattr_init(NULL), "posix_spawnattr_init") + CHECK_WHEN_NULL(posix_spawnattr_destroy(NULL), "posix_spawnattr_destroy") + + struct sched_param sp1; + sp1.sched_priority = 2; + struct sched_param sp2; + sp2.sched_priority = 0; + CHECK_WHEN_NULL(posix_spawnattr_setschedparam(NULL, &sp1), "posix_spawnattr_setschedparam") + CHECK_WHEN_NULL(posix_spawnattr_setschedparam(&sa, NULL), "posix_spawnattr_setschedparam") + CHECK((posix_spawnattr_setschedparam(&sa, &sp1) == 0), "posix_spawnattr_setschedparam failed") + CHECK_WHEN_NULL(posix_spawnattr_getschedparam(NULL, &sp2), "posix_spawnattr_getschedparam") + CHECK_WHEN_NULL(posix_spawnattr_getschedparam(&sa, NULL), "posix_spawnattr_getschedparam") + CHECK((posix_spawnattr_getschedparam(&sa, &sp2) == 0 && sp2.sched_priority == 2), "posix_spawnattr_getschedparam failed") + + CHECK_WHEN_NULL(posix_spawnattr_setschedpolicy(NULL, 0), "posix_spawnattr_setschedpolicy") + CHECK((posix_spawnattr_setschedpolicy(&sa, 1) == 0), "posix_spawnattr_setschedpolicy failed") + int pol = 0; + CHECK_WHEN_NULL(posix_spawnattr_getschedpolicy(NULL, &pol), "posix_spawnattr_getschedpolicy") + CHECK_WHEN_NULL(posix_spawnattr_getschedpolicy(&sa, NULL), "posix_spawnattr_getschedpolicy") + CHECK((posix_spawnattr_getschedpolicy(&sa, &pol) == 0 && pol == 1), "posix_spawnattr_getschedpolicy failed") + + sigset_t sst = 1; + sigset_t sst2 = 0; + CHECK_WHEN_NULL(posix_spawnattr_setsigdefault(NULL, &sst), "posix_spawnattr_setsigdefault") + CHECK_WHEN_NULL(posix_spawnattr_setsigdefault(&sa, NULL), "posix_spawnattr_setsigdefault") + CHECK((posix_spawnattr_setsigdefault(&sa, &sst) == 0), "posix_spawnattr_setsigdefault failed") + CHECK((posix_spawnattr_getsigdefault(&sa, &sst2) == 0 && sst2 == 1), "posix_spawnattr_getsigdefault failed") + CHECK_WHEN_NULL(posix_spawnattr_getsigdefault(NULL, &sst2), "posix_spawnattr_getsigdefault") + CHECK_WHEN_NULL(posix_spawnattr_getsigdefault(&sa, NULL), "posix_spawnattr_getsigdefault") + + sst = 3; + CHECK_WHEN_NULL(posix_spawnattr_setsigmask(NULL, &sst), "posix_spawnattr_setsigmask") + CHECK_WHEN_NULL(posix_spawnattr_setsigmask(&sa, NULL), "posix_spawnattr_setsigmask") + CHECK((posix_spawnattr_setsigmask(&sa, &sst) == 0), "posix_spawnattr_setsigmask failed") + CHECK_WHEN_NULL(posix_spawnattr_getsigmask(NULL, &sst2), "posix_spawnattr_getsigmask") + CHECK_WHEN_NULL(posix_spawnattr_getsigmask(&sa, NULL), "posix_spawnattr_getsigmask") + CHECK((posix_spawnattr_getsigmask(&sa, &sst2) == 0 && sst2 == 3), "posix_spawnattr_getsigmask failed") + + CHECK_WHEN_NULL(posix_spawnattr_setflags(NULL, 0), "posix_spawnattr_setflags") + CHECK((posix_spawnattr_setflags(&sa, 1) == 0), "posix_spawnattr_setflags failed") + short int pf = 0; + CHECK_WHEN_NULL(posix_spawnattr_getflags(NULL, &pf), "posix_spawnattr_getflags") + CHECK_WHEN_NULL(posix_spawnattr_getflags(&sa, NULL), "posix_spawnattr_getflags") + CHECK((posix_spawnattr_getflags(&sa, &pf) == 0 && pf == 1), "posix_spawnattr_getflags failed") + CHECK((posix_spawnattr_setflags(&sa, 7565) == EINVAL), "posix_spawnattr_setflags expected to fail when passing invalid bits") + + pid_t id = 0; + CHECK_WHEN_NULL(posix_spawnattr_setpgroup(NULL, 3), "posix_spawnattr_setpgroup") + CHECK((posix_spawnattr_setpgroup(&sa, 3) == 0), "posix_spawnattr_setpgroup failed") + CHECK_WHEN_NULL(posix_spawnattr_getpgroup(NULL, &id), "posix_spawnattr_getpgroup") + CHECK_WHEN_NULL(posix_spawnattr_getpgroup(&sa, NULL), "posix_spawnattr_getpgroup") + CHECK((posix_spawnattr_getpgroup(&sa, &id) == 0 && id == 3), "posix_spawnattr_getpgroup failed") +}