diff --git a/Cargo.lock b/Cargo.lock index aa6e71ad98..47568e432f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,15 +16,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt-pbkdf" @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "blake2" @@ -102,9 +102,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "num-traits", ] @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "drm-sys" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" +checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" dependencies = [ "libc", "linux-raw-sys", @@ -205,9 +205,9 @@ version = "0.1.0" [[package]] name = "goblin" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983a6aafb3b12d4c41ea78d39e189af4298ce747353945ff5105b54a056e5cd9" +checksum = "17582616a7718cca54cec18e534a76c7c4aec11a8b9a85695712f262fd15a4c8" dependencies = [ "log", "plain", @@ -244,15 +244,15 @@ version = "0.1.0" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" @@ -268,9 +268,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.6.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lock_api" @@ -283,9 +283,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "md-5" @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "num-traits" @@ -374,29 +374,29 @@ checksum = "66df580334caab2f744839ab1be85493d7ec731a92d6cf928008ab0b212bf3bc" [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -407,19 +407,19 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_jitter" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a02dd27aa28665e46e60168c8f355240c73b8a344d2557a92318849441ffda33" +checksum = "3fdcd80e68f0a8f9ca5ec7cfd02fd5fbb8fbe6ef4e9b90ea2f48bb929b74f88e" dependencies = [ "libc", - "rand_core 0.10.0", - "winapi", + "rand_core 0.10.1", + "windows-sys", ] [[package]] @@ -428,7 +428,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" dependencies = [ - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -472,9 +472,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7591fa2c6b601dfcfe5f043f65a1c39fcdf50efefcd7f1572e538c1f4b398d" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ "bitflags", ] @@ -602,9 +602,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "spin" @@ -623,9 +623,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -634,15 +634,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -656,28 +656,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 8a5878aba0..57dff8ebda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ ioslice = { version = "0.6", default-features = false } plain = "0.2" redox-path = "0.3" redox_protocols = { package = "libredox", version = "0.1.17", default-features = false, features = ["protocol"] } -redox_syscall = "0.8.0" +redox_syscall = "0.8.1" [build-dependencies] cc = "1" @@ -122,9 +122,7 @@ sc = "0.2.7" redox_syscall.workspace = true redox-rt = { path = "redox-rt" } redox-path.workspace = true -redox_event = { version = "0.4.7", default-features = false, features = [ - "redox_syscall", -] } +redox_event = { version = "0.4.7", default-features = false, features = ["redox_syscall"] } ioslice.workspace = true redox-ioctl = { path = "redox-ioctl" } redox_protocols.workspace = true diff --git a/redox-ioctl/Cargo.toml b/redox-ioctl/Cargo.toml index 0bf8029cab..5c89dfd6b6 100644 --- a/redox-ioctl/Cargo.toml +++ b/redox-ioctl/Cargo.toml @@ -8,6 +8,6 @@ description = "Ioctl definitions and (de)serialization for Redox" [dependencies] drm-sys = "0.8.0" -redox_syscall = "0.8" +redox_syscall = "0.8.1" [features] diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs index c79d475d75..f11cf5a4c1 100644 --- a/redox-rt/src/proc.rs +++ b/redox-rt/src/proc.rs @@ -63,6 +63,8 @@ pub struct ExtraInfo<'a> { pub ns_fd: Option, /// CWD handle pub cwd_fd: Option, + /// If the process for which the image is to be loaded the same as the currently running process + pub same_process: bool, } pub fn fexec_impl( @@ -74,7 +76,7 @@ pub fn fexec_impl( envs: &[&[u8]], extrainfo: &ExtraInfo, interp_override: Option, -) -> Result { +) -> Result> { // Here, we do the minimum part of loading an application, which is what the kernel used to do. // We load the executable into memory (albeit at different offsets in this executable), fix // some misalignments, and then switch address space. @@ -235,7 +237,7 @@ pub fn fexec_impl( } if let Some(interpreter_path) = interpreter { - return Ok(FexecResult::Interp { + return Ok(Some(FexecResult::Interp { path: interpreter_path, interp_override: InterpOverride { at_entry: base_addr + header.e_entry as usize, @@ -246,7 +248,7 @@ pub fn fexec_impl( min_mmap_addr, grants_fd: grants_fd.take(), }, - }); + })); } mmap_anon_remote( @@ -462,7 +464,7 @@ pub fn fexec_impl( )); // Close all O_CLOEXEC file descriptors. TODO: close_range? - { + if extrainfo.same_process { // NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded // scenarios. While execve() is undefined according to POSIX if there exist sibling // threads, it could still be allowed by keeping certain file descriptors and instead @@ -493,9 +495,13 @@ pub fn fexec_impl( } // Dropping this FD will cause the address space switch. - drop(addrspace_selection_fd); - unreachable!(); + if extrainfo.same_process { + drop(addrspace_selection_fd); + unreachable!(); + } else { + Ok(None) + } } fn write_usizes(fd: &FdGuardUpper, usizes: [usize; N]) -> Result { fd.write(unsafe { plain::as_bytes(&usizes) }) @@ -1086,10 +1092,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/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 190610b5de..1728f1ea62 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -340,7 +340,7 @@ pub struct Resugid { } /// Sets [res][ug]id, fields that are None will be unchanged. -pub fn posix_setresugid(ids: &Resugid>) -> Result<()> { +pub fn posix_setresugid(ids: &Resugid>, pid: Option) -> Result<()> { // TODO: not sure how "tmp" an IPC call is? let _sig_guard = tmp_disable_signals(); let mut guard = DYNAMIC_PROC_INFO.lock(); @@ -357,7 +357,16 @@ pub fn posix_setresugid(ids: &Resugid>) -> Result<()> { ids.sgid.unwrap_or(u32::MAX), ]); - this_proc_call(&mut buf, CallFlags::empty(), &[ProcCall::SetResugid as u64])?; + if let Some(pid) = pid { + proc_call( + pid, + &mut buf, + CallFlags::empty(), + &[ProcCall::SetResugid as u64], + )?; + } else { + this_proc_call(&mut buf, CallFlags::empty(), &[ProcCall::SetResugid as u64])?; + } if let Some(ruid) = ids.ruid { guard.ruid = ruid; diff --git a/src/header/mod.rs b/src/header/mod.rs index f89397d71e..d2f0cb310a 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -126,7 +126,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) pub mod stdarg; // 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..0a66991526 --- /dev/null +++ b/src/header/spawn/cbindgen.toml @@ -0,0 +1,20 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/spawn.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the mode_t and pid_t types as described in ." +# - "The header shall define the sigset_t type as described in ." +# - "The tag sched_param shall be declared as naming an incomplete structure type, the contents of which are described in the header." +# - "Inclusion of the header may make visible symbols defined in the and headers." +# +# shed.h brings in pid_t +# size_t would have been brought in by signal.h so we can include it here directly +sys_includes = ["sched.h", "bits/sigset-t.h", "bits/mode-t.h", "bits/size-t.h"] +include_guard = "_RELIBC_SPAWN_H" +language = "C" +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"sched_param" = "struct sched_param" diff --git a/src/header/spawn/file_actions.rs b/src/header/spawn/file_actions.rs new file mode 100644 index 0000000000..74b84e5509 --- /dev/null +++ b/src/header/spawn/file_actions.rs @@ -0,0 +1,221 @@ +use alloc::{ffi::CString, vec::Vec}; +use core::ptr; + +use crate::{ + header::errno::EBADF, + platform::types::{c_char, c_int, c_uchar, mode_t, size_t}, +}; + +#[derive(Debug, Clone)] +pub enum Action { + Open { + fd: c_int, + path: CString, + flag: c_int, + mode: mode_t, + }, + Close(c_int), + Chdir(CString), + FChdir(c_int), + Dup2(c_int, c_int), +} + +struct FileActions(Vec); + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct posix_spawn_file_actions_t { + __relibc_internal_size: [c_uchar; 24], + __relibc_internal_align: size_t, +} + +impl posix_spawn_file_actions_t { + pub fn add_action(&mut self, action: Action) { + let v = ptr::from_mut(self).cast::(); + unsafe { + (*v).0.push(action); + } + } +} + +pub struct FileActionsIter { + actions: posix_spawn_file_actions_t, + curr: usize, +} + +impl Iterator for FileActionsIter { + type Item = Action; + + fn next(&mut self) -> Option { + let actions = unsafe { + ptr::from_mut(&mut self.actions) + .cast::() + .as_ref() + .unwrap() + }; + let e = actions.0.get(self.curr)?; + self.curr += 1; + Some((*e).clone()) + } +} + +impl<'a> IntoIterator for &'a posix_spawn_file_actions_t { + type Item = Action; + + type IntoIter = FileActionsIter; + + fn into_iter(self) -> Self::IntoIter { + FileActionsIter { + actions: (*self).clone(), + curr: 0, + } + } +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +#[unsafe(no_mangle)] +pub extern "C" fn posix_spawn_file_actions_init( + file_actions: *mut posix_spawn_file_actions_t, +) -> c_int { + if file_actions.is_null() { + panic!("file_actions cannot be NULL"); + } + let v = Vec::new(); + let actions = FileActions(v); + unsafe { + ptr::write(file_actions.cast::(), actions); + } + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// If `file_actions` is not `NULL`, then it must be a pointer to a `file_actions` object that was initialised by calling `posix_spawn_file_actions_init`. +#[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() { + panic!("file_actions cannot be NULL"); + } + unsafe { + let _ = *(file_actions.cast::()); + } + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// `path` must be a valid null-terminated C string. +#[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 { + let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") }; + if fd < 0 { + return EBADF; + } + file_actions.add_action(Action::Open { + fd, + path: if path.is_null() { + CString::new("").unwrap() + } else { + unsafe { CString::from_raw(path as *mut c_char) } + }, + flag: oflag, + mode, + }); + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// `file_actions` must be initialised. +#[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 { + let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") }; + if fd < 0 { + return EBADF; + } + file_actions.add_action(Action::Close(fd)); + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// `file_actions` must be initialised and `path` must be a valid null-terminated C string. +#[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 file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") }; + file_actions.add_action(Action::Chdir(unsafe { + if path.is_null() { + CString::new("").unwrap() + } else { + CString::from_raw(path as *mut c_char) + } + })); + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// `file_actions` must be initialised. +#[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 { + let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") }; + if fd < 0 { + return EBADF; + } + file_actions.add_action(Action::FChdir(fd)); + 0 +} + +/// See +/// +/// Panics if `file_actions` is `NULL`. +/// +/// # Safety: +/// `file_actions` must be initialised. +#[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 { + let file_actions = unsafe { file_actions.as_mut().expect("file_actions cannot be NULL") }; + if fd < 0 || new < 0 { + return EBADF; + } + file_actions.add_action(Action::Dup2(fd, new)); + 0 +} diff --git a/src/header/spawn/mod.rs b/src/header/spawn/mod.rs new file mode 100644 index 0000000000..6f5ac703c9 --- /dev/null +++ b/src/header/spawn/mod.rs @@ -0,0 +1,218 @@ +//! `spawn.h` implementation +//! +//! See . + +mod file_actions; +mod spawn_attr; + +use core::str::FromStr; + +use alloc::{ + ffi::CString, + string::{String, ToString}, +}; +pub use file_actions::{Action, posix_spawn_file_actions_t}; +pub use spawn_attr::{Flags, posix_spawnattr_t}; + +use crate::{ + c_str::CStr, + error::{Errno, Result}, + header::{ + dirent::{opendir, readdir}, + errno::{ENOENT, ENOTDIR}, + limits::PATH_MAX, + stdlib::getenv, + unistd::{chdir, getcwd}, + }, + iter::NulTerminated, + platform::{ + self, ERRNO, Pal, + types::{c_char, c_int, pid_t}, + }, +}; + +unsafe fn spawn( + pid: Option<&mut pid_t>, + mut program: String, + file_actions: Option<&posix_spawn_file_actions_t>, + spawn_attr: Option<&posix_spawnattr_t>, + argv: NulTerminated<*mut c_char>, + envp: Option>, + use_path: bool, +) -> Result<()> { + let mut original_cwd = [0 as c_char; PATH_MAX]; + assert!( + unsafe { !getcwd(original_cwd.as_mut_ptr() as *mut c_char, PATH_MAX).is_null() }, + "Error getting cwd: {}", + ERRNO.get() + ); + + let mut path_path = None; + if use_path { + let path = unsafe { getenv(c"PATH".as_ptr()) }; + let path_env = unsafe { CStr::from_nullable_ptr(path).unwrap().to_str().unwrap() }; + let path_elements = path_env.split(':'); + let mut flag = false; + + 'a: for path_element in path_elements { + let pe = CString::from_str(path_element).unwrap(); + let dir = if let Some(dir) = + unsafe { opendir(pe.as_bytes_with_nul().as_ptr() as *const c_char).as_mut() } + { + dir + } else if ERRNO.get() == 0 || ERRNO.get() == ENOTDIR || ERRNO.get() == ENOENT { + continue; + } else { + return Err(Errno(ERRNO.get())); + }; + + while let Some(dir_ent) = unsafe { readdir(dir).as_ref() } { + let dir_ent_name = unsafe { + CStr::from_ptr(dir_ent.d_name.as_ptr() as *const c_char) + .to_str() + .unwrap() + .to_string() + }; + if dir_ent_name == program { + flag = true; + program = format!("{}/{}", path_element, program); + path_path = Some(path_element.to_string()); + break 'a; + } + } + } + + if !flag { + return Err(Errno(ENOENT)); + } + } + + let program = CString::from_str(program.as_str()).unwrap(); + + unsafe { + platform::Sys::spawn( + CStr::from_bytes_with_nul(program.as_bytes_with_nul()).unwrap(), + file_actions, + spawn_attr, + argv, + envp, + path_path, + ) + .map(|v| { + if let Some(pid) = pid { + *pid = v; + } + }) + .map_err(|e| { + let status = chdir(original_cwd.as_ptr() as *const c_char); + if status != 0 { + panic!("Error switching back to original cwd: {}", ERRNO.get()); + } + e + })?; + } + + Ok(()) +} + +/// See +/// +/// `argv` must **not** be `NULL` and must contain atleast the program name. `path` must also be **not** `NULL`. Failure to ensure any of this will result in a panic. +/// +/// # Safety: +/// `file_actions` and `attrp` must either be `NULL` or be pointers to properly initialised objects. Doing otherwise is undefined behaviour. +/// +/// `path` and the elements in `argv` must be a pointers to valid null-terminated character arrays. Failure to ensure any of this will result in undefined behaviour. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawn( + pid: *mut pid_t, + path: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + let argv = { + if argv.is_null() { + panic!("argv cannot be NULL") + } else { + if unsafe { (*argv).is_null() } { + panic!("argv must contain the program name"); + } + unsafe { NulTerminated::new(argv).unwrap() } + } + }; + let envp = unsafe { NulTerminated::new(envp) }; + let program = unsafe { + CStr::from_ptr(path) + .to_str() + .expect("path cannot be NULL") + .to_string() + }; + + if let Err(e) = unsafe { + spawn( + pid.as_mut(), + program, + file_actions.as_ref(), + attrp.as_ref(), + argv, + envp, + false, + ) + } { + return e.0; + } + 0 +} + +/// See +/// +/// `argv` must **not** be `NULL` and must contain atleast the program name. `path` must also be **not** `NULL`. Failure to ensure any of this will result in a panic. +/// +/// # Safety: +/// `file_actions` and `attrp` must either be `NULL` or be pointers to properly initialised objects. Doing otherwise is undefined behaviour. +/// +/// `path` and the elements in `argv` must be a pointers to valid null-terminated character arrays. Failure to ensure any of this will result in undefined behaviour. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnp( + pid: *mut pid_t, + path: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + let argv = { + if argv.is_null() { + panic!("argv cannot be NULL") + } else { + if unsafe { (*argv).is_null() } { + panic!("argv must contain the program name"); + } + unsafe { NulTerminated::new(argv).unwrap() } + } + }; + let envp = unsafe { NulTerminated::new(envp) }; + let program = unsafe { + CStr::from_ptr(path) + .to_str() + .expect("path cannot be NULL") + .to_string() + }; + + if let Err(e) = unsafe { + spawn( + pid.as_mut(), + program.clone(), + file_actions.as_ref(), + attrp.as_ref(), + argv, + envp, + if program.contains('/') { false } else { true }, + ) + } { + return e.0; + } + 0 +} diff --git a/src/header/spawn/spawn_attr.rs b/src/header/spawn/spawn_attr.rs new file mode 100644 index 0000000000..f2f7176023 --- /dev/null +++ b/src/header/spawn/spawn_attr.rs @@ -0,0 +1,281 @@ +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! { + pub 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 param: sched_param, + pub flags: c_short, + pub pgroup: c_int, + policy: c_int, + pub sigdefault: sigset_t, + pub sigmask: sigset_t, +} + +/// See +/// +/// Panics is `attr` is `NULL`. +#[unsafe(no_mangle)] +pub extern "C" fn posix_spawnattr_init(attr: *mut posix_spawnattr_t) -> c_int { + unsafe { + let attr = attr.as_mut().expect("posix_spawnattr_t cannot be NULL"); + *attr = zeroed(); + } + 0 +} + +/// See +/// +/// Panics is `attr` is `NULL`. +/// +/// # Safety: +/// `attr` must be a pointer to `posix_spawnattr_t` and must at least be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_destroy(attr: *mut posix_spawnattr_t) -> c_int { + unsafe { + let attr = attr.as_mut().expect("posix_spawnattr_t cannot be NULL"); + *attr = zeroed(); + } + 0 +} + +/// See +/// +/// Panics if `attr` or `schedparam` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setschedparam( + attr: *mut posix_spawnattr_t, + schedparam: *const sched_param, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + let schedparam = unsafe { schedparam.as_ref().expect("schedparam cannot be NULL") }; + (*attr).param = *schedparam; + 0 +} + +/// See +/// +/// Panics if `attr` or `schedparam` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getschedparam( + attr: *const posix_spawnattr_t, + schedparam: *mut sched_param, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let schedparam = unsafe { schedparam.as_mut().expect("schedparam cannot be NULL") }; + *schedparam = (*attr).param; + 0 +} + +/// See +/// +/// Panics if `attr` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setschedpolicy( + attr: *mut posix_spawnattr_t, + schedpolicy: c_int, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + match schedpolicy { + SCHED_FIFO | SCHED_RR | SCHED_OTHER => (*attr).policy = schedpolicy, + _ => return EINVAL, + } + + 0 +} + +/// See +/// +/// Panics if `attr` or `schedpolicy` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getschedpolicy( + attr: *const posix_spawnattr_t, + schedpolicy: *mut c_int, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let schedpolicy = unsafe { schedpolicy.as_mut().expect("schedpolicy cannot be NULL") }; + *schedpolicy = (*attr).policy; + 0 +} + +/// See +/// +/// Panics if `attr` or `sigdefault` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setsigdefault( + attr: *mut posix_spawnattr_t, + sigdefault: *const sigset_t, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + let sigdefault = unsafe { sigdefault.as_ref().expect("sigdefault cannot be NULL") }; + (*attr).sigdefault = *sigdefault; + 0 +} + +/// See +/// +/// Panics if `attr` or `sigdefault` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getsigdefault( + attr: *const posix_spawnattr_t, + sigdefault: *mut sigset_t, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let sigdefault = unsafe { sigdefault.as_mut().expect("sigdefault cannot be NULL") }; + *sigdefault = (*attr).sigdefault; + 0 +} + +/// See +/// +/// Panics if `attr` or `sigmask` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setsigmask( + attr: *mut posix_spawnattr_t, + sigmask: *const sigset_t, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + let sigmask = unsafe { sigmask.as_ref().expect("sigmask cannot be NULL") }; + (*attr).sigmask = *sigmask; + 0 +} + +/// See +/// +/// Panics if `attr` or `sigmask` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getsigmask( + attr: *const posix_spawnattr_t, + sigmask: *mut sigset_t, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let sigmask = unsafe { sigmask.as_mut().expect("sigmask cannot be NULL") }; + *sigmask = (*attr).sigmask; + 0 +} + +/// See +/// +/// Panics if `attr` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setflags( + attr: *mut posix_spawnattr_t, + flags: c_short, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + match Flags::from_bits(flags) { + Some(v) => (*attr).flags = v.bits(), + None => { + return EINVAL; + } + } + + 0 +} + +/// See +/// +/// Panics if `attr` or `flags` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getflags( + attr: *const posix_spawnattr_t, + flags: *mut c_short, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let flags = unsafe { flags.as_mut().expect("flags cannot be NULL") }; + + *flags = (*attr).flags; + 0 +} + +/// See +/// +/// Panics if `attr` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_setpgroup( + attr: *mut posix_spawnattr_t, + pgroup: pid_t, +) -> c_int { + let attr = unsafe { attr.as_mut().expect("posix_spawnattr_t cannot be NULL") }; + (*attr).pgroup = pgroup; + 0 +} + +/// See +/// +/// Panics if `attr` or `pgroup` is `NULL`. +/// +/// # Safety: +/// `attr` must be initialised. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_spawnattr_getpgroup( + attr: *const posix_spawnattr_t, + pgroup: *mut pid_t, +) -> c_int { + let attr = unsafe { attr.as_ref().expect("posix_spawnattr_t cannot be NULL") }; + let pgroup = unsafe { pgroup.as_mut().expect("pgroup cannot be NULL") }; + *pgroup = (*attr).pgroup; + 0 +} diff --git a/src/iter.rs b/src/iter.rs index 369d2dadc8..4b3cc6c872 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -30,6 +30,12 @@ unsafe impl Zero for wchar_t { } } +unsafe impl Zero for *mut c_char { + fn is_zero(&self) -> bool { + self.is_null() + } +} + /// An iterator over a nul-terminated buffer. /// /// This is intended to allow safe, ergonomic iteration over C-style byte and diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 1b002995c9..bc3cb308ab 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,6 +1,8 @@ #[cfg(target_arch = "x86_64")] use core::arch::asm; +use alloc::string::String; + use super::{Pal, types::*}; use crate::{ c_str::CStr, @@ -793,4 +795,15 @@ impl Pal for Sys { // GETPID on Linux is 39, which does not exist on Redox e_raw(unsafe { sc::syscall5(sc::nr::GETPID, !0, !0, !0, !0, !0) }).is_ok() } + + unsafe fn spawn( + program: CStr, + fac: Option<&crate::header::spawn::posix_spawn_file_actions_t>, + fat: Option<&crate::header::spawn::posix_spawnattr_t>, + argv: crate::iter::NulTerminated<*mut c_char>, + envp: Option>, + dir_ent_name: Option, + ) -> Result { + todo!() + } } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index f3ed9595b3..75a706605d 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -1,5 +1,7 @@ use core::num::NonZeroU64; +use alloc::string::String; + use super::types::*; use crate::{ c_str::CStr, @@ -15,6 +17,7 @@ use crate::{ sys_utsname::utsname, time::{itimerspec, timespec}, }, + iter::NulTerminated, ld_so::tcb::OsSpecific, out::Out, pthread, @@ -433,6 +436,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: NulTerminated<*mut c_char>, + envp: Option>, + dir_ent_name: Option, + ) -> 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/exec.rs b/src/platform/redox/exec.rs index f269f59234..5235549f90 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -38,7 +38,8 @@ fn fexec_impl( envs, extrainfo, interp_override, - )?; + )? + .unwrap(); // According to elf(5), PT_INTERP requires that the interpreter path be // null-terminated. Violating this should therefore give the "format error" ENOEXEC. @@ -238,6 +239,7 @@ pub fn execve( cwd_fd: super::path::current_dir() .ok() .map(|fd| fd.as_ref().unwrap().fd.as_raw_fd()), + same_process: true, }; fexec_impl( diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 54967ade95..0595b74bfd 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use core::{ convert::TryFrom, mem::{self, size_of}, @@ -22,19 +23,20 @@ use self::{ }; use super::{Pal, Read, types::*}; use crate::{ + alloc::string::ToString, c_str::{CStr, CString}, error::{Errno, Result}, fs::File, header::{ errno::{ - EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, - ENOSYS, EOPNOTSUPP, EPERM, + EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, + ENOEXEC, ENOMEM, ENOSYS, EOPNOTSUPP, EPERM, }, fcntl::{ self, AT_EACCESS, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, F_GETLK, F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, flock, }, - limits, + limits::{self}, pthread::{pthread_cancel, pthread_create}, signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent}, stdio::RENAME_NOREPLACE, @@ -42,9 +44,9 @@ use crate::{ sys_file, sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE}, sys_random, - sys_resource::{RLIM_INFINITY, rlimit, rusage}, + sys_resource::{PRIO_PROCESS, RLIM_INFINITY, rlimit, rusage, setpriority}, sys_select::timeval, - sys_stat::{S_ISVTX, stat}, + sys_stat::{S_ISGID, S_ISUID, S_ISVTX, stat}, sys_statvfs::statvfs, sys_time::timezone, sys_utsname::{UTSLENGTH, utsname}, @@ -54,10 +56,11 @@ use crate::{ unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK}, }, io::{self, BufReader, prelude::*}, + iter::NulTerminated, ld_so::tcb::OsSpecific, out::Out, platform::{ - free, + ERRNO, free, sys::timer::{TIMERS, timer_routine, timer_update_wake_time}, }, sync::rwlock::RwLock, @@ -67,7 +70,7 @@ pub use redox_rt::proc::FdGuard; mod epoll; mod event; -mod exec; +pub(crate) mod exec; mod extra; mod libcscheme; mod libredox; @@ -1169,29 +1172,286 @@ impl Pal for Sys { } fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> { - redox_rt::sys::posix_setresugid(&Resugid { - ruid: None, - euid: None, - suid: None, - rgid: cvt_uid(rgid)?, - egid: cvt_uid(egid)?, - sgid: cvt_uid(sgid)?, - })?; + redox_rt::sys::posix_setresugid( + &Resugid { + ruid: None, + euid: None, + suid: None, + rgid: cvt_uid(rgid)?, + egid: cvt_uid(egid)?, + sgid: cvt_uid(sgid)?, + }, + None, + )?; Ok(()) } fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> { - redox_rt::sys::posix_setresugid(&Resugid { - ruid: cvt_uid(ruid)?, - euid: cvt_uid(euid)?, - suid: cvt_uid(suid)?, - rgid: None, - egid: None, - sgid: None, - })?; + redox_rt::sys::posix_setresugid( + &Resugid { + ruid: cvt_uid(ruid)?, + euid: cvt_uid(euid)?, + suid: cvt_uid(suid)?, + rgid: None, + egid: None, + sgid: None, + }, + None, + )?; Ok(()) } + unsafe fn spawn( + program: CStr, + fac: Option<&crate::header::spawn::posix_spawn_file_actions_t>, + fat: Option<&crate::header::spawn::posix_spawnattr_t>, + argv: NulTerminated<*mut c_char>, + envp: Option>, + dir_ent_name: Option, + ) -> Result { + use crate::header::spawn::Flags; + + let child = redox_rt::proc::new_child_process(&redox_rt::proc::ForkArgs::Managed)?; + let executable = FdGuard::new(File::open(program, fcntl::O_RDONLY)?.fd as usize) + .to_upper() + .unwrap(); + let mut executable_stat = syscall::Stat::default(); + executable.fstat(&mut executable_stat)?; + let original_cwd = path::clone_cwd().unwrap().to_string(); + let mut cwd = original_cwd.clone(); + let proc_fd = child.proc_fd.unwrap(); + let curr_proc_fd = redox_rt::current_proc_fd(); + let file_table = RtTcb::current() + .thread_fd() + .dup(b"filetable")? + .dup(b"copy")?; + + { + let new_file_table = child.thr_fd.dup(b"current-filetable")?; + new_file_table.write(&file_table.as_raw_fd().to_ne_bytes())?; + } + + let mut args = Vec::new(); + let mut envs = Vec::new(); + + for arg in argv { + args.push(unsafe { CStr::from_ptr(*arg).to_chars() }); + } + + if let Some(envp) = envp { + for env in envp { + envs.push(unsafe { CStr::from_ptr(*env).to_chars() }); + } + } + + let program_name: String = if let Some(ent) = dir_ent_name { + let mut binary = str::from_utf8(args[0]).unwrap().to_string(); + binary.insert_str(0, "./"); + + redox_path::canonicalize_using_cwd(Some(ent.as_str()), binary.as_str()) + .ok_or(Errno(ENOENT))? + } else { + redox_path::canonicalize_using_cwd( + Some(original_cwd.as_str()), + str::from_utf8(args[0]).unwrap(), + ) + .ok_or(Errno(ENOENT))? + }; + + args[0] = program_name.as_bytes(); + + let new_file_table = child.thr_fd.dup(b"filetable")?; + + if let Some(fac) = fac { + for action in fac { + match action { + crate::header::spawn::Action::Open { + fd, + path, + flag, + mode, + } => { + let src_fd = Sys::open( + CStr::from_bytes_with_nul(path.as_bytes_with_nul()).unwrap(), + flag, + mode, + )? as usize; + syscall::sendfd( + new_file_table.as_raw_fd(), + src_fd, + 0, + u64::try_from(fd).map_err(|_| Errno(EBADFD))?, + )?; + } + crate::header::spawn::Action::Close(fd) => { + new_file_table.call_wo( + &(fd as usize).to_ne_bytes(), + syscall::CallFlags::empty(), + &[syscall::flag::FileTableVerb::Close as u64], + )?; + } + crate::header::spawn::Action::Chdir(path) => { + cwd = path.to_str().unwrap().to_string(); + + path::chdir(path.to_str().unwrap())?; + } + crate::header::spawn::Action::FChdir(fd) => { + path::fchdir(fd)?; + path::getcwd(Out::from_mut(unsafe { cwd.as_bytes_mut() }))?; + } + crate::header::spawn::Action::Dup2(old, new) => { + new_file_table.call_wo( + [(old as usize).to_ne_bytes(), (new as usize).to_ne_bytes()] + .into_iter() + .flatten() + .collect::>() + .as_slice(), + syscall::CallFlags::empty(), + &[syscall::flag::FileTableVerb::Dup2 as u64], + )?; + } + } + } + } + + new_file_table.call_wo( + &[], + syscall::CallFlags::empty(), + &[syscall::flag::FileTableVerb::CloseCloExec as u64], + )?; + + let extra_info = redox_rt::proc::ExtraInfo { + cwd: Some(cwd.as_bytes()), + // Signals set to be ignored by the calling process + // must also be ignored by the child process + sigignmask: redox_rt::signal::get_sigignmask_to_inherit(), + sigprocmask: if let Some(fat) = fat + && Flags::from_bits(fat.flags) + .unwrap() + .contains(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()), + same_process: false, + }; + + if let Some(attr) = fat { + let flags = Flags::from_bits(attr.flags).ok_or(Errno(EINVAL))?; + + if flags.contains(Flags::POSIX_SPAWN_SETPGROUP) && attr.pgroup != 0 { + redox_rt::sys::posix_setpgid( + proc_fd.as_raw_fd(), + usize::try_from(attr.pgroup).map_err(|_| Errno(EINVAL))?, + )?; + } else { + redox_rt::sys::posix_setpgid( + proc_fd.as_raw_fd(), + redox_rt::sys::posix_getpgid(proc_fd.as_raw_fd())?, + )?; + } + + let set_schedparam = || -> Result<()> { + if setpriority( + PRIO_PROCESS, + proc_fd.as_raw_fd() as id_t, + attr.param.sched_priority, + ) as usize + != 0 + { + Err(Errno(ERRNO.get())) + } else { + Ok(()) + } + }; + let set_scheduler = || -> Result<()> { todo!() }; + + // scheduling paramters must be set regardless of whether the flag POSIX_SPAWN_SETSCHEDPARAM is set + if flags.contains(Flags::POSIX_SPAWN_SETSCHEDULER) { + set_schedparam()?; + set_scheduler()?; + } else if flags.contains(Flags::POSIX_SPAWN_SETSCHEDPARAM) { + set_schedparam()?; + } + + let parent_resugid = redox_rt::sys::posix_getresugid(); + + redox_rt::sys::posix_setresugid( + &Resugid { + ruid: None, + euid: Some(if executable_stat.st_mode as mode_t & S_ISUID == S_ISUID { + executable_stat.st_uid + } else if flags.contains(Flags::POSIX_SPAWN_RESETIDS) { + parent_resugid.ruid + } else { + parent_resugid.euid + }), + suid: None, + rgid: None, + egid: Some(if executable_stat.st_mode as mode_t & S_ISGID == S_ISGID { + executable_stat.st_gid + } else if flags.contains(Flags::POSIX_SPAWN_RESETIDS) { + parent_resugid.rgid + } else { + parent_resugid.egid + }), + sgid: None, + }, + Some(proc_fd.as_raw_fd()), + )?; + + // if POSIX_SPAWN_SETSIGDEF flag is set, the signals specified in + // sigdefault must have default actions + } + + if let Some(redox_rt::proc::FexecResult::Interp { + path: interp_path, + interp_override, + }) = redox_rt::proc::fexec_impl( + executable, + &child.thr_fd, + &proc_fd, + program.to_bytes(), + args.as_slice(), + envs.as_slice(), + &extra_info, + None, + )? { + let interp_path = + CStr::from_bytes_with_nul(&interp_path).map_err(|_| Errno(ENOEXEC))?; + + let interpreter = File::open(interp_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) + .map_err(|_| Errno(ENOENT))?; + + redox_rt::proc::fexec_impl( + FdGuard::new(interpreter.fd as usize).to_upper().unwrap(), + &child.thr_fd, + &proc_fd, + program.to_bytes(), + args.as_slice(), + envs.as_slice(), + &extra_info, + Some(interp_override), + ) + .unwrap(); + } + + path::chdir(original_cwd.as_str())?; + + let start_fd = child.thr_fd.dup(b"start")?; + start_fd.write(&[0])?; + + Ok(pid_t::try_from(child.pid).unwrap()) + } + fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> { let mut file = File::createat( fd, diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk index e82236799e..ae0cd18e80 100644 --- a/tests/Makefile.tests.mk +++ b/tests/Makefile.tests.mk @@ -11,7 +11,7 @@ FAILING_TESTS += malloc/usable_size FAILING_TESTS += mkfifo # Waitpid had EINTR FAILING_TESTS += sigchld -# not triggering ERANGE +# not triggering ERANGE FAILING_TESTS += stdlib/ptsname # Hang FAILING_TESTS += sys_epoll/epollet @@ -286,6 +286,7 @@ VARIED_NAMES=\ signals/sigset-4 \ signals/sigset-5 \ signals/sigset-9 \ + spawn \ stdio/ctermid \ stdio/tempnam \ stdio/tmpnam \ diff --git a/tests/expected/spawn.stdout b/tests/expected/spawn.stdout new file mode 100644 index 0000000000..83e53f3f62 --- /dev/null +++ b/tests/expected/spawn.stdout @@ -0,0 +1,2 @@ +hello.txt +HELLO REDOX diff --git a/tests/spawn.c b/tests/spawn.c new file mode 100644 index 0000000000..26cd6220fc --- /dev/null +++ b/tests/spawn.c @@ -0,0 +1,95 @@ +#include "./test_helpers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char buf[] = "#include \nint main()\n{\nwrite(900, " + "\"hello_spawn REDOX\\n\", 18);\n}"; + +#define OPTIONALLY_ERROR(function, args, cond) \ + { \ + int x = function args; \ + if (!(x cond)) { \ + printf("%s failed with error: %s\n", #function, strerror(x)); \ + exit(EXIT_FAILURE); \ + } \ + } + +extern char **environ; + +int main() { + pid_t pid = 0; + char cwd[PATH_MAX]; + int status = 0; + char *__cwd = getcwd(cwd, PATH_MAX); + ERROR_IF(getcwd, __cwd, == NULL); + char target[PATH_MAX]; + strcpy(target, cwd); + strcat(target, "/hello_spawn"); + + // TEST: chdir and open + char *argv1[] = {"ls", target, NULL}; + posix_spawn_file_actions_t fa; + posix_spawn_file_actions_init(&fa); + status = mkdir("./hello_spawn", S_IRUSR | S_IWUSR); + ERROR_IF(mkdir, status, != 0); + OPTIONALLY_ERROR(posix_spawn_file_actions_addchdir, (&fa, "./hello_spawn"), + == 0); + OPTIONALLY_ERROR( + posix_spawn_file_actions_addopen, + (&fa, 2, "./hello_spawn.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR), == 0); + OPTIONALLY_ERROR(posix_spawnp, (&pid, "ls", &fa, NULL, argv1, environ), == 0); + assert(pid != 0); + waitpid(pid, NULL, 0); + OPTIONALLY_ERROR(posix_spawn_file_actions_destroy, (&fa), == 0); + + pid = 0; + + // TEST: close + argv1[0] = "/usr/bin/ls"; + OPTIONALLY_ERROR(posix_spawn_file_actions_init, (&fa), == 0); + OPTIONALLY_ERROR(posix_spawn_file_actions_addclose, (&fa, 1), == 0); + OPTIONALLY_ERROR(posix_spawn, + (&pid, "/usr/bin/ls", &fa, NULL, argv1, environ), == 0); + assert(pid != 0); + waitpid(pid, NULL, 0); + OPTIONALLY_ERROR(posix_spawn_file_actions_destroy, (&fa), == 0); + status = unlink("./hello_spawn/hello_spawn.txt"); + ERROR_IF(unlink, status, != 0); + status = rmdir("./hello_spawn"); + ERROR_IF(rmdir, status, != 0); + + // TEST: dup2 + pid = 0; + posix_spawn_file_actions_init(&fa); + posix_spawn_file_actions_adddup2(&fa, 1, 900); + + FILE *f = fopen("./dup2_check.c", "w"); + if (!f) { + printf("Failed to create dup2_check.c\n"); + exit(EXIT_FAILURE); + } + OPTIONALLY_ERROR(fwrite, (buf, sizeof(char), 74, f), == 74); + fclose(f); + char *argv2[] = {"gcc", "./dup2_check.c", "-o", "d.out", NULL}; + OPTIONALLY_ERROR(posix_spawnp, (&pid, "gcc", NULL, NULL, argv2, environ), + == 0); + waitpid(pid, NULL, 0); + assert(pid != 0); + pid = 0; + char *argv3[] = {"./d.out", NULL}; + OPTIONALLY_ERROR(posix_spawnp, (&pid, "./d.out", &fa, NULL, argv3, environ), + == 0); + assert(pid != 0); + waitpid(pid, NULL, 0); + posix_spawn_file_actions_destroy(&fa); +}