From 72978e963ef51f7e48c4f53768fec509f50193d8 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 13:45:23 +0100 Subject: [PATCH 01/77] Patch redox-rt, syscall. --- Cargo.lock | 21 ++++++++++----------- Cargo.toml | 10 +++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f88f5e6c0..02ce92c312 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" [[package]] name = "goblin" @@ -51,9 +51,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libredox" @@ -99,9 +99,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -132,7 +132,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc#5ea71a02c7f9e436ed0dcf53bc7a5bdb311279cf" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" dependencies = [ "bitflags", "generic-rt", @@ -143,8 +143,8 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.2.3" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git?tag=v0.2.3#dc6a047842353ed49c1788396d9135de2c3d7382" +version = "0.6.0" +source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#aee599d75a51cda40c23ca91689b1c92cc4a8557" dependencies = [ "libredox", "redox_syscall", @@ -152,9 +152,8 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +version = "0.5.11" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8a80d1f58bc92d8c0eb73a939420fb30fe1e85d0" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index f00f646349..8c7370749d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,10 @@ hashbrown = { version = "0.14", default-features = false, features = [ ] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } -redox_syscall = "0.5.4" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git", tag = "v0.2.3" } -redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox_syscall = { version = "0.5.4", default-features = false } +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +#redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } +redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" [profile.release] @@ -26,3 +27,6 @@ lto = "fat" [profile.dev] panic = "abort" + +[patch.crates-io] +redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "nuke_proc" } From eecd046709263a312141948d88f81ece2abd1a4c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 13:45:39 +0100 Subject: [PATCH 02/77] TODO: start writing proc manager --- src/exec.rs | 29 +++++++++++--------- src/initfs.rs | 1 - src/lib.rs | 1 + src/procmngr.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/procmngr.rs diff --git a/src/exec.rs b/src/exec.rs index 0083c906b3..b9ba4d51e2 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -37,14 +37,20 @@ pub fn main() -> ! { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; - unsafe { + spawn("initfs daemon", move |write_fd| unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - spawn_initfs( - core::ptr::addr_of!(__initfs_header), - initfs_length.get() as usize, + let initfs_start = core::ptr::addr_of!(__initfs_header); + let initfs_length = initfs_length.get() as usize; + + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write_fd, ); - } + }); + + spawn("process manager", move |write_fd| crate::procmngr::run(write_fd)); + const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; let extrainfo = ExtraInfo { @@ -67,11 +73,13 @@ pub fn main() -> ! { let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current/open_via_dup", 0) + syscall::open("/scheme/thisproc/current", 0) .expect("failed to open open_via_dup"), ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); + redox_rt::proc::make_init(); + fexec_impl( image_file, open_via_dup, @@ -88,7 +96,7 @@ pub fn main() -> ! { unreachable!() } -unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { +pub(crate) fn spawn(name: &str, inner: impl FnOnce(usize)) { let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. @@ -96,7 +104,7 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { match fork_impl() { Err(err) => { - panic!("Failed to fork in order to start initfs daemon: {}", err); + panic!("Failed to fork in order to start {name}: {err}"); } // Continue serving the scheme as the child. Ok(0) => { @@ -115,8 +123,5 @@ unsafe fn spawn_initfs(initfs_start: *const u8, initfs_length: usize) { return; } } - crate::initfs::run( - core::slice::from_raw_parts(initfs_start, initfs_length), - write, - ); + inner(write); } diff --git a/src/initfs.rs b/src/initfs.rs index b1e97cdfbc..ee7ccb10d1 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -342,7 +342,6 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { } } - syscall::exit(0).expect("initfs: failed to exit"); unreachable!() } diff --git a/src/lib.rs b/src/lib.rs index 7fc400fe98..71e88a352c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ pub mod arch; pub mod exec; pub mod initfs; +pub mod procmngr; pub mod start; extern crate alloc; diff --git a/src/procmngr.rs b/src/procmngr.rs new file mode 100644 index 0000000000..b70defe239 --- /dev/null +++ b/src/procmngr.rs @@ -0,0 +1,72 @@ +use core::cell::RefCell; + +use alloc::rc::Rc; +use hashbrown::hash_map::DefaultHashBuilder; +use hashbrown::{HashMap, HashSet}; +use redox_rt::proc::FdGuard; +use redox_scheme::{CallerCtx, OpenResult, RequestKind, SchemeMut, SignalBehavior, Socket, V2}; +use syscall::{Result, O_CREAT}; + +pub fn run(write_fd: usize) { + let socket = Socket::::create("proc").expect("failed to open proc scheme socket"); + let mut scheme = ProcScheme::new(); + + let _ = syscall::write(1, b"process manager started\n").unwrap(); + let _ = syscall::write(write_fd, &[0]); + let _ = syscall::close(write_fd); + + loop { + let RequestKind::Call(req) = (match socket + .next_request(SignalBehavior::Restart) + .expect("bootstrap: failed to read scheme request from kernel") + { + Some(req) => req.kind(), + None => break, + }) else { + continue; + }; + let resp = req.handle_scheme_mut(&mut scheme); + + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + + unreachable!() +} + +struct Process { + threads: Vec>>, + ppid: ProcessId, + pgid: ProcessId, + sid: ProcessId, +} +struct Thread { + fd: FdGuard, + // sig_ctrl: MmapGuard<...> +} +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct ProcessId(usize); + +struct ProcScheme { + processes: HashMap, + process_groups: HashSet, + sessions: HashSet, +} +impl ProcScheme { + pub fn new() -> ProcScheme { + ProcScheme { + processes: HashMap::new(), + process_groups: HashSet::new(), + sessions: HashSet::new(), + } + } +} +impl SchemeMut for ProcScheme { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + + } +} From 9f0ddc83f4d604e871cbc9928ac1034c8513df3c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 16:19:12 +0100 Subject: [PATCH 03/77] WIP: write skeleton for fork, new-thread --- Cargo.toml | 2 + src/aarch64.rs | 12 ++-- src/exec.rs | 11 ++-- src/initfs.rs | 15 ++--- src/procmngr.rs | 163 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 167 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8c7370749d..b7b2b3366f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["staticlib"] [dependencies] hashbrown = { version = "0.14", default-features = false, features = [ "inline-more", + "ahash", ] } linked_list_allocator = "0.10" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } @@ -20,6 +21,7 @@ redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } #redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" +slab = { version = "0.4.9", default-features = false } [profile.release] panic = "abort" diff --git a/src/aarch64.rs b/src/aarch64.rs index 56aecc3ee5..6e1a732175 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -1,9 +1,5 @@ use core::mem; -use syscall::{ - data::Map, - flag::MapFlags, - number::SYS_FMAP, -}; +use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; pub const USERMODE_END: usize = 0x0000_8000_0000_0000; pub const STACK_START: usize = USERMODE_END - STACK_SIZE; @@ -13,9 +9,9 @@ static MAP: Map = Map { offset: 0, size: STACK_SIZE, flags: MapFlags::PROT_READ - .union(MapFlags::PROT_WRITE) - .union(MapFlags::MAP_PRIVATE) - .union(MapFlags::MAP_FIXED_NOREPLACE), + .union(MapFlags::PROT_WRITE) + .union(MapFlags::MAP_PRIVATE) + .union(MapFlags::MAP_FIXED_NOREPLACE), address: STACK_START, // highest possible user address }; diff --git a/src/exec.rs b/src/exec.rs index b9ba4d51e2..9d6f491a0e 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -49,7 +49,9 @@ pub fn main() -> ! { ); }); - spawn("process manager", move |write_fd| crate::procmngr::run(write_fd)); + spawn("process manager", move |write_fd| { + crate::procmngr::run(write_fd) + }); const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -73,12 +75,13 @@ pub fn main() -> ! { let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current", 0) - .expect("failed to open open_via_dup"), + syscall::open("/scheme/thisproc/current", 0).expect("failed to open open_via_dup"), ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - redox_rt::proc::make_init(); + unsafe { + redox_rt::proc::make_init(); + } fexec_impl( image_file, diff --git a/src/initfs.rs b/src/initfs.rs index ee7ccb10d1..7568b15e9f 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -9,14 +9,9 @@ use hashbrown::HashMap; use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; use redox_path::canonicalize_to_standard; -use redox_scheme::CallerCtx; -use redox_scheme::OpenResult; -use redox_scheme::RequestKind; -use redox_scheme::SchemeMut; +use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme}; -use redox_scheme::SignalBehavior; -use redox_scheme::Socket; -use redox_scheme::V2; +use redox_scheme::{SignalBehavior, Socket}; use syscall::data::Stat; use syscall::dirent::DirEntry; use syscall::dirent::DirentBuf; @@ -93,7 +88,7 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl SchemeMut for InitFsScheme { +impl Scheme for InitFsScheme { fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash @@ -317,7 +312,7 @@ impl SchemeMut for InitFsScheme { pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let mut scheme = InitFsScheme::new(bytes); - let socket = Socket::::create("initfs").expect("failed to open initfs scheme socket"); + let socket = Socket::create("initfs").expect("failed to open initfs scheme socket"); let _ = syscall::write(sync_pipe, &[0]); let _ = syscall::close(sync_pipe); @@ -332,7 +327,7 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }) else { continue; }; - let resp = req.handle_scheme_mut(&mut scheme); + let resp = req.handle_scheme(&mut scheme); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmngr.rs b/src/procmngr.rs index b70defe239..01b67dc3cc 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,14 +1,21 @@ use core::cell::RefCell; use alloc::rc::Rc; +use alloc::vec::Vec; +use alloc::vec; + use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, SchemeMut, SignalBehavior, Socket, V2}; -use syscall::{Result, O_CREAT}; +use redox_scheme::{ + CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, +}; +use slab::Slab; +use syscall::schemev2::NewFdFlags; +use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CREAT}; pub fn run(write_fd: usize) { - let socket = Socket::::create("proc").expect("failed to open proc scheme socket"); + let socket = Socket::create("proc").expect("failed to open proc scheme socket"); let mut scheme = ProcScheme::new(); let _ = syscall::write(1, b"process manager started\n").unwrap(); @@ -16,16 +23,17 @@ pub fn run(write_fd: usize) { let _ = syscall::close(write_fd); loop { - let RequestKind::Call(req) = (match socket + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") - { - Some(req) => req.kind(), - None => break, - }) else { + else { continue; }; - let resp = req.handle_scheme_mut(&mut scheme); + let resp = match req.kind() { + RequestKind::Call(req) => req.handle_scheme(&mut scheme), + RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), + _ => continue, + }; if !socket .write_response(resp, SignalBehavior::Restart) @@ -43,6 +51,13 @@ struct Process { ppid: ProcessId, pgid: ProcessId, sid: ProcessId, + + ruid: u32, + euid: u32, + rgid: u32, + egid: u32, + rns: u32, + ens: u32, } struct Thread { fd: FdGuard, @@ -51,22 +66,142 @@ struct Thread { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct ProcessId(usize); +const INIT_PID: ProcessId = ProcessId(1); + struct ProcScheme { processes: HashMap, process_groups: HashSet, sessions: HashSet, + handles: Slab, + + init_claimed: bool, + next_id: ProcessId, } + +enum Handle { + Init, + Proc(ProcessId), +} + impl ProcScheme { pub fn new() -> ProcScheme { ProcScheme { processes: HashMap::new(), process_groups: HashSet::new(), sessions: HashSet::new(), + handles: Slab::new(), + init_claimed: false, + next_id: ProcessId(2), + } + } + fn new_id(&mut self) -> ProcessId { + let id = self.next_id; + self.next_id.0 += 1; + id + } + fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Response { + match self.handles[req.id()] { + ref mut st @ Handle::Init => { + let mut fd_out = usize::MAX; + if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { + return Response::for_sendfd(&req, Err(e)); + }; + let thread = Rc::new(RefCell::new(Thread { + fd: FdGuard::new(fd_out), + })); + self.processes.insert( + INIT_PID, + Process { + threads: vec![thread], + ppid: INIT_PID, + sid: INIT_PID, + pgid: INIT_PID, + ruid: 0, + euid: 0, + rgid: 0, + egid: 0, + rns: 1, + ens: 1, + }, + ); + self.process_groups.insert(INIT_PID); + self.sessions.insert(INIT_PID); + + *st = Handle::Proc(INIT_PID); + Response::for_sendfd(&req, Ok(0)) + } + _ => Response::for_sendfd(&req, Err(Error::new(EBADF))), + } + } + fn fork(&mut self, parent_pid: ProcessId) -> Result { + let child_pid = self.new_id(); + + let Process { + pgid, + sid, + euid, + ruid, + egid, + rgid, + ens, + rns, + .. + } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + + self.processes.insert( + child_pid, + Process { + threads: Vec::new(), + ppid: parent_pid, + pgid, + sid, + ruid, + rgid, + euid, + egid, + rns, + ens, + }, + ); + Ok(child_pid) + } + fn new_thread(&mut self, pid: ProcessId) -> Result { + let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + proc.threads + .push(Rc::new(RefCell::new(Thread { fd: todo!() }))); + } +} +impl Scheme for ProcScheme { + fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + if path == "init" { + if core::mem::replace(&mut self.init_claimed, true) { + return Err(Error::new(EEXIST)); + } + return Ok(OpenResult::ThisScheme { + number: self + .handles + .insert(Handle::Init), + flags: NewFdFlags::empty(), + }); + } + Err(Error::new(ENOENT)) + } + fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { + match self.handles[old_id] { + Handle::Proc(pid) => match buf { + b"fork" => { + let child_pid = self.fork(pid)?; + Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Proc(child_pid)), + flags: NewFdFlags::empty(), + }) + }, + b"new-thread" => Ok(OpenResult::OtherScheme { + fd: self.new_thread(pid)?.take(), + }), + _ => return Err(Error::new(EINVAL)), + }, + Handle::Init => Err(Error::new(EBADF)), } } } -impl SchemeMut for ProcScheme { - fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { - - } -} From b4a79920745b67219231e1042d3362a88cc50473 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 26 Dec 2024 21:29:06 +0100 Subject: [PATCH 04/77] Support reading metadata from process. --- src/exec.rs | 22 +++++++++++--------- src/procmngr.rs | 54 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 9d6f491a0e..77d00c068a 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -49,9 +49,17 @@ pub fn main() -> ! { ); }); - spawn("process manager", move |write_fd| { - crate::procmngr::run(write_fd) + let auth = FdGuard::new(syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC).expect("failed to get proc authority")); + + spawn("process manager", |write_fd| { + crate::procmngr::run(write_fd, &auth) }); + let this_thr_fd = FdGuard::new( + syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup"), + ); + let (init_proc_fd, init_thr_fd) = unsafe { + redox_rt::proc::make_init(this_thr_fd) + }; const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -74,18 +82,12 @@ pub fn main() -> ! { + 1; let image_file = FdGuard::new(syscall::open(path, O_RDONLY).expect("failed to open init")); - let open_via_dup = FdGuard::new( - syscall::open("/scheme/thisproc/current", 0).expect("failed to open open_via_dup"), - ); let memory = FdGuard::new(syscall::open("/scheme/memory", 0).expect("failed to open memory")); - unsafe { - redox_rt::proc::make_init(); - } - fexec_impl( image_file, - open_via_dup, + init_proc_fd, + init_thr_fd, &memory, path.as_bytes(), [path], diff --git a/src/procmngr.rs b/src/procmngr.rs index 01b67dc3cc..af04a1bf88 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -7,16 +7,17 @@ use alloc::vec; use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; +use redox_rt::protocol::ProcMeta; use redox_scheme::{ CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; -use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CREAT}; +use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT}; -pub fn run(write_fd: usize) { +pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); - let mut scheme = ProcScheme::new(); + let mut scheme = ProcScheme::new(auth); let _ = syscall::write(1, b"process manager started\n").unwrap(); let _ = syscall::write(write_fd, &[0]); @@ -68,7 +69,7 @@ struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); -struct ProcScheme { +struct ProcScheme<'a> { processes: HashMap, process_groups: HashSet, sessions: HashSet, @@ -76,6 +77,8 @@ struct ProcScheme { init_claimed: bool, next_id: ProcessId, + + auth: &'a FdGuard, } enum Handle { @@ -83,8 +86,8 @@ enum Handle { Proc(ProcessId), } -impl ProcScheme { - pub fn new() -> ProcScheme { +impl<'a> ProcScheme<'a> { + pub fn new(auth: &'a FdGuard) -> ProcScheme { ProcScheme { processes: HashMap::new(), process_groups: HashSet::new(), @@ -92,6 +95,7 @@ impl ProcScheme { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + auth, } } fn new_id(&mut self) -> ProcessId { @@ -148,10 +152,14 @@ impl ProcScheme { .. } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + self.processes.insert( child_pid, Process { - threads: Vec::new(), + threads: vec! [Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + }))], ppid: parent_pid, pgid, sid, @@ -167,11 +175,13 @@ impl ProcScheme { } fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let fd = todo!(); proc.threads - .push(Rc::new(RefCell::new(Thread { fd: todo!() }))); + .push(Rc::new(RefCell::new(Thread { fd }))); + Ok(fd) } } -impl Scheme for ProcScheme { +impl Scheme for ProcScheme<'_> { fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { if path == "init" { if core::mem::replace(&mut self.init_claimed, true) { @@ -186,6 +196,25 @@ impl Scheme for ProcScheme { } Err(Error::new(ENOENT)) } + fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, _fcntl_flags: u32) -> Result { + match self.handles[id] { + Handle::Proc(pid) => { + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let metadata = ProcMeta { + pid: pid.into(), + pgid: process.pgid.into(), + ppid: process.ppid.into(), + euid: process.euid, + egid: process.egid, + ruid: process.ruid, + rgid: process.rgid, + ens: process.ens, + rns: process.rns, + }; + } + Handle::Init => return Err(Error::new(EBADF)), + } + } fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { match self.handles[old_id] { Handle::Proc(pid) => match buf { @@ -199,6 +228,13 @@ impl Scheme for ProcScheme { b"new-thread" => Ok(OpenResult::OtherScheme { fd: self.new_thread(pid)?.take(), }), + w if w.starts_with("thread-") => { + let idx = core::str::from_utf8(&w["thread-".len()..]).ok().and_then(|s| s.parse::().ok()).ok_or(Error::new(EINVAL))?; + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); + + return Ok(OpenResult::OtherScheme { fd: syscall::dup(*thread.fd, &[])? }); + } _ => return Err(Error::new(EINVAL)), }, Handle::Init => Err(Error::new(EBADF)), From 3dbd60608682ba20af2bae8600ddc60dd9408cf7 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 13:55:49 +0100 Subject: [PATCH 05/77] Reach init with proc manager. --- Cargo.lock | 1 + Cargo.toml | 1 + src/exec.rs | 50 +++++++++++++++++++++++++++---------------------- src/procmngr.rs | 49 ++++++++++++++++++++++++++++++------------------ src/start.rs | 2 -- 5 files changed, 61 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02ce92c312..fa5a500e3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ version = "0.0.0" dependencies = [ "hashbrown", "linked_list_allocator", + "plain", "redox-initfs", "redox-path", "redox-rt", diff --git a/Cargo.toml b/Cargo.toml index b7b2b3366f..3413303d20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ hashbrown = { version = "0.14", default-features = false, features = [ "ahash", ] } linked_list_allocator = "0.10" +plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } diff --git a/src/exec.rs b/src/exec.rs index 77d00c068a..3d247cbc9d 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -7,6 +7,14 @@ use syscall::{Error, EINTR}; use redox_rt::proc::*; pub fn main() -> ! { + let auth = FdGuard::new( + syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC) + .expect("failed to get proc authority"), + ); + let this_thr_fd = + FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); + let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; + let envs = { let mut env = [0_u8; 4096]; @@ -37,29 +45,27 @@ pub fn main() -> ! { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; - spawn("initfs daemon", move |write_fd| unsafe { - // Creating a reference to NULL is UB. Mask the UB for now using black_box. - // FIXME use a raw pointer and inline asm for reading instead for the initfs header. - let initfs_start = core::ptr::addr_of!(__initfs_header); - let initfs_length = initfs_length.get() as usize; + spawn( + "initfs daemon", + &auth, + &this_thr_fd, + move |write_fd| unsafe { + // Creating a reference to NULL is UB. Mask the UB for now using black_box. + // FIXME use a raw pointer and inline asm for reading instead for the initfs header. + let initfs_start = core::ptr::addr_of!(__initfs_header); + let initfs_length = initfs_length.get() as usize; - crate::initfs::run( - core::slice::from_raw_parts(initfs_start, initfs_length), - write_fd, - ); - }); + crate::initfs::run( + core::slice::from_raw_parts(initfs_start, initfs_length), + write_fd, + ); + }, + ); - let auth = FdGuard::new(syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC).expect("failed to get proc authority")); - - spawn("process manager", |write_fd| { + spawn("process manager", &auth, &this_thr_fd, |write_fd| { crate::procmngr::run(write_fd, &auth) }); - let this_thr_fd = FdGuard::new( - syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup"), - ); - let (init_proc_fd, init_thr_fd) = unsafe { - redox_rt::proc::make_init(this_thr_fd) - }; + let init_proc_fd = unsafe { redox_rt::proc::make_init() }; const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -86,8 +92,8 @@ pub fn main() -> ! { fexec_impl( image_file, + this_thr_fd, init_proc_fd, - init_thr_fd, &memory, path.as_bytes(), [path], @@ -101,13 +107,13 @@ pub fn main() -> ! { unreachable!() } -pub(crate) fn spawn(name: &str, inner: impl FnOnce(usize)) { +pub(crate) fn spawn(name: &str, auth: &FdGuard, this_thr_fd: &FdGuard, inner: impl FnOnce(usize)) { let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); - match fork_impl() { + match fork_impl(&ForkArgs::Init { this_thr_fd, auth }) { Err(err) => { panic!("Failed to fork in order to start {name}: {err}"); } diff --git a/src/procmngr.rs b/src/procmngr.rs index af04a1bf88..3052a6206a 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,8 +1,9 @@ use core::cell::RefCell; +use core::mem::size_of; use alloc::rc::Rc; -use alloc::vec::Vec; use alloc::vec; +use alloc::vec::Vec; use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::{HashMap, HashSet}; @@ -13,7 +14,9 @@ use redox_scheme::{ }; use slab::Slab; use syscall::schemev2::NewFdFlags; -use syscall::{Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT}; +use syscall::{ + Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT, +}; pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -157,9 +160,7 @@ impl<'a> ProcScheme<'a> { self.processes.insert( child_pid, Process { - threads: vec! [Rc::new(RefCell::new(Thread { - fd: new_ctxt_fd, - }))], + threads: vec![Rc::new(RefCell::new(Thread { fd: new_ctxt_fd }))], ppid: parent_pid, pgid, sid, @@ -176,8 +177,7 @@ impl<'a> ProcScheme<'a> { fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let fd = todo!(); - proc.threads - .push(Rc::new(RefCell::new(Thread { fd }))); + proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); Ok(fd) } } @@ -188,22 +188,26 @@ impl Scheme for ProcScheme<'_> { return Err(Error::new(EEXIST)); } return Ok(OpenResult::ThisScheme { - number: self - .handles - .insert(Handle::Init), + number: self.handles.insert(Handle::Init), flags: NewFdFlags::empty(), }); } Err(Error::new(ENOENT)) } - fn read(&mut self, id: usize, buf: &mut [u8], _offset: u64, _fcntl_flags: u32) -> Result { + fn read( + &mut self, + id: usize, + buf: &mut [u8], + _offset: u64, + _fcntl_flags: u32, + ) -> Result { match self.handles[id] { Handle::Proc(pid) => { let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let metadata = ProcMeta { - pid: pid.into(), - pgid: process.pgid.into(), - ppid: process.ppid.into(), + pid: pid.0 as u32, + pgid: process.pgid.0 as u32, + ppid: process.ppid.0 as u32, euid: process.euid, egid: process.egid, ruid: process.ruid, @@ -211,6 +215,10 @@ impl Scheme for ProcScheme<'_> { ens: process.ens, rns: process.rns, }; + *buf.get_mut(..size_of::()) + .and_then(|b| plain::from_mut_bytes(b).ok()) + .ok_or(Error::new(EINVAL))? = metadata; + Ok(size_of::()) } Handle::Init => return Err(Error::new(EBADF)), } @@ -224,16 +232,21 @@ impl Scheme for ProcScheme<'_> { number: self.handles.insert(Handle::Proc(child_pid)), flags: NewFdFlags::empty(), }) - }, + } b"new-thread" => Ok(OpenResult::OtherScheme { fd: self.new_thread(pid)?.take(), }), - w if w.starts_with("thread-") => { - let idx = core::str::from_utf8(&w["thread-".len()..]).ok().and_then(|s| s.parse::().ok()).ok_or(Error::new(EINVAL))?; + w if w.starts_with(b"thread-") => { + let idx = core::str::from_utf8(&w["thread-".len()..]) + .ok() + .and_then(|s| s.parse::().ok()) + .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); - return Ok(OpenResult::OtherScheme { fd: syscall::dup(*thread.fd, &[])? }); + return Ok(OpenResult::OtherScheme { + fd: syscall::dup(*thread.fd, &[])?, + }); } _ => return Err(Error::new(EINVAL)), }, diff --git a/src/start.rs b/src/start.rs index ebaeb17505..f85ec084ab 100644 --- a/src/start.rs +++ b/src/start.rs @@ -78,8 +78,6 @@ pub unsafe extern "C" fn start() -> ! { ) .expect("mprotect failed for rest of memory"); - redox_rt::initialize_freestanding(); - // FIXME make the initfs read-only crate::exec::main(); From 737d87cf9d570b9defa873dd7462f28ec052e34f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 16:32:48 +0100 Subject: [PATCH 06/77] Reach init fork. --- src/exec.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/exec.rs b/src/exec.rs index 3d247cbc9d..e092d6ec5b 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -75,6 +75,8 @@ pub fn main() -> ! { sigprocmask: 0, sigignmask: 0, umask: redox_rt::sys::get_umask(), + thr_fd: **this_thr_fd, + proc_fd: **init_proc_fd, }; let path = "/scheme/initfs/bin/init"; From 063430f7acaa27f38c020967c3fd099ee119ea03 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 16:35:31 +0100 Subject: [PATCH 07/77] Sync kernel ctxt attrs in fork. --- src/procmngr.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index 3052a6206a..cdfbad5b6a 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -15,7 +15,8 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, + O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -156,6 +157,19 @@ impl<'a> ProcScheme<'a> { } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + let attr_fd = FdGuard::new(syscall::dup( + *new_ctxt_fd, + alloc::format!("attrs-{}", **self.auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: child_pid.0 as u32, + euid, + egid, + ens, + }, + )?; self.processes.insert( child_pid, From a839c8f2596b6b3baa2f5a04737003812f548184 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 19:55:08 +0100 Subject: [PATCH 08/77] Update redox-scheme. --- Cargo.lock | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fa5a500e3e..a0c534e2fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -26,8 +38,15 @@ dependencies = [ "redox-rt", "redox-scheme", "redox_syscall", + "slab", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "generic-rt" version = "0.1.0" @@ -49,6 +68,9 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "libc" @@ -92,6 +114,12 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "plain" version = "0.2.3" @@ -185,6 +213,15 @@ dependencies = [ "syn", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "spinning_top" version = "0.2.5" @@ -210,3 +247,29 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From 84b4d1f274c4f076c162a9f32b317891d3572428 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 27 Dec 2024 21:19:25 +0100 Subject: [PATCH 09/77] Implement basic state machines, add setrens. --- src/initfs.rs | 29 ++++---- src/procmngr.rs | 177 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 176 insertions(+), 30 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index 7568b15e9f..b9f9d03b9a 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -9,7 +9,7 @@ use hashbrown::HashMap; use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; use redox_path::canonicalize_to_standard; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme}; +use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, RequestKind}; use redox_scheme::{SignalBehavior, Socket}; use syscall::data::Stat; @@ -88,8 +88,8 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { }) } -impl Scheme for InitFsScheme { - fn xopen(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { +impl SchemeSync for InitFsScheme { + fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -175,6 +175,7 @@ impl Scheme for InitFsScheme { buffer: &mut [u8], offset: u64, _fcntl_flags: u32, + _ctx: &CallerCtx, ) -> Result { let Ok(offset) = usize::try_from(offset) else { return Ok(0); @@ -243,19 +244,19 @@ impl Scheme for InitFsScheme { Ok(buf) } - fn fsize(&mut self, id: usize) -> Result { + fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; Ok(inode_len(Self::get_inode(&self.fs, handle.inode)?)? as u64) } - fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize) -> Result { + fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; Ok(0) } - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; // TODO: Copy scheme part in kernel @@ -270,7 +271,7 @@ impl Scheme for InitFsScheme { Ok(scheme_bytes + path_bytes) } - fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; let Timespec { sec, nsec } = self.fs.image_creation_time(); @@ -292,20 +293,20 @@ impl Scheme for InitFsScheme { stat.st_mtime = sec.get(); stat.st_mtime_nsec = nsec.get(); - Ok(0) + Ok(()) } - fn fsync(&mut self, id: usize) -> Result { + fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { if !self.handles.contains_key(&id) { return Err(Error::new(EBADF)); } - Ok(0) + Ok(()) } - fn close(&mut self, id: usize) -> Result { + fn close(&mut self, id: usize) -> Result<()> { let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(0) + Ok(()) } } @@ -327,7 +328,9 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }) else { continue; }; - let resp = req.handle_scheme(&mut scheme); + let resp = req + .handle_sync(&mut scheme) + .expect("failed to handle request"); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmngr.rs b/src/procmngr.rs index cdfbad5b6a..904af7c011 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -1,23 +1,31 @@ use core::cell::RefCell; +use core::hash::BuildHasherDefault; use core::mem::size_of; +use core::task::Poll; +use core::task::Poll::*; +use alloc::collections::VecDeque; use alloc::rc::Rc; use alloc::vec; use alloc::vec::Vec; -use hashbrown::hash_map::DefaultHashBuilder; +use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::ProcMeta; +use redox_rt::protocol::{ProcCall, ProcMeta}; +use redox_scheme::scheme::Op; use redox_scheme::{ - CallerCtx, OpenResult, RequestKind, Response, Scheme, SendFdRequest, SignalBehavior, Socket, + CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, - O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, ENOSYS, + EOPNOTSUPP, EPERM, O_CLOEXEC, O_CREAT, }; +enum PendingState { + Waiting, +} pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -27,7 +35,24 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); + let mut states = HashMap::::new(); + let mut awoken = VecDeque::::new(); + loop { + for awoken in awoken.drain(..) { + let Entry::Occupied(entry) = states.entry(awoken) else { + continue; + }; + match scheme.work_on(entry) { + Ready(resp) => { + socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel"); + } + Pending => continue, + } + } + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") @@ -35,7 +60,34 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let resp = match req.kind() { - RequestKind::Call(req) => req.handle_scheme(&mut scheme), + RequestKind::Call(req) => { + let res = req.with(|req, caller, op| match op { + Op::Open { path, flags } => { + Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + } + Op::Dup { old_fd, buf } => { + Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), + Op::Call { + fd, + payload, + metadata, + } => scheme + .on_call( + fd, + payload, + metadata, + states.entry(req.request().request_id()), + ) + .map(|r| Response::new(&req, r)), + _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + }); + match res { + Ok(Ready(r)) | Err(r) => r, + Ok(Pending) => continue, + } + } RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), _ => continue, }; @@ -194,9 +246,7 @@ impl<'a> ProcScheme<'a> { proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); Ok(fd) } -} -impl Scheme for ProcScheme<'_> { - fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { if core::mem::replace(&mut self.init_claimed, true) { return Err(Error::new(EEXIST)); @@ -208,13 +258,7 @@ impl Scheme for ProcScheme<'_> { } Err(Error::new(ENOENT)) } - fn read( - &mut self, - id: usize, - buf: &mut [u8], - _offset: u64, - _fcntl_flags: u32, - ) -> Result { + fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; @@ -237,7 +281,7 @@ impl Scheme for ProcScheme<'_> { Handle::Init => return Err(Error::new(EBADF)), } } - fn xdup(&mut self, old_id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { + fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { @@ -267,4 +311,103 @@ impl Scheme for ProcScheme<'_> { Handle::Init => Err(Error::new(EBADF)), } } + pub fn on_call( + &mut self, + id: usize, + payload: &mut [u8], + metadata: &[u64], + state: Entry, + ) -> Poll> { + match self.handles[id] { + Handle::Init => Ready(Err(Error::new(EBADF))), + Handle::Proc(pid) => { + let verb = + ProcCall::try_from_raw(metadata[0] as usize).ok_or(Error::new(EINVAL))?; + fn cvt_u32(u: u32) -> Option { + if u == u32::MAX { + None + } else { + Some(u) + } + } + match verb { + ProcCall::Setrens => Ready( + self.on_setrens( + pid, + cvt_u32(metadata[1] as u32), + cvt_u32(metadata[2] as u32), + ) + .map(|()| 0), + ), + ProcCall::Waitpid => Ready(Err(Error::new(EOPNOTSUPP))), + } + } + } + } + pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { + let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let setrns = if rns.is_none() { + // Ignore RNS if -1 is passed + false + } else if rns == Some(0) { + // Allow entering capability mode + true + } else if process.rns == 0 { + // Do not allow leaving capability mode + return Err(Error::new(EPERM)); + } else if process.euid == 0 { + // Allow setting RNS if root + true + } else if rns == Some(process.ens) { + // Allow setting RNS if used for ENS + true + } else if rns == Some(process.rns) { + // Allow setting RNS if used for RNS + true + } else { + // Not permitted otherwise + return Err(Error::new(EPERM)); + }; + + let setens = if ens.is_none() { + // Ignore ENS if -1 is passed + false + } else if ens == Some(0) { + // Allow entering capability mode + true + } else if process.ens == 0 { + // Do not allow leaving capability mode + return Err(Error::new(EPERM)); + } else if process.euid == 0 { + // Allow setting ENS if root + true + } else if ens == Some(process.ens) { + // Allow setting ENS if used for ENS + true + } else if ens == Some(process.rns) { + // Allow setting ENS if used for RNS + true + } else { + // Not permitted otherwise + return Err(Error::new(EPERM)); + }; + + if setrns { + process.rns = rns.unwrap(); + } + + if setens { + process.ens = ens.unwrap(); + } + Ok(()) + } + pub fn work_on( + &mut self, + mut entry: OccupiedEntry, + ) -> Poll { + match entry.get_mut() { + // TODO + PendingState::Waiting => Poll::Pending, + } + } } From 2293828906cffff757dc29e27455b77e818b3604 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 19 Jan 2025 14:56:54 +0100 Subject: [PATCH 10/77] WIP: waitpid handler. --- src/procmngr.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 11 deletions(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index 904af7c011..bebb4b2e85 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -12,7 +12,7 @@ use alloc::vec::Vec; use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcMeta}; +use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; use redox_scheme::scheme::Op; use redox_scheme::{ CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, @@ -20,12 +20,9 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, ENOSYS, - EOPNOTSUPP, EPERM, O_CLOEXEC, O_CREAT, + Error, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, + ENOSYS, EOPNOTSUPP, EPERM, ESRCH, O_CLOEXEC, O_CREAT, }; -enum PendingState { - Waiting, -} pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::create("proc").expect("failed to open proc scheme socket"); @@ -102,7 +99,15 @@ pub fn run(write_fd: usize, auth: &FdGuard) { unreachable!() } +enum PendingState { + AwaitingStatusChange { + waiter: ProcessId, + target: WaitpidTarget, + }, + AwaitingThreadsTermination(ProcessId), +} +#[derive(Debug)] struct Process { threads: Vec>>, ppid: ProcessId, @@ -115,7 +120,20 @@ struct Process { egid: u32, rns: u32, ens: u32, + + status: ProcessStatus, + + waitpid: Vec, + children_waitpid: Vec, } +#[derive(Debug, Clone, Copy)] +enum ProcessStatus { + PossiblyRunnable, + Stopped(usize), + Exiting { status: i32 }, + Exited { status: i32 }, +} +#[derive(Debug)] struct Thread { fd: FdGuard, // sig_ctrl: MmapGuard<...> @@ -127,27 +145,35 @@ const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { processes: HashMap, - process_groups: HashSet, sessions: HashSet, handles: Slab, init_claimed: bool, next_id: ProcessId, + waitpgid: HashMap, DefaultHashBuilder>, auth: &'a FdGuard, } +#[derive(Debug)] enum Handle { Init, Proc(ProcessId), } +enum WaitpidTarget { + SingleProc(ProcessId), + ProcGroup(ProcessId), + AnyChild, + AnyGroupMember, +} + impl<'a> ProcScheme<'a> { pub fn new(auth: &'a FdGuard) -> ProcScheme { ProcScheme { processes: HashMap::new(), - process_groups: HashSet::new(), sessions: HashSet::new(), + waitpgid: HashMap::new(), handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), @@ -182,9 +208,12 @@ impl<'a> ProcScheme<'a> { egid: 0, rns: 1, ens: 1, + + status: ProcessStatus::PossiblyRunnable, + waitpid: Vec::new(), + children_waitpid: Vec::new(), }, ); - self.process_groups.insert(INIT_PID); self.sessions.insert(INIT_PID); *st = Handle::Proc(INIT_PID); @@ -236,6 +265,10 @@ impl<'a> ProcScheme<'a> { egid, rns, ens, + + status: ProcessStatus::PossiblyRunnable, + waitpid: Vec::new(), + children_waitpid: Vec::new(), }, ); Ok(child_pid) @@ -339,11 +372,98 @@ impl<'a> ProcScheme<'a> { ) .map(|()| 0), ), - ProcCall::Waitpid => Ready(Err(Error::new(EOPNOTSUPP))), + ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state), + ProcCall::Waitpid | ProcCall::Waitpgid => { + let req_pid = ProcessId(metadata[1] as usize); + let target = match (verb, metadata[1] == 0) { + (ProcCall::Waitpid, true) => WaitpidTarget::AnyChild, + (ProcCall::Waitpid, false) => WaitpidTarget::SingleProc(req_pid), + (ProcCall::Waitpgid, true) => WaitpidTarget::AnyGroupMember, + (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), + _ => unreachable!(), + }; + self.on_waitpid( + pid, + target, + plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, + WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, + state, + ) + } } } } } + pub fn on_exit_start( + &mut self, + pid: ProcessId, + status: i32, + mut state: Entry, + ) -> Poll> { + let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + match process.status { + ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), + //ProcessStatus::Exiting => return Pending, + ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), + ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), + } + process.status = ProcessStatus::Exiting { status }; + if process.threads.is_empty() { + Self::on_exit_complete(); + Ready(Ok(0)) + } else { + let _ = syscall::write(1, b"\nEXIT PENDING\n"); + self.debug(); + // TODO: check? + state.insert(PendingState::AwaitingThreadsTermination(pid)); + Pending + } + } + fn on_exit_complete() { + // TODO: send waitpid status + } + pub fn on_waitpid( + &mut self, + this_pid: ProcessId, + target: WaitpidTarget, + stat_loc: &mut i32, + flags: WaitFlags, + mut state: Entry, + ) -> Poll> { + let _ = syscall::write(1, b"\nWAITPID\n"); + self.debug(); + Pending + } + fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { + struct Iter<'a> { + cur: Option, + procs: &'a HashMap, + } + impl Iterator for Iter<'_> { + type Item = ProcessId; + + fn next(&mut self) -> Option { + let proc = self.procs.get(&self.cur?)?; + self.cur = Some(proc.ppid); + Some(proc.ppid) + } + } + Iter { + cur: Some(pid), + procs: &self.processes, + } + } + fn check_waitpid_queues( + &mut self, + waiter: ProcessId, + target: WaitpidTarget, + mask: WaitFlags, + ) -> Option<(ProcessId, i32)> { + /*match target { + //WaitpidTarget::SingleProc(target_pid) => , + }*/ + todo!() + } pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let setrns = if rns.is_none() { @@ -407,7 +527,18 @@ impl<'a> ProcScheme<'a> { ) -> Poll { match entry.get_mut() { // TODO - PendingState::Waiting => Poll::Pending, + PendingState::AwaitingThreadsTermination(_) => Pending, + PendingState::AwaitingStatusChange { waiter, target } => Pending, } } + fn debug(&self) { + let _ = syscall::write( + 1, + alloc::format!("PROCESSES\n\n{:#?}\n\n", self.processes).as_bytes(), + ); + let _ = syscall::write( + 1, + alloc::format!("HANDLES\n\n{:#?}\n\n", self.handles).as_bytes(), + ); + } } From 5fba53023e03a8886ab513d3a85f184e42940a38 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 21 Feb 2025 11:33:10 +0100 Subject: [PATCH 11/77] Add event queue. --- src/procmngr.rs | 193 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 55 deletions(-) diff --git a/src/procmngr.rs b/src/procmngr.rs index bebb4b2e85..84bc29090e 100644 --- a/src/procmngr.rs +++ b/src/procmngr.rs @@ -11,22 +11,35 @@ use alloc::vec::Vec; use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; use hashbrown::{HashMap, HashSet}; + use redox_rt::proc::FdGuard; use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; use redox_scheme::scheme::Op; use redox_scheme::{ - CallerCtx, Id, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, + CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, + Socket, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, - ENOSYS, EOPNOTSUPP, EPERM, ESRCH, O_CLOEXEC, O_CREAT, + Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, + EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, + O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { - let socket = Socket::create("proc").expect("failed to open proc scheme socket"); - let mut scheme = ProcScheme::new(auth); + let socket = Socket::nonblock("proc").expect("failed to open proc scheme socket"); + + // TODO? + let socket_ident = socket.inner().raw(); + + let queue = RawEventQueue::new().expect("failed to create event queue"); + + queue + .subscribe(socket.inner().raw(), socket_ident, EventFlags::EVENT_READ) + .expect("failed to listen to scheme socket events"); + + let mut scheme = ProcScheme::new(auth, &queue); let _ = syscall::write(1, b"process manager started\n").unwrap(); let _ = syscall::write(write_fd, &[0]); @@ -35,70 +48,102 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut states = HashMap::::new(); let mut awoken = VecDeque::::new(); - loop { + 'outer: loop { for awoken in awoken.drain(..) { let Entry::Occupied(entry) = states.entry(awoken) else { continue; }; match scheme.work_on(entry) { - Ready(resp) => { - socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel"); - } + Ready(resp) => loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } + } + }, Pending => continue, } } + // TODO: multiple events? + let event = queue.next_event().expect("failed to get next event"); - let Some(req) = socket - .next_request(SignalBehavior::Restart) - .expect("bootstrap: failed to read scheme request from kernel") - else { - continue; - }; - let resp = match req.kind() { - RequestKind::Call(req) => { - let res = req.with(|req, caller, op| match op { - Op::Open { path, flags } => { - Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + if event.data == socket_ident { + let req = loop { + match socket.next_request(SignalBehavior::Interrupt) { + Ok(None) => break 'outer, + Ok(Some(req)) => break req, + Err(e) if e.errno == EINTR => continue, + // spurious event + Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => continue 'outer, + Err(other) => { + panic!("bootstrap: failed to read scheme request from kernel: {other}") } - Op::Dup { old_fd, buf } => { - Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + }; + let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states) else { + continue 'outer; + }; + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") } - Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), - Op::Call { - fd, - payload, - metadata, - } => scheme - .on_call( - fd, - payload, - metadata, - states.entry(req.request().request_id()), - ) - .map(|r| Response::new(&req, r)), - _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), - }); - match res { - Ok(Ready(r)) | Err(r) => r, - Ok(Pending) => continue, } } - RequestKind::SendFd(req) => scheme.on_sendfd(&socket, &req), - _ => continue, - }; - - if !socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel") - { - break; + } else { + let _ = syscall::write(1, b"\nTODO: EVENT\n"); } } unreachable!() } +fn handle_scheme<'a>( + req: Request, + socket: &'a Socket, + scheme: &mut ProcScheme<'a>, + states: &mut HashMap, +) -> Option { + match req.kind() { + RequestKind::Call(req) => { + let res = req.with(|req, caller, op| match op { + Op::Open { path, flags } => { + Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) + } + Op::Dup { old_fd, buf } => { + Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) + } + Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), + Op::Call { + fd, + payload, + metadata, + } => scheme + .on_call( + fd, + payload, + metadata, + states.entry(req.request().request_id()), + ) + .map(|r| Response::new(&req, r)), + _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + }); + match res { + Ok(Ready(r)) | Err(r) => Some(r), + // waker has already been registered, so the logic for the caller is to not send a + // response and continue with remaining events + Ok(Pending) => None, + } + } + RequestKind::SendFd(req) => Some(scheme.on_sendfd(socket, &req)), + _ => None, + } +} enum PendingState { AwaitingStatusChange { waiter: ProcessId, @@ -152,6 +197,7 @@ struct ProcScheme<'a> { next_id: ProcessId, waitpgid: HashMap, DefaultHashBuilder>, + queue: &'a RawEventQueue, auth: &'a FdGuard, } @@ -167,9 +213,40 @@ enum WaitpidTarget { AnyChild, AnyGroupMember, } +// TODO: Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and +// backend +struct RawEventQueue(FdGuard); +impl RawEventQueue { + pub fn new() -> Result { + syscall::open("/scheme/event", O_CREAT) + .map(FdGuard::new) + .map(Self) + } + pub fn subscribe(&self, fd: usize, ident: usize, flags: EventFlags) -> Result<()> { + let _ = syscall::write( + *self.0, + &Event { + id: fd, + data: ident, + flags, + }, + )?; + Ok(()) + } + pub fn next_event(&self) -> Result { + let mut event = Event::default(); + let read = syscall::read(*self.0, &mut event)?; + assert_eq!( + read, + size_of::(), + "event queue EOF currently undefined" + ); + Ok(event) + } +} impl<'a> ProcScheme<'a> { - pub fn new(auth: &'a FdGuard) -> ProcScheme { + pub fn new(auth: &'a FdGuard, queue: &'a RawEventQueue) -> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), sessions: HashSet::new(), @@ -177,6 +254,7 @@ impl<'a> ProcScheme<'a> { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + queue, auth, } } @@ -192,9 +270,14 @@ impl<'a> ProcScheme<'a> { if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { return Response::for_sendfd(&req, Err(e)); }; - let thread = Rc::new(RefCell::new(Thread { - fd: FdGuard::new(fd_out), - })); + let fd = FdGuard::new(fd_out); + + // TODO: Use global thread id etc. rather than reusing fd for identifier? + self.queue + .subscribe(*fd, fd_out, EventFlags::EVENT_READ) + .expect("TODO"); + + let thread = Rc::new(RefCell::new(Thread { fd })); self.processes.insert( INIT_PID, Process { From 910c714b46d2b3d3510311a9eb5d498ac76e3f0a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 21 Feb 2025 11:34:00 +0100 Subject: [PATCH 12/77] Rename procmngr => procmgr. --- src/exec.rs | 2 +- src/lib.rs | 2 +- src/{procmngr.rs => procmgr.rs} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{procmngr.rs => procmgr.rs} (100%) diff --git a/src/exec.rs b/src/exec.rs index e092d6ec5b..825e4003bf 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -63,7 +63,7 @@ pub fn main() -> ! { ); spawn("process manager", &auth, &this_thr_fd, |write_fd| { - crate::procmngr::run(write_fd, &auth) + crate::procmgr::run(write_fd, &auth) }); let init_proc_fd = unsafe { redox_rt::proc::make_init() }; diff --git a/src/lib.rs b/src/lib.rs index 71e88a352c..515955489b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub mod arch; pub mod exec; pub mod initfs; -pub mod procmngr; +pub mod procmgr; pub mod start; extern crate alloc; diff --git a/src/procmngr.rs b/src/procmgr.rs similarity index 100% rename from src/procmngr.rs rename to src/procmgr.rs From 547b50e4b03df7d925c4887acbd9e0675b48eddb Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 23 Feb 2025 16:42:16 +0100 Subject: [PATCH 13/77] Successfully await thread death in exit impl. --- src/initfs.rs | 40 ++++++++++++++++++++-------------------- src/procmgr.rs | 31 ++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index b9f9d03b9a..7bfa8a7ed3 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -303,11 +303,6 @@ impl SchemeSync for InitFsScheme { Ok(()) } - - fn close(&mut self, id: usize) -> Result<()> { - let _ = self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(()) - } } pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { @@ -319,24 +314,29 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { let _ = syscall::close(sync_pipe); loop { - let RequestKind::Call(req) = (match socket + let Some(req) = socket .next_request(SignalBehavior::Restart) .expect("bootstrap: failed to read scheme request from kernel") - { - Some(req) => req.kind(), - None => break, - }) else { - continue; - }; - let resp = req - .handle_sync(&mut scheme) - .expect("failed to handle request"); - - if !socket - .write_response(resp, SignalBehavior::Restart) - .expect("bootstrap: failed to write scheme response to kernel") - { + else { break; + }; + match req.kind() { + RequestKind::Call(req) => { + let resp = req + .handle_sync(&mut scheme) + .expect("failed to handle request"); + + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + RequestKind::OnClose { id } => { + scheme.handles.remove(&id); + } + _ => (), } } diff --git a/src/procmgr.rs b/src/procmgr.rs index 84bc29090e..b29b8ec0a7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -131,7 +131,10 @@ fn handle_scheme<'a>( states.entry(req.request().request_id()), ) .map(|r| Response::new(&req, r)), - _ => Ready(Response::new(&req, Err(Error::new(ENOSYS)))), + _ => { + let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); + Ready(Response::new(&req, Err(Error::new(ENOSYS)))) + } }); match res { Ok(Ready(r)) | Err(r) => Some(r), @@ -181,6 +184,7 @@ enum ProcessStatus { #[derive(Debug)] struct Thread { fd: FdGuard, + status_hndl: FdGuard, // sig_ctrl: MmapGuard<...> } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -276,8 +280,9 @@ impl<'a> ProcScheme<'a> { self.queue .subscribe(*fd, fd_out, EventFlags::EVENT_READ) .expect("TODO"); + let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); - let thread = Rc::new(RefCell::new(Thread { fd })); + let thread = Rc::new(RefCell::new(Thread { fd, status_hndl })); self.processes.insert( INIT_PID, Process { @@ -334,11 +339,19 @@ impl<'a> ProcScheme<'a> { ens, }, )?; + let status_fd = FdGuard::new(syscall::dup(*new_ctxt_fd, b"status")?); + + self.queue + .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) + .expect("TODO"); self.processes.insert( child_pid, Process { - threads: vec![Rc::new(RefCell::new(Thread { fd: new_ctxt_fd }))], + threads: vec![Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + status_hndl: status_fd, + }))], ppid: parent_pid, pgid, sid, @@ -359,7 +372,9 @@ impl<'a> ProcScheme<'a> { fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let fd = todo!(); - proc.threads.push(Rc::new(RefCell::new(Thread { fd }))); + let status_hndl = todo!(); + proc.threads + .push(Rc::new(RefCell::new(Thread { fd, status_hndl }))); Ok(fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { @@ -468,7 +483,7 @@ impl<'a> ProcScheme<'a> { self.on_waitpid( pid, target, - plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, + plain::from_mut_bytes(payload).unwrap(), //.map_err(|_| Error::new(EINVAL))?, WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, state, ) @@ -495,6 +510,12 @@ impl<'a> ProcScheme<'a> { Self::on_exit_complete(); Ready(Ok(0)) } else { + // terminate all threads + for thread in &process.threads { + let mut thread = thread.borrow_mut(); + syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); + } + let _ = syscall::write(1, b"\nEXIT PENDING\n"); self.debug(); // TODO: check? From edb8d80d4331f3658b0da56e0b2c695933c383b4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 26 Feb 2025 11:22:51 +0100 Subject: [PATCH 14/77] Add lookup event id => (thread, pid). --- src/procmgr.rs | 61 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b29b8ec0a7..86f0b566e5 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -5,7 +5,7 @@ use core::task::Poll; use core::task::Poll::*; use alloc::collections::VecDeque; -use alloc::rc::Rc; +use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; @@ -96,8 +96,25 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } } + } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { + let Some(thread) = thread.upgrade() else { + let _ = syscall::write( + 1, + alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), + ); + continue; + }; + let _ = syscall::write( + 1, + alloc::format!( + "\nTHREAD EVENT FROM {}, {}, \n", + event.data, + thread.borrow().pid.0 + ) + .as_bytes(), + ); } else { - let _ = syscall::write(1, b"\nTODO: EVENT\n"); + let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); } } @@ -185,6 +202,7 @@ enum ProcessStatus { struct Thread { fd: FdGuard, status_hndl: FdGuard, + pid: ProcessId, // sig_ctrl: MmapGuard<...> } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -197,6 +215,8 @@ struct ProcScheme<'a> { sessions: HashSet, handles: Slab, + thread_lookup: HashMap>>, + init_claimed: bool, next_id: ProcessId, @@ -255,6 +275,7 @@ impl<'a> ProcScheme<'a> { processes: HashMap::new(), sessions: HashSet::new(), waitpgid: HashMap::new(), + thread_lookup: HashMap::new(), handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), @@ -282,7 +303,12 @@ impl<'a> ProcScheme<'a> { .expect("TODO"); let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); - let thread = Rc::new(RefCell::new(Thread { fd, status_hndl })); + let thread = Rc::new(RefCell::new(Thread { + fd, + status_hndl, + pid: INIT_PID, + })); + let thread_weak = Rc::downgrade(&thread); self.processes.insert( INIT_PID, Process { @@ -304,6 +330,8 @@ impl<'a> ProcScheme<'a> { ); self.sessions.insert(INIT_PID); + self.thread_lookup.insert(fd_out, thread_weak); + *st = Handle::Proc(INIT_PID); Response::for_sendfd(&req, Ok(0)) } @@ -345,13 +373,18 @@ impl<'a> ProcScheme<'a> { .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) .expect("TODO"); + let thread_ident = *new_ctxt_fd; + let thread = Rc::new(RefCell::new(Thread { + fd: new_ctxt_fd, + status_hndl: status_fd, + pid: child_pid, + })); + let thread_weak = Rc::downgrade(&thread); + self.processes.insert( child_pid, Process { - threads: vec![Rc::new(RefCell::new(Thread { - fd: new_ctxt_fd, - status_hndl: status_fd, - }))], + threads: vec![thread], ppid: parent_pid, pgid, sid, @@ -367,14 +400,22 @@ impl<'a> ProcScheme<'a> { children_waitpid: Vec::new(), }, ); + self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; - let fd = todo!(); + let fd: FdGuard = todo!(); let status_hndl = todo!(); - proc.threads - .push(Rc::new(RefCell::new(Thread { fd, status_hndl }))); + let ident = *fd; + let thread = Rc::new(RefCell::new(Thread { + fd, + status_hndl, + pid, + })); + let thread_weak = Rc::downgrade(&thread); + proc.threads.push(thread); + self.thread_lookup.insert(ident, thread_weak); Ok(fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { From 82d59302c84df509f298fd6b9b2c2c1231d70261 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 27 Feb 2025 00:55:05 +0100 Subject: [PATCH 15/77] Impl basic state machine for proc exit. --- src/procmgr.rs | 62 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 86f0b566e5..11894725f1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -22,9 +22,9 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, EBADF, EBADFD, - EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, - O_CREAT, + ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, + EBADF, EBADFD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, + O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -97,22 +97,36 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { - let Some(thread) = thread.upgrade() else { + let Some(thread_rc) = thread.upgrade() else { let _ = syscall::write( 1, alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), ); continue; }; + let thread = thread_rc.borrow(); + let Some(proc) = scheme.processes.get_mut(&thread.pid) else { + // TODO? + continue; + }; let _ = syscall::write( 1, - alloc::format!( - "\nTHREAD EVENT FROM {}, {}, \n", - event.data, - thread.borrow().pid.0 - ) - .as_bytes(), + alloc::format!("\nTHREAD EVENT FROM {}, {}, \n", event.data, thread.pid.0) + .as_bytes(), ); + let mut buf = 0_usize.to_ne_bytes(); + let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); + let status = usize::from_ne_bytes(buf); + + let _ = syscall::write(1, alloc::format!("\nSTATUS {status}\n",).as_bytes()); + + if status != ContextStatus::Dead as usize { + // spurious event + continue; + } + scheme.thread_lookup.remove(&event.data); + proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); } @@ -188,6 +202,7 @@ struct Process { status: ProcessStatus, + awaiting_threads_term: Vec, waitpid: Vec, children_waitpid: Vec, } @@ -326,6 +341,7 @@ impl<'a> ProcScheme<'a> { status: ProcessStatus::PossiblyRunnable, waitpid: Vec::new(), children_waitpid: Vec::new(), + awaiting_threads_term: Vec::new(), }, ); self.sessions.insert(INIT_PID); @@ -398,6 +414,7 @@ impl<'a> ProcScheme<'a> { status: ProcessStatus::PossiblyRunnable, waitpid: Vec::new(), children_waitpid: Vec::new(), + awaiting_threads_term: Vec::new(), }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -524,7 +541,7 @@ impl<'a> ProcScheme<'a> { self.on_waitpid( pid, target, - plain::from_mut_bytes(payload).unwrap(), //.map_err(|_| Error::new(EINVAL))?, + plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, state, ) @@ -551,15 +568,17 @@ impl<'a> ProcScheme<'a> { Self::on_exit_complete(); Ready(Ok(0)) } else { - // terminate all threads + // terminate all threads (possibly including the caller, resulting in EINTR and a + // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); } let _ = syscall::write(1, b"\nEXIT PENDING\n"); - self.debug(); + //self.debug(); // TODO: check? + process.awaiting_threads_term.push(*state.key()); state.insert(PendingState::AwaitingThreadsTermination(pid)); Pending } @@ -670,10 +689,21 @@ impl<'a> ProcScheme<'a> { &mut self, mut entry: OccupiedEntry, ) -> Poll { - match entry.get_mut() { + let req_id = *entry.key(); + match *entry.get_mut() { // TODO - PendingState::AwaitingThreadsTermination(_) => Pending, - PendingState::AwaitingStatusChange { waiter, target } => Pending, + PendingState::AwaitingThreadsTermination(pid) => { + let Some(proc) = self.processes.get_mut(&pid) else { + return Pending; // TODO + }; + if proc.threads.is_empty() { + let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + Ready(Response::new(req_id, Ok(0))) + } else { + Pending + } + } + PendingState::AwaitingStatusChange { waiter, ref target } => Pending, } } fn debug(&self) { From cbe00a9be86f3808a5ec1272d0f016de23ef141d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 1 Mar 2025 15:48:49 +0100 Subject: [PATCH 16/77] Impl waitpid further, still does not compile. --- src/procmgr.rs | 306 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 265 insertions(+), 41 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 11894725f1..3ada198067 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1,9 +1,11 @@ use core::cell::RefCell; +use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::task::Poll; use core::task::Poll::*; +use alloc::collections::btree_map::BTreeMap; use alloc::collections::VecDeque; use alloc::rc::{Rc, Weak}; use alloc::vec; @@ -23,8 +25,8 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, - O_CLOEXEC, O_CREAT, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EWOULDBLOCK, O_CLOEXEC, O_CREAT, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -47,13 +49,15 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut states = HashMap::::new(); let mut awoken = VecDeque::::new(); + let mut new_awoken = VecDeque::new(); 'outer: loop { + awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { let Entry::Occupied(entry) = states.entry(awoken) else { continue; }; - match scheme.work_on(entry) { + match scheme.work_on(entry, &mut new_awoken) { Ready(resp) => loop { match socket.write_response(resp, SignalBehavior::Interrupt) { Ok(false) => break 'outer, @@ -83,7 +87,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } }; - let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states) else { + let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + else { continue 'outer; }; loop { @@ -139,6 +144,7 @@ fn handle_scheme<'a>( socket: &'a Socket, scheme: &mut ProcScheme<'a>, states: &mut HashMap, + awoken: &mut VecDeque, ) -> Option { match req.kind() { RequestKind::Call(req) => { @@ -160,6 +166,7 @@ fn handle_scheme<'a>( payload, metadata, states.entry(req.request().request_id()), + awoken, ) .map(|r| Response::new(&req, r)), _ => { @@ -182,6 +189,7 @@ enum PendingState { AwaitingStatusChange { waiter: ProcessId, target: WaitpidTarget, + flags: WaitFlags, }, AwaitingThreadsTermination(ProcessId), } @@ -203,15 +211,75 @@ struct Process { status: ProcessStatus, awaiting_threads_term: Vec, - waitpid: Vec, - children_waitpid: Vec, + + waitpid: BTreeMap, + waitpid_waiting: VecDeque, } +#[derive(Copy, Clone, Debug)] +pub struct WaitpidKey { + pub pid: Option, + pub pgid: Option, +} + +// TODO: Is this valid? (transitive?) +impl Ord for WaitpidKey { + fn cmp(&self, other: &WaitpidKey) -> Ordering { + // If both have pid set, compare that + if let Some(s_pid) = self.pid { + if let Some(o_pid) = other.pid { + return s_pid.cmp(&o_pid); + } + } + + // If both have pgid set, compare that + if let Some(s_pgid) = self.pgid { + if let Some(o_pgid) = other.pgid { + return s_pgid.cmp(&o_pgid); + } + } + + // If either has pid set, it is greater + if self.pid.is_some() { + return Ordering::Greater; + } + + if other.pid.is_some() { + return Ordering::Less; + } + + // If either has pgid set, it is greater + if self.pgid.is_some() { + return Ordering::Greater; + } + + if other.pgid.is_some() { + return Ordering::Less; + } + + // If all pid and pgid are None, they are equal + Ordering::Equal + } +} + +impl PartialOrd for WaitpidKey { + fn partial_cmp(&self, other: &WaitpidKey) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for WaitpidKey { + fn eq(&self, other: &WaitpidKey) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for WaitpidKey {} #[derive(Debug, Clone, Copy)] enum ProcessStatus { PossiblyRunnable, Stopped(usize), - Exiting { status: i32 }, - Exited { status: i32 }, + Exiting { signal: Option, status: u8 }, + Exited { signal: Option, status: u8 }, } #[derive(Debug)] struct Thread { @@ -220,7 +288,7 @@ struct Thread { pid: ProcessId, // sig_ctrl: MmapGuard<...> } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); @@ -235,10 +303,15 @@ struct ProcScheme<'a> { init_claimed: bool, next_id: ProcessId, - waitpgid: HashMap, DefaultHashBuilder>, queue: &'a RawEventQueue, auth: &'a FdGuard, } +#[derive(Clone, Copy, Debug)] +enum WaitpidStatus { + Continued, + Stopped { signal: u8 }, + Terminated { signal: Option, status: u8 }, +} #[derive(Debug)] enum Handle { @@ -289,7 +362,6 @@ impl<'a> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), sessions: HashSet::new(), - waitpgid: HashMap::new(), thread_lookup: HashMap::new(), handles: Slab::new(), init_claimed: false, @@ -339,9 +411,9 @@ impl<'a> ProcScheme<'a> { ens: 1, status: ProcessStatus::PossiblyRunnable, - waitpid: Vec::new(), - children_waitpid: Vec::new(), awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), }, ); self.sessions.insert(INIT_PID); @@ -412,9 +484,10 @@ impl<'a> ProcScheme<'a> { ens, status: ProcessStatus::PossiblyRunnable, - waitpid: Vec::new(), - children_waitpid: Vec::new(), awaiting_threads_term: Vec::new(), + + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -506,6 +579,7 @@ impl<'a> ProcScheme<'a> { payload: &mut [u8], metadata: &[u64], state: Entry, + awoken: &mut VecDeque, ) -> Poll> { match self.handles[id] { Handle::Init => Ready(Err(Error::new(EBADF))), @@ -528,7 +602,7 @@ impl<'a> ProcScheme<'a> { ) .map(|()| 0), ), - ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state), + ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state, awoken), ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -538,13 +612,13 @@ impl<'a> ProcScheme<'a> { (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), _ => unreachable!(), }; - self.on_waitpid( - pid, + let state = state.insert(PendingState::AwaitingStatusChange { + waiter: req_pid, target, - plain::from_mut_bytes(payload).map_err(|_| Error::new(EINVAL))?, - WaitFlags::from_bits(metadata[2] as usize).ok_or(Error::new(EINVAL))?, - state, - ) + flags: WaitFlags::from_bits(metadata[2] as usize) + .ok_or(Error::new(EINVAL))?, + }); + self.work_on(state, &mut awoken, Some(payload)) } } } @@ -555,6 +629,7 @@ impl<'a> ProcScheme<'a> { pid: ProcessId, status: i32, mut state: Entry, + awoken: &mut VecDeque, ) -> Poll> { let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; match process.status { @@ -563,40 +638,155 @@ impl<'a> ProcScheme<'a> { ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), } - process.status = ProcessStatus::Exiting { status }; - if process.threads.is_empty() { - Self::on_exit_complete(); - Ready(Ok(0)) - } else { + // TODO: status/signal + process.status = ProcessStatus::Exiting { + status: status as u8, + signal: None, + }; + if !process.threads.is_empty() { // terminate all threads (possibly including the caller, resulting in EINTR and a // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); - syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()).expect("TODO"); + syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes())?; } let _ = syscall::write(1, b"\nEXIT PENDING\n"); //self.debug(); // TODO: check? process.awaiting_threads_term.push(*state.key()); - state.insert(PendingState::AwaitingThreadsTermination(pid)); - Pending } - } - fn on_exit_complete() { - // TODO: send waitpid status + self.work_on( + state.insert(PendingState::AwaitingThreadsTermination(pid)), + awoken, + None, + ) } pub fn on_waitpid( &mut self, this_pid: ProcessId, target: WaitpidTarget, - stat_loc: &mut i32, flags: WaitFlags, - mut state: Entry, - ) -> Poll> { + ) -> Poll> { + let req_id = *state.key(); + + if matches!( + target, + WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember + ) { + // Check for existence of child. + // TODO: inefficient, keep refcount? + if !self.processes.values().any(|p| p.ppid == this_pid) { + return Ready(Err(Error::new(ECHILD))); + } + } + + let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; let _ = syscall::write(1, b"\nWAITPID\n"); - self.debug(); - Pending + + let recv_nonblock = |waitpid: &mut BTreeMap, + key: &WaitpidKey| + -> Option<(WaitpidKey, WaitpidStatus)> { + if let Some((key, value)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { + waitpid.remove(&key); + Some((key, value)) + } else { + None + } + }; + let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { + match status { + WaitpidStatus::Continued => { + // TODO: Handle None, i.e. restart everything until a match is found + if flags.contains(WaitFlags::WCONTINUED) { + Option::<(ProcessId, i32)>::Some((w_pid, todo!())) + } else { + None + } + } + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Some((w_pid, todo!())) + } else { + None + } + } + WaitpidStatus::Terminated { signal, status } => Some((w_pid, todo!())), + } + }; + + match target { + // TODO: not the same + WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { + if let Some((wid, (w_pid, status))) = + proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) + { + let _ = proc.waitpid.remove(&wid); + Ready(grim_reaper(w_pid, status)) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + WaitpidTarget::SingleProc(pid) => { + if this_pid == pid { + return Err(Error::new(EINVAL)); + } + let [proc, target_proc] = self + .processes + .get_many_mut((&this_pid, &pid)) + .ok_or(Error::new(ESRCH))?; + if target_proc.ppid != this_pid { + return Ready(Err(Error::new(ECHILD))); + } + let key = WaitpidKey { + pid: Some(pid), + pgid: None, + }; + if let ProcessStatus::Exited { status, signal } = target_proc.status { + let _ = recv_nonblock(&mut proc.waitpid, &key); + Ready(grim_reaper( + pid, + WaitpidStatus::Terminated { signal, status }, + )) + } else { + let res = recv_nonblock(&mut proc.waitpid, &key); + if let Some((w_pid, status)) = res { + Ready(grim_reaper(w_pid, status)) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + } + WaitpidTarget::ProcGroup(pgid) => { + let this_pgid = proc.pgid; + if !self.processes.values().any(|p| p.pgid == this_pgid) { + return Ready(Err(Error::new(ECHILD))); + } + + // reborrow proc + let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; + + let key = WaitpidKey { + pid: None, + pgid: Some(pgid), + }; + if let Some((w_pid, status)) = proc.waitpid.get(&key) { + let _ = proc.waitpid.remove(&key); + grim_reaper(w_pid, status) + } else if flags.contains(WaitFlags::WNOHANG) { + Ready(Ok((0, 0))) + } else { + proc.waitpid_waiting.push_back(req_id); + Pending + } + } + } } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { @@ -688,22 +878,56 @@ impl<'a> ProcScheme<'a> { pub fn work_on( &mut self, mut entry: OccupiedEntry, + awoken: &mut VecDeque, + buffer: Option<&mut [u8]>, ) -> Poll { let req_id = *entry.key(); match *entry.get_mut() { // TODO - PendingState::AwaitingThreadsTermination(pid) => { - let Some(proc) = self.processes.get_mut(&pid) else { + PendingState::AwaitingThreadsTermination(current_pid) => { + let Some(proc) = self.processes.get_mut(¤t_pid) else { return Pending; // TODO }; if proc.threads.is_empty() { let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + let (signal, status) = match proc.status { + ProcessStatus::Exiting { signal, status } => (signal, status), + ProcessStatus::Exited { .. } => return Ready(Response::new(req_id, Ok(0))), + _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? + }; + proc.status = ProcessStatus::Exited { signal, status }; + let ppid = proc.ppid; + if let Some(parent) = self.processes.get_mut(&ppid) { + // TODO: transfer children to parent, and all of self.waitpid + parent.waitpid.insert( + WaitpidKey { + pid: Some(current_pid), + pgid: Some(pgid), + }, + (current_pid, WaitpidStatus::Terminated { signal, status }), + ); + // TODO: inefficient + awoken.extend(parent.waitpid_waiting.drain(..)); + } Ready(Response::new(req_id, Ok(0))) } else { Pending } } - PendingState::AwaitingStatusChange { waiter, ref target } => Pending, + PendingState::AwaitingStatusChange { + waiter, + target, + flags, + } => self + .on_waitpid(waiter, target, flags) + .map_ok(|(pid, status)| { + if let Some(buf) = buffer.and_then(|buf| plain::from_mut_bytes::(buf).ok()) + { + *buf = status; + } + pid + }) + .map(|res| Response::new(req_id, res)), } } fn debug(&self) { From 016c60c11fffc883d0b11725916b41d28298d841 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 12:17:20 +0100 Subject: [PATCH 17/77] Fix most errors. --- src/procmgr.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 3ada198067..12d67854f8 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -319,6 +319,7 @@ enum Handle { Proc(ProcessId), } +#[derive(Clone, Copy, Debug)] enum WaitpidTarget { SingleProc(ProcessId), ProcGroup(ProcessId), @@ -684,34 +685,35 @@ impl<'a> ProcScheme<'a> { let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; let _ = syscall::write(1, b"\nWAITPID\n"); - let recv_nonblock = |waitpid: &mut BTreeMap, + let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| - -> Option<(WaitpidKey, WaitpidStatus)> { - if let Some((key, value)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { - waitpid.remove(&key); - Some((key, value)) + -> Option<(ProcessId, WaitpidStatus)> { + if let Some((pid, sts)) = waitpid.get(key).map(|(k, v)| (*k, *v)) { + waitpid.remove(key); + Some((pid, sts)) } else { None } }; let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { + let ret: i32 = todo!(); match status { WaitpidStatus::Continued => { // TODO: Handle None, i.e. restart everything until a match is found if flags.contains(WaitFlags::WCONTINUED) { - Option::<(ProcessId, i32)>::Some((w_pid, todo!())) + Ready((w_pid.0, ret)) } else { - None + Pending } } WaitpidStatus::Stopped { signal } => { if flags.contains(WaitFlags::WUNTRACED) { - Some((w_pid, todo!())) + Ready((w_pid.0, ret)) } else { - None + Pending } } - WaitpidStatus::Terminated { signal, status } => Some((w_pid, todo!())), + WaitpidStatus::Terminated { signal, status } => Ready((w_pid.0, ret)), } }; @@ -722,7 +724,7 @@ impl<'a> ProcScheme<'a> { proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) { let _ = proc.waitpid.remove(&wid); - Ready(grim_reaper(w_pid, status)) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -732,11 +734,11 @@ impl<'a> ProcScheme<'a> { } WaitpidTarget::SingleProc(pid) => { if this_pid == pid { - return Err(Error::new(EINVAL)); + return Ready(Err(Error::new(EINVAL))); } let [proc, target_proc] = self .processes - .get_many_mut((&this_pid, &pid)) + .get_many_mut([&this_pid, &pid]) .ok_or(Error::new(ESRCH))?; if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); @@ -747,14 +749,11 @@ impl<'a> ProcScheme<'a> { }; if let ProcessStatus::Exited { status, signal } = target_proc.status { let _ = recv_nonblock(&mut proc.waitpid, &key); - Ready(grim_reaper( - pid, - WaitpidStatus::Terminated { signal, status }, - )) + grim_reaper(pid, WaitpidStatus::Terminated { signal, status }).map(Ok) } else { let res = recv_nonblock(&mut proc.waitpid, &key); if let Some((w_pid, status)) = res { - Ready(grim_reaper(w_pid, status)) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -776,9 +775,9 @@ impl<'a> ProcScheme<'a> { pid: None, pgid: Some(pgid), }; - if let Some((w_pid, status)) = proc.waitpid.get(&key) { + if let Some(&(w_pid, status)) = proc.waitpid.get(&key) { let _ = proc.waitpid.remove(&key); - grim_reaper(w_pid, status) + grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -896,7 +895,7 @@ impl<'a> ProcScheme<'a> { _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? }; proc.status = ProcessStatus::Exited { signal, status }; - let ppid = proc.ppid; + let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent) = self.processes.get_mut(&ppid) { // TODO: transfer children to parent, and all of self.waitpid parent.waitpid.insert( From 7e8b8d7183b0e6e42e0ae7a5e37d6d74841bc9d3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 15:04:26 +0100 Subject: [PATCH 18/77] Make it compile. --- Cargo.toml | 4 +- src/procmgr.rs | 217 ++++++++++++++++++++++++++++--------------------- 2 files changed, 128 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3413303d20..2f6308d655 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,9 @@ name = "bootstrap" crate-type = ["staticlib"] [dependencies] -hashbrown = { version = "0.14", default-features = false, features = [ +hashbrown = { version = "0.15", default-features = false, features = [ "inline-more", - "ahash", + "default-hasher", ] } linked_list_allocator = "0.10" plain = "0.2" diff --git a/src/procmgr.rs b/src/procmgr.rs index 12d67854f8..f596f39345 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -11,21 +11,21 @@ use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; -use hashbrown::hash_map::{DefaultHashBuilder, Entry, OccupiedEntry}; -use hashbrown::{HashMap, HashSet}; +use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; +use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; -use redox_scheme::scheme::Op; +use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, - Socket, + Socket, Tag, }; use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, }; @@ -54,10 +54,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { 'outer: loop { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { - let Entry::Occupied(entry) = states.entry(awoken) else { + let Entry::Occupied(state) = states.entry(awoken) else { continue; }; - match scheme.work_on(entry, &mut new_awoken) { + match scheme.work_on(state, &mut new_awoken) { Ready(resp) => loop { match socket.write_response(resp, SignalBehavior::Interrupt) { Ok(false) => break 'outer, @@ -87,7 +87,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } }; - let Some(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + let Ready(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) else { continue 'outer; }; @@ -145,44 +145,44 @@ fn handle_scheme<'a>( scheme: &mut ProcScheme<'a>, states: &mut HashMap, awoken: &mut VecDeque, -) -> Option { +) -> Poll { match req.kind() { RequestKind::Call(req) => { - let res = req.with(|req, caller, op| match op { - Op::Open { path, flags } => { - Ready(Response::open_dup_like(&req, scheme.on_open(path, flags))) - } - Op::Dup { old_fd, buf } => { - Ready(Response::open_dup_like(&req, scheme.on_dup(old_fd, buf))) - } - Op::Read { fd, buf, .. } => Ready(Response::new(&req, scheme.on_read(fd, buf))), - Op::Call { - fd, - payload, - metadata, - } => scheme - .on_call( - fd, - payload, - metadata, - states.entry(req.request().request_id()), - awoken, - ) - .map(|r| Response::new(&req, r)), + let req_id = req.request_id(); + let op = match req.op() { + Ok(op) => op, + Err(req) => return Response::ready_err(ENOSYS, req), + }; + match op { + Op::Open(op) => Ready(Response::open_dup_like( + scheme.on_open(op.path(), op.flags), + op, + )), + Op::Dup(op) => Ready(Response::open_dup_like(scheme.on_dup(op.fd, op.buf()), op)), + Op::Read(mut op) => Ready(Response::new(scheme.on_read(op.fd, op.buf()), op)), + Op::Call(op) => scheme.on_call( + { + // TODO: cleanup + states.remove(&req_id); + if let Entry::Vacant(entry) = states.entry(req_id) { + entry + } else { + unreachable!() + } + }, + op, + awoken, + ), _ => { let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); - Ready(Response::new(&req, Err(Error::new(ENOSYS)))) + Ready(Response::new(Err(Error::new(ENOSYS)), op)) } - }); - match res { - Ok(Ready(r)) | Err(r) => Some(r), - // waker has already been registered, so the logic for the caller is to not send a - // response and continue with remaining events - Ok(Pending) => None, } } - RequestKind::SendFd(req) => Some(scheme.on_sendfd(socket, &req)), - _ => None, + RequestKind::SendFd(req) => Ready(scheme.on_sendfd(socket, req)), + + // ignore + _ => Pending, } } enum PendingState { @@ -190,8 +190,19 @@ enum PendingState { waiter: ProcessId, target: WaitpidTarget, flags: WaitFlags, + op: OpCall, }, - AwaitingThreadsTermination(ProcessId), + AwaitingThreadsTermination(ProcessId, Tag), + Placeholder, +} +impl IntoTag for PendingState { + fn into_tag(self) -> Tag { + match self { + Self::AwaitingThreadsTermination(_, tag) => tag, + Self::AwaitingStatusChange { op, .. } => op.into_tag(), + Self::Placeholder => unreachable!(), + } + } } #[derive(Debug)] @@ -376,12 +387,12 @@ impl<'a> ProcScheme<'a> { self.next_id.0 += 1; id } - fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Response { + fn on_sendfd(&mut self, socket: &Socket, req: SendFdRequest) -> Response { match self.handles[req.id()] { ref mut st @ Handle::Init => { let mut fd_out = usize::MAX; if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), Err(&mut fd_out)) { - return Response::for_sendfd(&req, Err(e)); + return Response::new(Err(e), req); }; let fd = FdGuard::new(fd_out); @@ -422,9 +433,9 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(fd_out, thread_weak); *st = Handle::Proc(INIT_PID); - Response::for_sendfd(&req, Ok(0)) + Response::ok(0, req) } - _ => Response::for_sendfd(&req, Err(Error::new(EBADF))), + _ => Response::err(EBADF, req), } } fn fork(&mut self, parent_pid: ProcessId) -> Result { @@ -576,17 +587,18 @@ impl<'a> ProcScheme<'a> { } pub fn on_call( &mut self, - id: usize, - payload: &mut [u8], - metadata: &[u64], - state: Entry, + state: VacantEntry, + mut op: OpCall, awoken: &mut VecDeque, - ) -> Poll> { + ) -> Poll { + let id = op.fd; + let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { - Handle::Init => Ready(Err(Error::new(EBADF))), + Handle::Init => Response::ready_err(EBADF, op), Handle::Proc(pid) => { - let verb = - ProcCall::try_from_raw(metadata[0] as usize).ok_or(Error::new(EINVAL))?; + let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { + return Response::ready_err(EINVAL, op); + }; fn cvt_u32(u: u32) -> Option { if u == u32::MAX { None @@ -595,15 +607,18 @@ impl<'a> ProcScheme<'a> { } } match verb { - ProcCall::Setrens => Ready( + ProcCall::Setrens => Ready(Response::new( self.on_setrens( pid, cvt_u32(metadata[1] as u32), cvt_u32(metadata[2] as u32), ) .map(|()| 0), - ), - ProcCall::Exit => self.on_exit_start(pid, metadata[1] as i32, state, awoken), + op, + )), + ProcCall::Exit => { + self.on_exit_start(pid, metadata[1] as i32, state, awoken, op.into_tag()) + } ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -613,13 +628,17 @@ impl<'a> ProcScheme<'a> { (ProcCall::Waitpgid, false) => WaitpidTarget::ProcGroup(req_pid), _ => unreachable!(), }; - let state = state.insert(PendingState::AwaitingStatusChange { + let flags = match WaitFlags::from_bits(metadata[2] as usize) { + Some(fl) => fl, + None => return Response::ready_err(EINVAL, op), + }; + let state = state.insert_entry(PendingState::AwaitingStatusChange { waiter: req_pid, target, - flags: WaitFlags::from_bits(metadata[2] as usize) - .ok_or(Error::new(EINVAL))?, + flags, + op, }); - self.work_on(state, &mut awoken, Some(payload)) + self.work_on(state, awoken) } } } @@ -629,15 +648,18 @@ impl<'a> ProcScheme<'a> { &mut self, pid: ProcessId, status: i32, - mut state: Entry, + mut state: VacantEntry, awoken: &mut VecDeque, - ) -> Poll> { - let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + tag: Tag, + ) -> Poll { + let Some(process) = self.processes.get_mut(&pid) else { + return Response::ready_err(EBADFD, tag); + }; match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, - ProcessStatus::Exiting { .. } => return Ready(Err(Error::new(EAGAIN))), - ProcessStatus::Exited { .. } => return Ready(Err(Error::new(ESRCH))), + ProcessStatus::Exiting { .. } => return Response::ready_err(EAGAIN, tag), + ProcessStatus::Exited { .. } => return Response::ready_err(ESRCH, tag), } // TODO: status/signal process.status = ProcessStatus::Exiting { @@ -649,7 +671,9 @@ impl<'a> ProcScheme<'a> { // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); - syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes())?; + if let Err(err) = syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()) { + return Response::ready_err(err.errno, tag); + } } let _ = syscall::write(1, b"\nEXIT PENDING\n"); @@ -658,9 +682,8 @@ impl<'a> ProcScheme<'a> { process.awaiting_threads_term.push(*state.key()); } self.work_on( - state.insert(PendingState::AwaitingThreadsTermination(pid)), + state.insert_entry(PendingState::AwaitingThreadsTermination(pid, tag)), awoken, - None, ) } pub fn on_waitpid( @@ -668,9 +691,8 @@ impl<'a> ProcScheme<'a> { this_pid: ProcessId, target: WaitpidTarget, flags: WaitFlags, + req_id: Id, ) -> Poll> { - let req_id = *state.key(); - if matches!( target, WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember @@ -736,10 +758,11 @@ impl<'a> ProcScheme<'a> { if this_pid == pid { return Ready(Err(Error::new(EINVAL))); } - let [proc, target_proc] = self - .processes - .get_many_mut([&this_pid, &pid]) - .ok_or(Error::new(ESRCH))?; + let [Some(proc), Some(target_proc)] = + self.processes.get_many_mut([&this_pid, &pid]) + else { + return Ready(Err(Error::new(ESRCH))); + }; if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); } @@ -876,23 +899,24 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut entry: OccupiedEntry, + mut state: OccupiedEntry, awoken: &mut VecDeque, - buffer: Option<&mut [u8]>, ) -> Poll { - let req_id = *entry.key(); - match *entry.get_mut() { + let req_id = *state.key(); + let mut state = state.get_mut(); + match core::mem::replace(state, PendingState::Placeholder) { + PendingState::Placeholder => unreachable!(), // TODO - PendingState::AwaitingThreadsTermination(current_pid) => { + PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc) = self.processes.get_mut(¤t_pid) else { - return Pending; // TODO + return Response::ready_err(ESRCH, tag); }; if proc.threads.is_empty() { let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), - ProcessStatus::Exited { .. } => return Ready(Response::new(req_id, Ok(0))), - _ => return Ready(Response::new(req_id, Err(Error::new(ESRCH)))), // TODO? + ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), + _ => return Response::ready_err(ESRCH, tag), // TODO? }; proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); @@ -908,8 +932,10 @@ impl<'a> ProcScheme<'a> { // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } - Ready(Response::new(req_id, Ok(0))) + Ready(Response::new(Ok(0), tag)) } else { + proc.awaiting_threads_term.push(req_id); + *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending } } @@ -917,16 +943,25 @@ impl<'a> ProcScheme<'a> { waiter, target, flags, - } => self - .on_waitpid(waiter, target, flags) - .map_ok(|(pid, status)| { - if let Some(buf) = buffer.and_then(|buf| plain::from_mut_bytes::(buf).ok()) - { - *buf = status; + mut op, + } => match self.on_waitpid(waiter, target, flags, req_id) { + Ready(Ok((pid, status))) => { + if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { + *status_out = status; } - pid - }) - .map(|res| Response::new(req_id, res)), + Response::ready_ok(pid, op) + } + Ready(Err(e)) => Response::ready_err(e.errno, op), + Pending => { + *state = PendingState::AwaitingStatusChange { + waiter, + target, + flags, + op, + }; + Pending + } + }, } } fn debug(&self) { From ddc47a7e43576d2c73c24a5cdb372b80a35b9d90 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 2 Mar 2025 15:20:00 +0100 Subject: [PATCH 19/77] Fix incorrect waitpid request pid. --- src/procmgr.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f596f39345..0c5e768e74 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -595,7 +595,7 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), - Handle::Proc(pid) => { + Handle::Proc(fd_pid) => { let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); }; @@ -609,7 +609,7 @@ impl<'a> ProcScheme<'a> { match verb { ProcCall::Setrens => Ready(Response::new( self.on_setrens( - pid, + fd_pid, cvt_u32(metadata[1] as u32), cvt_u32(metadata[2] as u32), ) @@ -617,7 +617,7 @@ impl<'a> ProcScheme<'a> { op, )), ProcCall::Exit => { - self.on_exit_start(pid, metadata[1] as i32, state, awoken, op.into_tag()) + self.on_exit_start(fd_pid, metadata[1] as i32, state, awoken, op.into_tag()) } ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); @@ -630,10 +630,12 @@ impl<'a> ProcScheme<'a> { }; let flags = match WaitFlags::from_bits(metadata[2] as usize) { Some(fl) => fl, - None => return Response::ready_err(EINVAL, op), + None => { + return Response::ready_err(EINVAL, op); + } }; let state = state.insert_entry(PendingState::AwaitingStatusChange { - waiter: req_pid, + waiter: fd_pid, target, flags, op, From bdb665d897c9d5851ae5e3bd34b4e8adad7c196f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 15:21:39 +0100 Subject: [PATCH 20/77] Ensure new_awoken are handled before event queue wait. --- src/initfs.rs | 4 +-- src/procmgr.rs | 94 +++++++++++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/src/initfs.rs b/src/initfs.rs index 7bfa8a7ed3..a594140c9c 100644 --- a/src/initfs.rs +++ b/src/initfs.rs @@ -322,9 +322,7 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { }; match req.kind() { RequestKind::Call(req) => { - let resp = req - .handle_sync(&mut scheme) - .expect("failed to handle request"); + let resp = req.handle_sync(&mut scheme); if !socket .write_response(resp, SignalBehavior::Restart) diff --git a/src/procmgr.rs b/src/procmgr.rs index 0c5e768e74..047694ae07 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -52,23 +52,30 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - awoken.append(&mut new_awoken); - for awoken in awoken.drain(..) { - let Entry::Occupied(state) = states.entry(awoken) else { - continue; - }; - match scheme.work_on(state, &mut new_awoken) { - Ready(resp) => loop { - match socket.write_response(resp, SignalBehavior::Interrupt) { - Ok(false) => break 'outer, - Ok(_) => break, - Err(err) if err.errno == EINTR => continue, - Err(err) => { - panic!("bootstrap: failed to write scheme response to kernel: {err}") + let _ = syscall::write(1, alloc::format!("\n{awoken:#?}\n").as_bytes()); + while !awoken.is_empty() || !new_awoken.is_empty() { + awoken.append(&mut new_awoken); + for awoken in awoken.drain(..) { + //let _ = syscall::write(1, alloc::format!("\nALL STATES {states:#?}, AWOKEN {awoken:#?}\n").as_bytes()); + let Entry::Occupied(state) = states.entry(awoken) else { + continue; + }; + //let _ = syscall::write(1, alloc::format!("\nSTATE {state:#?}\n").as_bytes()); + match scheme.work_on(state, &mut new_awoken) { + Ready(resp) => { + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } + } } } - }, - Pending => continue, + Pending => continue, + } } } // TODO: multiple events? @@ -131,6 +138,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); + let _ = syscall::write( + 1, + alloc::format!("\nAWAITING {}\n", proc.awaiting_threads_term.len()).as_bytes(), + ); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); @@ -185,6 +196,7 @@ fn handle_scheme<'a>( _ => Pending, } } +#[derive(Debug)] enum PendingState { AwaitingStatusChange { waiter: ProcessId, @@ -901,13 +913,14 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut state: OccupiedEntry, + mut state_entry: OccupiedEntry, awoken: &mut VecDeque, ) -> Poll { - let req_id = *state.key(); - let mut state = state.get_mut(); - match core::mem::replace(state, PendingState::Placeholder) { - PendingState::Placeholder => unreachable!(), + let req_id = *state_entry.key(); + let mut state = state_entry.get_mut(); + let this_state = core::mem::replace(state, PendingState::Placeholder); + match this_state { + PendingState::Placeholder => return Pending, // unreachable!(), // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc) = self.processes.get_mut(¤t_pid) else { @@ -920,6 +933,9 @@ impl<'a> ProcScheme<'a> { ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), _ => return Response::ready_err(ESRCH, tag), // TODO? }; + // TODO: Properly remove state + state_entry.remove(); + proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent) = self.processes.get_mut(&ppid) { @@ -931,11 +947,13 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); + //let _ = syscall::write(1, alloc::format!("\nAWAKING WAITPID {:?}\n", parent.waitpid_waiting).as_bytes()); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } Ready(Response::new(Ok(0), tag)) } else { + let _ = syscall::write(1, b"\nWAITING AGAIN\n"); proc.awaiting_threads_term.push(req_id); *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending @@ -946,24 +964,28 @@ impl<'a> ProcScheme<'a> { target, flags, mut op, - } => match self.on_waitpid(waiter, target, flags, req_id) { - Ready(Ok((pid, status))) => { - if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { - *status_out = status; + } => { + let _ = syscall::write(1, b"\nWORKING ON AWAIT STS CHANGE\n"); + + match self.on_waitpid(waiter, target, flags, req_id) { + Ready(Ok((pid, status))) => { + if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { + *status_out = status; + } + Response::ready_ok(pid, op) + } + Ready(Err(e)) => Response::ready_err(e.errno, op), + Pending => { + *state = PendingState::AwaitingStatusChange { + waiter, + target, + flags, + op, + }; + Pending } - Response::ready_ok(pid, op) } - Ready(Err(e)) => Response::ready_err(e.errno, op), - Pending => { - *state = PendingState::AwaitingStatusChange { - waiter, - target, - flags, - op, - }; - Pending - } - }, + } } } fn debug(&self) { From fdbc00b0fc3af1f484e1596b87c2c931298eb564 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 15:43:22 +0100 Subject: [PATCH 21/77] Pass status in waitpid. --- src/procmgr.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 047694ae07..bd75e7885d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -2,6 +2,7 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; +use core::num::NonZeroU8; use core::task::Poll; use core::task::Poll::*; @@ -301,8 +302,14 @@ impl Eq for WaitpidKey {} enum ProcessStatus { PossiblyRunnable, Stopped(usize), - Exiting { signal: Option, status: u8 }, - Exited { signal: Option, status: u8 }, + Exiting { + signal: Option, + status: u8, + }, + Exited { + signal: Option, + status: u8, + }, } #[derive(Debug)] struct Thread { @@ -332,8 +339,13 @@ struct ProcScheme<'a> { #[derive(Clone, Copy, Debug)] enum WaitpidStatus { Continued, - Stopped { signal: u8 }, - Terminated { signal: Option, status: u8 }, + Stopped { + signal: NonZeroU8, + }, + Terminated { + signal: Option, + status: u8, + }, } #[derive(Debug)] @@ -732,24 +744,25 @@ impl<'a> ProcScheme<'a> { } }; let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { - let ret: i32 = todo!(); match status { WaitpidStatus::Continued => { // TODO: Handle None, i.e. restart everything until a match is found if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, ret)) + Ready((w_pid.0, 0xffff)) } else { Pending } } WaitpidStatus::Stopped { signal } => { if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, ret)) + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) } else { Pending } } - WaitpidStatus::Terminated { signal, status } => Ready((w_pid.0, ret)), + WaitpidStatus::Terminated { signal, status } => { + Ready((w_pid.0, signal.map_or(0, NonZeroU8::get).into())) + } } }; From e5a97a68c98c2837715413fcc7baee2d1fe48f24 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 16:05:09 +0100 Subject: [PATCH 22/77] Use log crate. --- Cargo.lock | 1 + Cargo.toml | 1 + src/exec.rs | 18 +++++++++++++++++ src/procmgr.rs | 52 +++++++++++++++++--------------------------------- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0c534e2fc..9848284378 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,7 @@ version = "0.0.0" dependencies = [ "hashbrown", "linked_list_allocator", + "log", "plain", "redox-initfs", "redox-path", diff --git a/Cargo.toml b/Cargo.toml index 2f6308d655..4b2536203d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ hashbrown = { version = "0.15", default-features = false, features = [ "default-hasher", ] } linked_list_allocator = "0.10" +log = { version = "0.4", default-features = false } plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } diff --git a/src/exec.rs b/src/exec.rs index 825e4003bf..a82b006ff3 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -6,6 +6,21 @@ use syscall::{Error, EINTR}; use redox_rt::proc::*; +struct Logger; + +impl log::Log for Logger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= log::max_level() + } + fn log(&self, record: &log::Record) { + let module = record.module_path().unwrap_or(""); + let level = record.level(); + let msg = record.args(); + let _ = syscall::write(1, alloc::format!("[{module} {level}] {msg}\n").as_bytes()); + } + fn flush(&self) {} +} + pub fn main() -> ! { let auth = FdGuard::new( syscall::open("/scheme/kernel.proc/authority", O_CLOEXEC) @@ -15,6 +30,9 @@ pub fn main() -> ! { FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; + log::set_max_level(log::LevelFilter::Trace); + let _ = log::set_logger(&Logger); + let envs = { let mut env = [0_u8; 4096]; diff --git a/src/procmgr.rs b/src/procmgr.rs index bd75e7885d..25639d7e8f 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -44,7 +44,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut scheme = ProcScheme::new(auth, &queue); - let _ = syscall::write(1, b"process manager started\n").unwrap(); + log::info!("process manager started"); let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); @@ -53,15 +53,14 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - let _ = syscall::write(1, alloc::format!("\n{awoken:#?}\n").as_bytes()); + log::trace!("{awoken:#?}"); while !awoken.is_empty() || !new_awoken.is_empty() { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { - //let _ = syscall::write(1, alloc::format!("\nALL STATES {states:#?}, AWOKEN {awoken:#?}\n").as_bytes()); + //log::trace!("ALL STATES {states:#?}, AWOKEN {awoken:#?}"); let Entry::Occupied(state) = states.entry(awoken) else { continue; }; - //let _ = syscall::write(1, alloc::format!("\nSTATE {state:#?}\n").as_bytes()); match scheme.work_on(state, &mut new_awoken) { Ready(resp) => { loop { @@ -111,10 +110,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } } else if let Some(thread) = scheme.thread_lookup.get(&event.data) { let Some(thread_rc) = thread.upgrade() else { - let _ = syscall::write( - 1, - alloc::format!("\nDEAD THREAD EVENT FROM {}\n", event.data).as_bytes(), - ); + log::trace!("DEAD THREAD EVENT FROM {}", event.data,); continue; }; let thread = thread_rc.borrow(); @@ -122,16 +118,12 @@ pub fn run(write_fd: usize, auth: &FdGuard) { // TODO? continue; }; - let _ = syscall::write( - 1, - alloc::format!("\nTHREAD EVENT FROM {}, {}, \n", event.data, thread.pid.0) - .as_bytes(), - ); + log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); let mut buf = 0_usize.to_ne_bytes(); let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); let status = usize::from_ne_bytes(buf); - let _ = syscall::write(1, alloc::format!("\nSTATUS {status}\n",).as_bytes()); + log::trace!("STATUS {status}"); if status != ContextStatus::Dead as usize { // spurious event @@ -139,13 +131,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); - let _ = syscall::write( - 1, - alloc::format!("\nAWAITING {}\n", proc.awaiting_threads_term.len()).as_bytes(), - ); + log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - let _ = syscall::write(1, b"\nTODO: UNKNOWN EVENT\n"); + log::debug!("TODO: UNKNOWN EVENT"); } } @@ -186,7 +175,7 @@ fn handle_scheme<'a>( awoken, ), _ => { - let _ = syscall::write(1, alloc::format!("\nUNKNOWN: {op:?}\n").as_bytes()); + log::trace!("UNKNOWN: {op:?}"); Ready(Response::new(Err(Error::new(ENOSYS)), op)) } } @@ -583,6 +572,7 @@ impl<'a> ProcScheme<'a> { match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { + log::trace!("Forking {pid:?}"); let child_pid = self.fork(pid)?; Ok(OpenResult::ThisScheme { number: self.handles.insert(Handle::Proc(child_pid)), @@ -702,7 +692,7 @@ impl<'a> ProcScheme<'a> { } } - let _ = syscall::write(1, b"\nEXIT PENDING\n"); + log::trace!("EXIT PENDING"); //self.debug(); // TODO: check? process.awaiting_threads_term.push(*state.key()); @@ -731,7 +721,7 @@ impl<'a> ProcScheme<'a> { } let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; - let _ = syscall::write(1, b"\nWAITPID\n"); + log::trace!("WAITPID"); let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| @@ -940,7 +930,7 @@ impl<'a> ProcScheme<'a> { return Response::ready_err(ESRCH, tag); }; if proc.threads.is_empty() { - let _ = syscall::write(1, b"\nWORKING ON AWAIT TERM\n"); + log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), @@ -960,13 +950,13 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); - //let _ = syscall::write(1, alloc::format!("\nAWAKING WAITPID {:?}\n", parent.waitpid_waiting).as_bytes()); + //log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } Ready(Response::new(Ok(0), tag)) } else { - let _ = syscall::write(1, b"\nWAITING AGAIN\n"); + log::trace!("WAITING AGAIN"); proc.awaiting_threads_term.push(req_id); *state = PendingState::AwaitingThreadsTermination(current_pid, tag); Pending @@ -978,7 +968,7 @@ impl<'a> ProcScheme<'a> { flags, mut op, } => { - let _ = syscall::write(1, b"\nWORKING ON AWAIT STS CHANGE\n"); + log::trace!("WORKING ON AWAIT STS CHANGE"); match self.on_waitpid(waiter, target, flags, req_id) { Ready(Ok((pid, status))) => { @@ -1002,13 +992,7 @@ impl<'a> ProcScheme<'a> { } } fn debug(&self) { - let _ = syscall::write( - 1, - alloc::format!("PROCESSES\n\n{:#?}\n\n", self.processes).as_bytes(), - ); - let _ = syscall::write( - 1, - alloc::format!("HANDLES\n\n{:#?}\n\n", self.handles).as_bytes(), - ); + log::trace!("PROCESSES\n{:#?}", self.processes,); + log::trace!("HANDLES\n{:#?}", self.handles,); } } From 80ed3c47d1e71dc5e9e29a302a7ba86fbb189452 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 16:13:47 +0100 Subject: [PATCH 23/77] Use inner loop to handle multiple reqs per event. --- src/exec.rs | 8 ++++++-- src/procmgr.rs | 51 +++++++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index a82b006ff3..bbb012eb2f 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -13,10 +13,14 @@ impl log::Log for Logger { metadata.level() <= log::max_level() } fn log(&self, record: &log::Record) { - let module = record.module_path().unwrap_or(""); + let file = record.file().unwrap_or(""); + let line = record.line().unwrap_or(0); let level = record.level(); let msg = record.args(); - let _ = syscall::write(1, alloc::format!("[{module} {level}] {msg}\n").as_bytes()); + let _ = syscall::write( + 1, + alloc::format!("[{file}:{line} {level}] {msg}\n").as_bytes(), + ); } fn flush(&self) {} } diff --git a/src/procmgr.rs b/src/procmgr.rs index 25639d7e8f..e4073d4a38 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -53,7 +53,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut new_awoken = VecDeque::new(); 'outer: loop { - log::trace!("{awoken:#?}"); + log::trace!("AWOKEN {awoken:#?}"); while !awoken.is_empty() || !new_awoken.is_empty() { awoken.append(&mut new_awoken); for awoken in awoken.drain(..) { @@ -82,29 +82,33 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let event = queue.next_event().expect("failed to get next event"); if event.data == socket_ident { - let req = loop { - match socket.next_request(SignalBehavior::Interrupt) { - Ok(None) => break 'outer, - Ok(Some(req)) => break req, - Err(e) if e.errno == EINTR => continue, - // spurious event - Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => continue 'outer, - Err(other) => { - panic!("bootstrap: failed to read scheme request from kernel: {other}") + 'reqs: loop { + let req = loop { + match socket.next_request(SignalBehavior::Interrupt) { + Ok(None) => break 'outer, + Ok(Some(req)) => break req, + Err(e) if e.errno == EINTR => continue, + // spurious event + Err(e) if e.errno == EWOULDBLOCK || e.errno == EAGAIN => break 'reqs, + Err(other) => { + panic!("bootstrap: failed to read scheme request from kernel: {other}") + } } - } - }; - let Ready(resp) = handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) - else { - continue 'outer; - }; - loop { - match socket.write_response(resp, SignalBehavior::Interrupt) { - Ok(false) => break 'outer, - Ok(_) => break, - Err(err) if err.errno == EINTR => continue, - Err(err) => { - panic!("bootstrap: failed to write scheme response to kernel: {err}") + }; + log::trace!("REQ{req:#?}"); + let Ready(resp) = + handle_scheme(req, &socket, &mut scheme, &mut states, &mut awoken) + else { + continue 'reqs; + }; + loop { + match socket.write_response(resp, SignalBehavior::Interrupt) { + Ok(false) => break 'outer, + Ok(_) => break, + Err(err) if err.errno == EINTR => continue, + Err(err) => { + panic!("bootstrap: failed to write scheme response to kernel: {err}") + } } } } @@ -569,6 +573,7 @@ impl<'a> ProcScheme<'a> { } } fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { + log::trace!("Dup request"); match self.handles[old_id] { Handle::Proc(pid) => match buf { b"fork" => { From 0a1e1b2f9c1ee2298c287a268c99885f4d4f4059 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 7 Mar 2025 23:15:43 +0100 Subject: [PATCH 24/77] Implement new-thread. --- src/procmgr.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e4073d4a38..082ee97ab7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -523,19 +523,40 @@ impl<'a> ProcScheme<'a> { Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { + // TODO: deduplicate code with fork let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; - let fd: FdGuard = todo!(); - let status_hndl = todo!(); - let ident = *fd; + let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + + let attr_fd = FdGuard::new(syscall::dup( + *ctxt_fd, + alloc::format!("attrs-{}", **self.auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: pid.0 as u32, + euid: proc.euid, + egid: proc.egid, + ens: proc.ens, + }, + )?; + + let status_hndl = FdGuard::new(syscall::dup(*ctxt_fd, b"status")?); + + self.queue + .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) + .expect("TODO"); + + let ident = *ctxt_fd; let thread = Rc::new(RefCell::new(Thread { - fd, + fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), status_hndl, pid, })); let thread_weak = Rc::downgrade(&thread); proc.threads.push(thread); self.thread_lookup.insert(ident, thread_weak); - Ok(fd) + Ok(ctxt_fd) } fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { From 443c49f6c5a40cdd0a6def14b356122cd0ec3777 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 00:53:13 +0100 Subject: [PATCH 25/77] Implement most of getpgid and setpgid. --- src/procmgr.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 082ee97ab7..152855f1bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -2,7 +2,7 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::hash::BuildHasherDefault; use core::mem::size_of; -use core::num::NonZeroU8; +use core::num::{NonZeroU8, NonZeroUsize}; use core::task::Poll; use core::task::Poll::*; @@ -682,10 +682,83 @@ impl<'a> ProcScheme<'a> { }); self.work_on(state, awoken) } + ProcCall::SetResugid => { + log::error!("SETRESGUID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Setpgid => { + let target_pid = NonZeroUsize::new(metadata[1] as usize) + .map_or(fd_pid, |n| ProcessId(n.get())); + + let new_pgid = NonZeroUsize::new(metadata[2] as usize) + .map_or(target_pid, |n| ProcessId(n.get())); + if new_pgid.0 == usize::wrapping_neg(1) { + Ready(Response::new( + self.on_getpgid(fd_pid, target_pid).map(|ProcessId(p)| p), + op, + )) + } else { + Ready(Response::new( + self.on_setpgid(fd_pid, target_pid, new_pgid).map(|()| 0), + op, + )) + } + } + ProcCall::Getsid => { + log::error!("GETSID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Setsid => { + log::error!("SETSID STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::SetResugid => Ready(Response::new( + self.on_setresugid(fd_pid, payload).map(|()| 0), + op, + )), } } } } + pub fn on_getpgid( + &mut self, + caller_pid: ProcessId, + target_pid: ProcessId, + ) -> Result { + let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + + // Although not required, POSIX allows the impl to forbid getting the pgid of processes + // outside of the caller's session. + if caller_proc.sid != target_proc.sid && caller_proc.euid != 0 { + return Err(Error::new(EPERM)); + } + + Ok(target_proc.pgid) + } + pub fn on_setpgid( + &mut self, + caller_pid: ProcessId, + target_pid: ProcessId, + new_pgid: ProcessId, + ) -> Result<()> { + let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + + let proc = self + .processes + .get_mut(&target_pid) + .ok_or(Error::new(ESRCH))?; + + // Session leaders cannot have their pgid changed. + if proc.sid == target_pid { + return Err(Error::new(EPERM)); + } + + // TODO: other security checks + + proc.pgid = new_pgid; + Ok(()) + } pub fn on_exit_start( &mut self, pid: ProcessId, @@ -853,6 +926,19 @@ impl<'a> ProcScheme<'a> { } } } + pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { + log::info!("ON_SETRESUGID {pid:?} {raw_buf:?}"); + let ids = { + let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) + .unwrap() + .try_into() + .map_err(|_| Error::new(EINVAL))?; + raw_ids.map(|i| if i == u32::MAX { None } else { Some(i) }) + }; + let proc = self.processes.get_mut(&pid).ok_or(Error::new(ESRCH))?; + log::warn!("TODO: on_setresugid({pid:?}): {ids:?}"); + Ok(()) + } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { cur: Option, From fd3a9f9a947366d265cd381b3f43fa763641e0da Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 01:13:38 +0100 Subject: [PATCH 26/77] Add stub for kill and sigq. --- src/exec.rs | 2 +- src/procmgr.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/exec.rs b/src/exec.rs index bbb012eb2f..12e720e140 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -34,7 +34,7 @@ pub fn main() -> ! { FdGuard::new(syscall::dup(*auth, b"cur-context").expect("failed to open open_via_dup")); let this_thr_fd = unsafe { redox_rt::initialize_freestanding(this_thr_fd) }; - log::set_max_level(log::LevelFilter::Trace); + log::set_max_level(log::LevelFilter::Debug); let _ = log::set_logger(&Logger); let envs = { diff --git a/src/procmgr.rs b/src/procmgr.rs index 152855f1bf..84142fa34d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -716,6 +716,14 @@ impl<'a> ProcScheme<'a> { self.on_setresugid(fd_pid, payload).map(|()| 0), op, )), + ProcCall::Kill => { + log::error!("KILL STUB"); + Ready(Response::ok(0, op)) + } + ProcCall::Sigq => { + log::error!("SIGQ STUB"); + Ready(Response::ok(0, op)) + } } } } From 19cb1c05129c740150b90d9b44ae668180bc4b23 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 13:09:40 +0200 Subject: [PATCH 27/77] WIP: start implementing kill. --- src/lib.rs | 2 +- src/procmgr.rs | 399 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 379 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 515955489b..4b76efcf38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![allow(internal_features)] -#![feature(asm_const, core_intrinsics, str_from_raw_parts, iter_intersperse)] +#![feature(core_intrinsics, let_chains, iter_intersperse, str_from_raw_parts)] #[cfg(target_arch = "aarch64")] #[path = "aarch64.rs"] diff --git a/src/procmgr.rs b/src/procmgr.rs index 84142fa34d..a5f8d83cf1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1,8 +1,10 @@ use core::cell::RefCell; -use core::cmp::Ordering; +use core::cmp; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; +use core::ptr::NonNull; +use core::sync::atomic::Ordering; use core::task::Poll; use core::task::Poll::*; @@ -16,7 +18,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcMeta, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -25,9 +27,10 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - ContextStatus, Error, Event, EventFlags, FobtainFdFlags, ProcSchemeAttrs, Result, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, - EWOULDBLOCK, O_CLOEXEC, O_CREAT, + sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, + Result, RtSigInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, + EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, + PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -211,6 +214,39 @@ impl IntoTag for PendingState { } } +#[derive(Debug)] +pub struct Page { + ptr: NonNull, +} +impl Page { + pub fn map(fd: &FdGuard) -> Result { + Ok(Self { + ptr: NonNull::new(unsafe { + syscall::fmap( + **fd, + &syscall::Map { + offset: 0, + size: PAGE_SIZE, + flags: MapFlags::PROT_READ, + address: 0, + }, + )? as *mut T + }) + .unwrap(), + }) + } + pub fn get(&self) -> &T { + unsafe { self.ptr.as_ref() } + } +} +impl Drop for Page { + fn drop(&mut self) { + unsafe { + let _ = syscall::funmap(self.ptr.as_ptr() as usize, PAGE_SIZE); + } + } +} + #[derive(Debug)] struct Process { threads: Vec>>, @@ -231,6 +267,8 @@ struct Process { waitpid: BTreeMap, waitpid_waiting: VecDeque, + + pctl: Option>, } #[derive(Copy, Clone, Debug)] pub struct WaitpidKey { @@ -240,7 +278,7 @@ pub struct WaitpidKey { // TODO: Is this valid? (transitive?) impl Ord for WaitpidKey { - fn cmp(&self, other: &WaitpidKey) -> Ordering { + fn cmp(&self, other: &WaitpidKey) -> cmp::Ordering { // If both have pid set, compare that if let Some(s_pid) = self.pid { if let Some(o_pid) = other.pid { @@ -257,36 +295,36 @@ impl Ord for WaitpidKey { // If either has pid set, it is greater if self.pid.is_some() { - return Ordering::Greater; + return cmp::Ordering::Greater; } if other.pid.is_some() { - return Ordering::Less; + return cmp::Ordering::Less; } // If either has pgid set, it is greater if self.pgid.is_some() { - return Ordering::Greater; + return cmp::Ordering::Greater; } if other.pgid.is_some() { - return Ordering::Less; + return cmp::Ordering::Less; } // If all pid and pgid are None, they are equal - Ordering::Equal + cmp::Ordering::Equal } } impl PartialOrd for WaitpidKey { - fn partial_cmp(&self, other: &WaitpidKey) -> Option { + fn partial_cmp(&self, other: &WaitpidKey) -> Option { Some(self.cmp(other)) } } impl PartialEq for WaitpidKey { fn eq(&self, other: &WaitpidKey) -> bool { - self.cmp(other) == Ordering::Equal + self.cmp(other) == cmp::Ordering::Equal } } @@ -309,7 +347,7 @@ struct Thread { fd: FdGuard, status_hndl: FdGuard, pid: ProcessId, - // sig_ctrl: MmapGuard<...> + sig_ctrl: Option>, } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] struct ProcessId(usize); @@ -423,6 +461,7 @@ impl<'a> ProcScheme<'a> { fd, status_hndl, pid: INIT_PID, + sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); self.processes.insert( @@ -443,6 +482,8 @@ impl<'a> ProcScheme<'a> { awaiting_threads_term: Vec::new(), waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), + + pctl: None, }, ); self.sessions.insert(INIT_PID); @@ -495,6 +536,7 @@ impl<'a> ProcScheme<'a> { fd: new_ctxt_fd, status_hndl: status_fd, pid: child_pid, + sig_ctrl: None, // TODO })); let thread_weak = Rc::downgrade(&thread); @@ -517,6 +559,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), + + pctl: None, // TODO }, ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -552,6 +596,7 @@ impl<'a> ProcScheme<'a> { fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), status_hndl, pid, + sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); proc.threads.push(thread); @@ -716,13 +761,33 @@ impl<'a> ProcScheme<'a> { self.on_setresugid(fd_pid, payload).map(|()| 0), op, )), - ProcCall::Kill => { - log::error!("KILL STUB"); - Ready(Response::ok(0, op)) - } - ProcCall::Sigq => { - log::error!("SIGQ STUB"); - Ready(Response::ok(0, op)) + ProcCall::Kill | ProcCall::Sigq => { + let (payload, metadata) = op.payload_and_metadata(); + let target = ProcKillTarget::from_raw(metadata[1] as usize); + let Some(signal) = u8::try_from(metadata[2]).ok().filter(|s| *s <= 64) + else { + return Response::ready_err(EINVAL, op); + }; + let mut killed_self = false; + + let mode = match verb { + ProcCall::Kill => KillMode::Idempotent, + ProcCall::Sigq => KillMode::Queued({ + let mut buf = RtSigInfo::default(); + if payload.len() != buf.len() { + return Response::ready_err(EINVAL, op); + } + buf.copy_from_slice(payload); + buf + }), + _ => unreachable!(), + }; + + let is_sigchld_to_parent = false; + Ready(Response::new( + self.on_kill(fd_pid, target, signal, mode).map(|()| 0), + op, + )) } } } @@ -1115,4 +1180,296 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } + pub fn on_kill( + &mut self, + caller_pid: ProcessId, + target: ProcKillTarget, + signal: u8, + mode: KillMode, + ) -> Result<()> { + let mut num_succeeded = 0; + + match target { + ProcKillTarget::SingleProc(proc) => (), + ProcKillTarget::ProcGroup(grp) => { + for (pid, proc) in self.processes.iter().filter(|(_, p)| p.pgid == grp) { + self.on_send_sig(caller_pid); + } + } + ProcKillTarget::All => { + for (pid, proc) in self.processes.iter() { + self.on_send_sig(caller_pid); + } + } + } + + Ok(()) + } + pub fn on_send_sig( + &mut self, + caller_pid: ProcessId, + target: KillTarget, + signal: u8, + killed_self: &mut bool, + mode: KillMode, + is_sigchld_to_parent: bool, + ) -> Result<()> { + debug_assert!(sig <= 64); + + let sig = usize::from(signal); + let sig_group = (sig - 1) / 32; + let sig_idx = sig - 1; + + /*let (context_lock, process_lock) = match target { + KillTarget::Thread(ref c) => (Arc::clone(&c), Arc::clone(&c.read().process)), + KillTarget::Process(ref p) => ( + p.read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + .next() + .ok_or(Error::new(ESRCH))?, + Arc::clone(p), + ), + };*/ + let proc_info = process_lock.read().info; + + enum SendResult { + Succeeded, + SucceededSigchld { orig_signal: usize }, + SucceededSigcont { ppid: ProcessId, pgid: ProcessId }, + FullQ, + Invalid, + } + + let result = (|| { + let is_self = context::is_current(&context_lock); + + // If sig = 0, test that process exists and can be signalled, but don't send any + // signal. + if sig == 0 { + return SendResult::Succeeded; + } + + let mut process_guard = process_lock.write(); + + if sig == SIGCONT + && let ProcessStatus::Stopped(_sig) = process_guard.status + { + // Convert stopped processes to blocked if sending SIGCONT, regardless of whether + // SIGCONT is blocked or ignored. It can however be controlled whether the process + // will additionally ignore, defer, or handle that signal. + process_guard.status = ProcessStatus::PossiblyRunnable; + drop(process_guard); + + let mut context_guard = context_lock.write(); + if let Some((_, pctl, _)) = context_guard.sigcontrol() { + if !pctl.signal_will_ign(SIGCONT, false) { + pctl.pending.fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); + } + drop(context_guard); + + // TODO: which threads should become Runnable? + for thread in process_lock + .read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + { + let mut thread = thread.write(); + if let Some((tctl, _, _)) = thread.sigcontrol() { + tctl.word[0].fetch_and( + !(sig_bit(SIGSTOP) + | sig_bit(SIGTTIN) + | sig_bit(SIGTTOU) + | sig_bit(SIGTSTP)), + Ordering::Relaxed, + ); + } + thread.unblock(); + } + } + // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. + return SendResult::SucceededSigcont {}; + } + drop(process_guard); + let mut context_guard = context_lock.write(); + if sig == SIGSTOP + || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) + && context_guard + .sigcontrol() + .map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) + { + todo!("tell kernel to stop process"); + /* + context_guard.status = context::Status::Blocked; + drop(context_guard); + process_lock.write().status = ProcessStatus::Stopped(sig); + */ + + // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise. + let mut already = false; + for thread in process_lock + .read() + .threads + .iter() + .filter_map(|t| t.upgrade()) + { + let mut thread = thread.write(); + if let Some((tctl, pctl, _)) = thread.sigcontrol() { + if !already { + pctl.pending.fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + already = true; + } + tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + } + } + + return SendResult::SucceededSigchld { orig_signal: sig }; + } + if sig == SIGKILL { + todo!("tell kernel to kill context"); + /* + context_guard.being_sigkilled = true; + context_guard.unblock(); + drop(context_guard); + */ + *killed_self |= is_self; + + // exit() will signal the parent, rather than immediately in kill() + return SendResult::Succeeded; + } + if let Some((tctl, pctl, sigst)) = context_guard.sigcontrol() + && !pctl.signal_will_ign(sig, is_sigchld_to_parent) + { + match target { + KillTarget::Thread => { + tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + + let _was_new = + tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 + { + context_guard.unblock(); + *killed_self |= is_self; + } + } + KillTarget::Proc(proc) => { + match mode { + KillMode::Queued(arg) => { + if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + return SendResult::Invalid; + } + let rtidx = sig_idx - 32; + //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); + if rtidx >= sigst.rtqs.len() { + sigst.rtqs.resize_with(rtidx + 1, VecDeque::new); + } + let rtq = sigst.rtqs.get_mut(rtidx).unwrap(); + + // TODO: configurable limit? + if rtq.len() > 32 { + return SendResult::FullQ; + } + + rtq.push_back(arg); + } + KillMode::Idempotent => { + if pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { + // If already pending, do not send this signal. While possible that + // another thread is concurrently clearing pending, and that other + // spuriously awoken threads would benefit from actually receiving + // this signal, there is no requirement by POSIX for such signals + // not to be mergeable. So unless the signal handler is observed to + // happen-before this syscall, it can be ignored. The pending bits + // would certainly have been cleared, thus contradicting this + // already reached statement. + return SendResult::Succeeded; + } + + if sig_group != 0 { + return SendResult::Invalid; + } + pctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + } + } + + pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); + drop(context_guard); + + for thread in proc.read().threads.iter().filter_map(|t| t.upgrade()) { + let mut thread = thread.write(); + let Some((tctl, _, _)) = thread.sigcontrol() else { + continue; + }; + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) + != 0 + { + thread.unblock(); + *killed_self |= is_self; + break; + } + } + } + } + SendResult::Succeeded + } else { + // Discard signals if sighandler is unset. This includes both special contexts such + // as bootstrap, and child processes or threads that have not yet been started. + // This is semantically equivalent to having all signals except SIGSTOP and SIGKILL + // blocked/ignored (SIGCONT can be ignored and masked, but will always continue + // stopped processes first). + SendResult::Succeeded + } + })(); + + match result { + SendResult::Succeeded => (), + SendResult::FullQ => return Err(Error::new(EAGAIN)), + SendResult::Invalid => return Err(Error::new(EINVAL)), + SendResult::SucceededSigchld { + ppid, + pgid, + orig_signal, + } => {} + SendResult::SucceededSigcont { ppid, pgid } => { + // POSIX XSI allows but does not require SIGCONT to send signals to the parent. + //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; + } + } + + Ok(()) + } } +#[derive(Clone, Copy)] +pub enum KillMode { + Idempotent, + Queued(RtSigInfo), +} +pub enum KillTarget { + Proc(ProcessId), + Thread(Rc>), +} +/* +pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { + let Some((_tctl, pctl, st)) = current.sigcontrol() else { + return Err(Error::new(ESRCH)); + }; + if sig_idx >= 32 { + return Err(Error::new(EINVAL)); + } + let q = st + .rtqs + .get_mut(sig_idx as usize) + .ok_or(Error::new(EAGAIN))?; + let Some(front) = q.pop_front() else { + return Err(Error::new(EAGAIN)); + }; + if q.is_empty() { + pctl.pending + .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); + } + out.copy_exactly(&front)?; + Ok(()) +} +*/ From a7df1a1734437df290f9e4bd90d6a15ea59e4650 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 13:55:27 +0200 Subject: [PATCH 28/77] progress. --- src/procmgr.rs | 83 +++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index a5f8d83cf1..d868f4b93c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1189,24 +1189,47 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { let mut num_succeeded = 0; - match target { - ProcKillTarget::SingleProc(proc) => (), - ProcKillTarget::ProcGroup(grp) => { - for (pid, proc) in self.processes.iter().filter(|(_, p)| p.pgid == grp) { - self.on_send_sig(caller_pid); - } + let mut killed_self = false; // TODO + let is_sigchld_to_parent = false; // TODO + + let match_grp = match target { + ProcKillTarget::SingleProc(pid) => { + return self.on_send_sig( + caller_pid, + KillTarget::Proc(ProcessId(pid)), + signal, + &mut killed_self, + mode, + is_sigchld_to_parent, + ) } - ProcKillTarget::All => { - for (pid, proc) in self.processes.iter() { - self.on_send_sig(caller_pid); - } + ProcKillTarget::All => None, + ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), + }; + + for (pid, proc) in self.processes.iter() { + if match_grp.map_or(false, |g| proc.pgid != g) { + continue; + } + let res = self.on_send_sig( + caller_pid, + KillTarget::Proc(*pid), + signal, + &mut killed_self, + mode, + is_sigchld_to_parent, + ); + match res { + Ok(()) => (), + Err(err) if num_succeeded > 0 => break, + Err(err) => return Err(err), } } Ok(()) } pub fn on_send_sig( - &mut self, + &self, caller_pid: ProcessId, target: KillTarget, signal: u8, @@ -1214,36 +1237,36 @@ impl<'a> ProcScheme<'a> { mode: KillMode, is_sigchld_to_parent: bool, ) -> Result<()> { - debug_assert!(sig <= 64); - let sig = usize::from(signal); + debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; let sig_idx = sig - 1; - /*let (context_lock, process_lock) = match target { - KillTarget::Thread(ref c) => (Arc::clone(&c), Arc::clone(&c.read().process)), - KillTarget::Process(ref p) => ( - p.read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - .next() - .ok_or(Error::new(ESRCH))?, - Arc::clone(p), - ), - };*/ - let proc_info = process_lock.read().info; + let target_pid = match target { + KillTarget::Proc(pid) => pid, + KillTarget::Thread(thread) => thread.borrow().pid, + }; + let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; enum SendResult { Succeeded, - SucceededSigchld { orig_signal: usize }, - SucceededSigcont { ppid: ProcessId, pgid: ProcessId }, + SucceededSigchld { + orig_signal: usize, + ppid: ProcessId, + pgid: ProcessId, + }, + SucceededSigcont { + ppid: ProcessId, + pgid: ProcessId, + }, FullQ, Invalid, } let result = (|| { - let is_self = context::is_current(&context_lock); + // FIXME + let is_self = false; + //let is_self = context::is_current(&context_lock); // If sig = 0, test that process exists and can be signalled, but don't send any // signal. @@ -1251,8 +1274,6 @@ impl<'a> ProcScheme<'a> { return SendResult::Succeeded; } - let mut process_guard = process_lock.write(); - if sig == SIGCONT && let ProcessStatus::Stopped(_sig) = process_guard.status { From fbbb27b0e58d616c885f46f2cb417ceec04f1f1d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:13:35 +0200 Subject: [PATCH 29/77] Tmp disable kill and wrap Process in Rc. --- src/procmgr.rs | 105 ++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d868f4b93c..b3f4d68600 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -121,10 +121,11 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let thread = thread_rc.borrow(); - let Some(proc) = scheme.processes.get_mut(&thread.pid) else { + let Some(proc_rc) = scheme.processes.get(&thread.pid) else { // TODO? continue; }; + let mut proc = proc_rc.borrow_mut(); log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); let mut buf = 0_usize.to_ne_bytes(); let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); @@ -355,7 +356,7 @@ struct ProcessId(usize); const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { - processes: HashMap, + processes: HashMap>, DefaultHashBuilder>, sessions: HashSet, handles: Slab, @@ -466,7 +467,7 @@ impl<'a> ProcScheme<'a> { let thread_weak = Rc::downgrade(&thread); self.processes.insert( INIT_PID, - Process { + Rc::new(RefCell::new(Process { threads: vec![thread], ppid: INIT_PID, sid: INIT_PID, @@ -484,7 +485,7 @@ impl<'a> ProcScheme<'a> { waitpid_waiting: VecDeque::new(), pctl: None, - }, + })), ); self.sessions.insert(INIT_PID); @@ -499,6 +500,8 @@ impl<'a> ProcScheme<'a> { fn fork(&mut self, parent_pid: ProcessId) -> Result { let child_pid = self.new_id(); + let proc_guard = self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + let Process { pgid, sid, @@ -509,7 +512,7 @@ impl<'a> ProcScheme<'a> { ens, rns, .. - } = *self.processes.get(&parent_pid).ok_or(Error::new(EBADFD))?; + } = *proc_guard.borrow(); let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( @@ -542,7 +545,7 @@ impl<'a> ProcScheme<'a> { self.processes.insert( child_pid, - Process { + Rc::new(RefCell::new(Process { threads: vec![thread], ppid: parent_pid, pgid, @@ -561,14 +564,16 @@ impl<'a> ProcScheme<'a> { waitpid_waiting: VecDeque::new(), pctl: None, // TODO - }, + })), ); self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } fn new_thread(&mut self, pid: ProcessId) -> Result { // TODO: deduplicate code with fork - let proc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let mut proc = proc_rc.borrow_mut(); + let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( @@ -618,7 +623,8 @@ impl<'a> ProcScheme<'a> { fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { - let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let process = proc_rc.borrow(); let metadata = ProcMeta { pid: pid.0 as u32, pgid: process.pgid.0 as u32, @@ -658,7 +664,7 @@ impl<'a> ProcScheme<'a> { .ok() .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; - let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); return Ok(OpenResult::OtherScheme { @@ -798,8 +804,16 @@ impl<'a> ProcScheme<'a> { caller_pid: ProcessId, target_pid: ProcessId, ) -> Result { - let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; - let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + let caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + let target_proc = self + .processes + .get(&target_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); // Although not required, POSIX allows the impl to forbid getting the pgid of processes // outside of the caller's session. @@ -817,10 +831,8 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; - let proc = self - .processes - .get_mut(&target_pid) - .ok_or(Error::new(ESRCH))?; + let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + let mut proc = proc_rc.borrow_mut(); // Session leaders cannot have their pgid changed. if proc.sid == target_pid { @@ -840,9 +852,12 @@ impl<'a> ProcScheme<'a> { awoken: &mut VecDeque, tag: Tag, ) -> Poll { - let Some(process) = self.processes.get_mut(&pid) else { + let Some(proc_rc) = self.processes.get(&pid) else { return Response::ready_err(EBADFD, tag); }; + let mut process_guard = proc_rc.borrow_mut(); + let process = &mut *process_guard; + match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, @@ -869,6 +884,7 @@ impl<'a> ProcScheme<'a> { // TODO: check? process.awaiting_threads_term.push(*state.key()); } + drop(process_guard); self.work_on( state.insert_entry(PendingState::AwaitingThreadsTermination(pid, tag)), awoken, @@ -887,12 +903,15 @@ impl<'a> ProcScheme<'a> { ) { // Check for existence of child. // TODO: inefficient, keep refcount? - if !self.processes.values().any(|p| p.ppid == this_pid) { + if !self.processes.values().any(|p| p.borrow().ppid == this_pid) { return Ready(Err(Error::new(ECHILD))); } } - let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; + let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; + let mut proc_guard = proc_rc.borrow_mut(); + let proc = &mut *proc_guard; + log::trace!("WAITPID"); let recv_nonblock = |waitpid: &mut BTreeMap, @@ -947,11 +966,9 @@ impl<'a> ProcScheme<'a> { if this_pid == pid { return Ready(Err(Error::new(EINVAL))); } - let [Some(proc), Some(target_proc)] = - self.processes.get_many_mut([&this_pid, &pid]) - else { - return Ready(Err(Error::new(ESRCH))); - }; + let target_proc_rc = self.processes.get(&pid).ok_or(Error::new(ESRCH))?; + let mut target_proc = target_proc_rc.borrow_mut(); + if target_proc.ppid != this_pid { return Ready(Err(Error::new(ECHILD))); } @@ -976,13 +993,14 @@ impl<'a> ProcScheme<'a> { } WaitpidTarget::ProcGroup(pgid) => { let this_pgid = proc.pgid; - if !self.processes.values().any(|p| p.pgid == this_pgid) { + if !self + .processes + .values() + .any(|p| p.borrow().pgid == this_pgid) + { return Ready(Err(Error::new(ECHILD))); } - // reborrow proc - let proc = self.processes.get_mut(&this_pid).ok_or(Error::new(ESRCH))?; - let key = WaitpidKey { pid: None, pgid: Some(pgid), @@ -1015,15 +1033,16 @@ impl<'a> ProcScheme<'a> { fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { struct Iter<'a> { cur: Option, - procs: &'a HashMap, + procs: &'a HashMap>, DefaultHashBuilder>, } impl Iterator for Iter<'_> { type Item = ProcessId; fn next(&mut self) -> Option { let proc = self.procs.get(&self.cur?)?; - self.cur = Some(proc.ppid); - Some(proc.ppid) + let ppid = proc.borrow().ppid; + self.cur = Some(ppid); + Some(ppid) } } Iter { @@ -1043,7 +1062,9 @@ impl<'a> ProcScheme<'a> { todo!() } pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { - let process = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; + let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; + let mut process = proc_rc.borrow_mut(); + let setrns = if rns.is_none() { // Ignore RNS if -1 is passed false @@ -1111,9 +1132,12 @@ impl<'a> ProcScheme<'a> { PendingState::Placeholder => return Pending, // unreachable!(), // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { - let Some(proc) = self.processes.get_mut(¤t_pid) else { + let Some(proc_rc) = self.processes.get(¤t_pid) else { return Response::ready_err(ESRCH, tag); }; + let mut proc_guard = proc_rc.borrow_mut(); + let proc = &mut *proc_guard; + if proc.threads.is_empty() { log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { @@ -1126,7 +1150,8 @@ impl<'a> ProcScheme<'a> { proc.status = ProcessStatus::Exited { signal, status }; let (ppid, pgid) = (proc.ppid, proc.pgid); - if let Some(parent) = self.processes.get_mut(&ppid) { + if let Some(parent_rc) = self.processes.get(&ppid) { + let mut parent = parent_rc.borrow_mut(); // TODO: transfer children to parent, and all of self.waitpid parent.waitpid.insert( WaitpidKey { @@ -1207,8 +1232,8 @@ impl<'a> ProcScheme<'a> { ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), }; - for (pid, proc) in self.processes.iter() { - if match_grp.map_or(false, |g| proc.pgid != g) { + for (pid, proc_rc) in self.processes.iter() { + if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { continue; } let res = self.on_send_sig( @@ -1263,6 +1288,7 @@ impl<'a> ProcScheme<'a> { Invalid, } + /* let result = (|| { // FIXME let is_self = false; @@ -1275,13 +1301,12 @@ impl<'a> ProcScheme<'a> { } if sig == SIGCONT - && let ProcessStatus::Stopped(_sig) = process_guard.status + && let ProcessStatus::Stopped(_sig) = target_proc.status { // Convert stopped processes to blocked if sending SIGCONT, regardless of whether // SIGCONT is blocked or ignored. It can however be controlled whether the process // will additionally ignore, defer, or handle that signal. - process_guard.status = ProcessStatus::PossiblyRunnable; - drop(process_guard); + target_proc.status = ProcessStatus::PossiblyRunnable; let mut context_guard = context_lock.write(); if let Some((_, pctl, _)) = context_guard.sigcontrol() { @@ -1457,7 +1482,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; } - } + }*/ Ok(()) } From a0fc7febfc051b5b2bae4d19e2feb824223f3050 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:32:15 +0200 Subject: [PATCH 30/77] Make send_signal code compile. --- src/procmgr.rs | 164 +++++++++++++++++++++++++++---------------------- 1 file changed, 90 insertions(+), 74 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b3f4d68600..e4a0f48c73 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -3,6 +3,7 @@ use core::cmp; use core::hash::BuildHasherDefault; use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; +use core::ops::Deref; use core::ptr::NonNull; use core::sync::atomic::Ordering; use core::task::Poll; @@ -28,9 +29,9 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, - EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, - PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + Result, RtSigInfo, SenderInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, + EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, + O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -236,7 +237,11 @@ impl Page { .unwrap(), }) } - pub fn get(&self) -> &T { +} +impl Deref for Page { + type Target = T; + + fn deref(&self) -> &T { unsafe { self.ptr.as_ref() } } } @@ -269,7 +274,8 @@ struct Process { waitpid: BTreeMap, waitpid_waiting: VecDeque, - pctl: Option>, + sig_pctl: Option>, + rtqs: Vec>, } #[derive(Copy, Clone, Debug)] pub struct WaitpidKey { @@ -484,7 +490,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), - pctl: None, + sig_pctl: None, + rtqs: Vec::new(), })), ); self.sessions.insert(INIT_PID); @@ -563,7 +570,8 @@ impl<'a> ProcScheme<'a> { waitpid: BTreeMap::new(), waitpid_waiting: VecDeque::new(), - pctl: None, // TODO + sig_pctl: None, // TODO + rtqs: Vec::new(), })), ); self.thread_lookup.insert(thread_ident, thread_weak); @@ -1269,9 +1277,14 @@ impl<'a> ProcScheme<'a> { let target_pid = match target { KillTarget::Proc(pid) => pid, - KillTarget::Thread(thread) => thread.borrow().pid, + KillTarget::Thread(ref thread) => thread.borrow().pid, + }; + let target_proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; + + let sender = SenderInfo { + pid: caller_pid.0 as u32, + ruid: 0, // TODO }; - let target_proc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; enum SendResult { Succeeded, @@ -1288,7 +1301,6 @@ impl<'a> ProcScheme<'a> { Invalid, } - /* let result = (|| { // FIXME let is_self = false; @@ -1299,6 +1311,12 @@ impl<'a> ProcScheme<'a> { if sig == 0 { return SendResult::Succeeded; } + let mut target_proc = target_proc_rc.borrow_mut(); + let target_proc = &mut *target_proc; + + let Some(ref sig_pctl) = target_proc.sig_pctl else { + return SendResult::Invalid; + }; if sig == SIGCONT && let ProcessStatus::Stopped(_sig) = target_proc.status @@ -1308,43 +1326,39 @@ impl<'a> ProcScheme<'a> { // will additionally ignore, defer, or handle that signal. target_proc.status = ProcessStatus::PossiblyRunnable; - let mut context_guard = context_lock.write(); - if let Some((_, pctl, _)) = context_guard.sigcontrol() { - if !pctl.signal_will_ign(SIGCONT, false) { - pctl.pending.fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); - } - drop(context_guard); + if !sig_pctl.signal_will_ign(SIGCONT, false) { + sig_pctl + .pending + .fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); + } - // TODO: which threads should become Runnable? - for thread in process_lock - .read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - { - let mut thread = thread.write(); - if let Some((tctl, _, _)) = thread.sigcontrol() { - tctl.word[0].fetch_and( - !(sig_bit(SIGSTOP) - | sig_bit(SIGTTIN) - | sig_bit(SIGTTOU) - | sig_bit(SIGTSTP)), - Ordering::Relaxed, - ); - } - thread.unblock(); + // TODO: which threads should become Runnable? + for thread_rc in target_proc.threads.iter() { + let mut thread = thread_rc.borrow_mut(); + if let Some(ref tctl) = thread.sig_ctrl { + tctl.word[0].fetch_and( + !(sig_bit(SIGSTOP) + | sig_bit(SIGTTIN) + | sig_bit(SIGTTOU) + | sig_bit(SIGTSTP)), + Ordering::Relaxed, + ); } + // TODO + //thread.unblock(); } // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. - return SendResult::SucceededSigcont {}; + return SendResult::SucceededSigcont { + ppid: target_proc.ppid, + pgid: target_proc.pgid, + }; } - drop(process_guard); - let mut context_guard = context_lock.write(); if sig == SIGSTOP || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) - && context_guard - .sigcontrol() - .map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) + && target_proc + .sig_pctl + .as_ref() + .map_or(false, |proc| proc.signal_will_stop(sig))) { todo!("tell kernel to stop process"); /* @@ -1353,25 +1367,23 @@ impl<'a> ProcScheme<'a> { process_lock.write().status = ProcessStatus::Stopped(sig); */ - // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise. - let mut already = false; - for thread in process_lock - .read() - .threads - .iter() - .filter_map(|t| t.upgrade()) - { - let mut thread = thread.write(); - if let Some((tctl, pctl, _)) = thread.sigcontrol() { - if !already { - pctl.pending.fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - already = true; - } + // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise? + sig_pctl + .pending + .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + + for thread in target_proc.threads.iter() { + let thread = thread.borrow(); + if let Some(ref tctl) = thread.sig_ctrl { tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); } } - return SendResult::SucceededSigchld { orig_signal: sig }; + return SendResult::SucceededSigchld { + orig_signal: sig, + ppid: target_proc.ppid, + pgid: target_proc.pgid, + }; } if sig == SIGKILL { todo!("tell kernel to kill context"); @@ -1385,18 +1397,21 @@ impl<'a> ProcScheme<'a> { // exit() will signal the parent, rather than immediately in kill() return SendResult::Succeeded; } - if let Some((tctl, pctl, sigst)) = context_guard.sigcontrol() - && !pctl.signal_will_ign(sig, is_sigchld_to_parent) - { + if !sig_pctl.signal_will_ign(sig, is_sigchld_to_parent) { match target { - KillTarget::Thread => { + KillTarget::Thread(ref thread_rc) => { + let thread = thread_rc.borrow(); + let Some(ref tctl) = thread.sig_ctrl else { + return SendResult::Invalid; + }; + tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); let _was_new = tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - context_guard.unblock(); + //context_guard.unblock(); *killed_self |= is_self; } } @@ -1408,10 +1423,10 @@ impl<'a> ProcScheme<'a> { } let rtidx = sig_idx - 32; //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); - if rtidx >= sigst.rtqs.len() { - sigst.rtqs.resize_with(rtidx + 1, VecDeque::new); + if rtidx >= target_proc.rtqs.len() { + target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); } - let rtq = sigst.rtqs.get_mut(rtidx).unwrap(); + let rtq = target_proc.rtqs.get_mut(rtidx).unwrap(); // TODO: configurable limit? if rtq.len() > 32 { @@ -1421,7 +1436,7 @@ impl<'a> ProcScheme<'a> { rtq.push_back(arg); } KillMode::Idempotent => { - if pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { + if sig_pctl.pending.load(Ordering::Acquire) & sig_bit(sig) != 0 { // If already pending, do not send this signal. While possible that // another thread is concurrently clearing pending, and that other // spuriously awoken threads would benefit from actually receiving @@ -1436,22 +1451,23 @@ impl<'a> ProcScheme<'a> { if sig_group != 0 { return SendResult::Invalid; } - pctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + sig_pctl.sender_infos[sig_idx] + .store(sender.raw(), Ordering::Relaxed); } } - pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); - drop(context_guard); + sig_pctl.pending.fetch_or(sig_bit(sig), Ordering::Release); - for thread in proc.read().threads.iter().filter_map(|t| t.upgrade()) { - let mut thread = thread.write(); - let Some((tctl, _, _)) = thread.sigcontrol() else { + for thread in target_proc.threads.iter() { + let thread = thread.borrow(); + let Some(ref tctl) = thread.sig_ctrl else { continue; }; if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - thread.unblock(); + // TODO + //thread.unblock(); *killed_self |= is_self; break; } @@ -1482,7 +1498,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; } - }*/ + } Ok(()) } @@ -1498,7 +1514,7 @@ pub enum KillTarget { } /* pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { - let Some((_tctl, pctl, st)) = current.sigcontrol() else { + let Some((_tctl, sig_pctl, st)) = current.sigcontrol() else { return Err(Error::new(ESRCH)); }; if sig_idx >= 32 { @@ -1512,7 +1528,7 @@ pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { return Err(Error::new(EAGAIN)); }; if q.is_empty() { - pctl.pending + sig_pctl.pending .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); } out.copy_exactly(&front)?; From ae63c54988fec808f6ba1f38471eaa8027731494 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 10:42:04 +0200 Subject: [PATCH 31/77] Implement getsid. --- src/procmgr.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e4a0f48c73..78cdec1437 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -764,8 +764,12 @@ impl<'a> ProcScheme<'a> { } } ProcCall::Getsid => { - log::error!("GETSID STUB"); - Ready(Response::ok(0, op)) + let req_pid = NonZeroUsize::new(metadata[1] as usize) + .map_or(fd_pid, |n| ProcessId(n.get())); + Ready(Response::new( + self.on_getsid(fd_pid, req_pid).map(|ProcessId(s)| s), + op, + )) } ProcCall::Setsid => { log::error!("SETSID STUB"); @@ -831,6 +835,26 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } + pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { + let caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + let requested_proc = self + .processes + .get(&req_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + + // POSIX allows, but does not require, the implementation to forbid getting the session ID of processes outside + // the current session. + if caller_proc.sid != requested_proc.sid && caller_proc.euid != 0 { + return Err(Error::new(EPERM)); + } + + Ok(requested_proc.sid) + } pub fn on_setpgid( &mut self, caller_pid: ProcessId, From a2861753dec96ade1d83913b2084f95536f9690f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:14:26 +0200 Subject: [PATCH 32/77] Implement setsid. --- src/procmgr.rs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 78cdec1437..8f7464c6bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -742,7 +742,7 @@ impl<'a> ProcScheme<'a> { self.work_on(state, awoken) } ProcCall::SetResugid => { - log::error!("SETRESGUID STUB"); + log::error!("SETRESUGID STUB"); Ready(Response::ok(0, op)) } ProcCall::Setpgid => { @@ -772,8 +772,7 @@ impl<'a> ProcScheme<'a> { )) } ProcCall::Setsid => { - log::error!("SETSID STUB"); - Ready(Response::ok(0, op)) + Ready(Response::new(self.on_setsid(fd_pid).map(|()| 0), op)) } ProcCall::SetResugid => Ready(Response::new( self.on_setresugid(fd_pid, payload).map(|()| 0), @@ -835,6 +834,33 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } + pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + let mut caller_proc = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + + // POSIX: already a process group leader + if caller_proc.pgid == caller_pid { + return Err(Error::new(EPERM)); + } + // TODO: more efficient? + // POSIX: any other process's pgid matches the caller pid + if self + .processes + .values() + .any(|p| p.borrow().pgid == caller_pid) + { + return Err(Error::new(EPERM)); + } + + caller_proc.pgid = caller_pid; + caller_proc.sid = caller_pid; + + // TODO: Remove controlling terminal + Ok(()) + } pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes From 0c5b47e677116bba526f0612d757fe7968828939 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:24:54 +0200 Subject: [PATCH 33/77] Phase out static mut for global allocator. --- src/lib.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4b76efcf38..6ed7505f9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,8 @@ pub mod start; extern crate alloc; +use core::cell::UnsafeCell; + use syscall::data::Map; use syscall::flag::MapFlags; @@ -52,15 +54,26 @@ struct Allocator; #[global_allocator] static ALLOCATOR: Allocator = Allocator; -static mut HEAP: Option = None; -static mut HEAP_TOP: usize = HEAP_OFF + SIZE; +struct AllocStateInner { + heap: Option, + heap_top: usize, +} +struct AllocState(UnsafeCell); +unsafe impl Send for AllocState {} +unsafe impl Sync for AllocState {} +static ALLOC_STATE: AllocState = AllocState(UnsafeCell::new(AllocStateInner { + heap: None, + heap_top: HEAP_OFF + SIZE, +})); + const SIZE: usize = 1024 * 1024; const HEAP_INCREASE_BY: usize = SIZE; unsafe impl alloc::alloc::GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { - let heap = HEAP.get_or_insert_with(|| { - HEAP_TOP = HEAP_OFF + SIZE; + let state = &mut (*ALLOC_STATE.0.get()); + let heap = state.heap.get_or_insert_with(|| { + state.heap_top = HEAP_OFF + SIZE; let _ = syscall::fmap( !0, &Map { @@ -89,7 +102,7 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { &Map { offset: 0, size: HEAP_INCREASE_BY, - address: HEAP_TOP, + address: state.heap_top, flags: MapFlags::PROT_WRITE | MapFlags::PROT_READ | MapFlags::MAP_PRIVATE @@ -98,14 +111,16 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { ) .expect("failed to extend heap"); heap.extend(HEAP_INCREASE_BY); - HEAP_TOP += HEAP_INCREASE_BY; + state.heap_top += HEAP_INCREASE_BY; return self.alloc(layout); } } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - HEAP.as_mut() + (&mut *ALLOC_STATE.0.get()) + .heap + .as_mut() .unwrap() .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) } From b4f717bac613923f359949f60252bc102f58917c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:34:18 +0200 Subject: [PATCH 34/77] Implement setresugid. --- src/procmgr.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8f7464c6bf..f5f5624b0d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -262,8 +262,10 @@ struct Process { ruid: u32, euid: u32, + suid: u32, rgid: u32, egid: u32, + sgid: u32, rns: u32, ens: u32, @@ -480,8 +482,10 @@ impl<'a> ProcScheme<'a> { pgid: INIT_PID, ruid: 0, euid: 0, + suid: 0, rgid: 0, egid: 0, + sgid: 0, rns: 1, ens: 1, @@ -512,10 +516,12 @@ impl<'a> ProcScheme<'a> { let Process { pgid, sid, - euid, ruid, - egid, + euid, + suid, rgid, + egid, + sgid, ens, rns, .. @@ -558,9 +564,11 @@ impl<'a> ProcScheme<'a> { pgid, sid, ruid, - rgid, euid, + suid, + rgid, egid, + sgid, rns, ens, @@ -1076,16 +1084,56 @@ impl<'a> ProcScheme<'a> { } } pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { - log::info!("ON_SETRESUGID {pid:?} {raw_buf:?}"); - let ids = { + let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) .unwrap() .try_into() .map_err(|_| Error::new(EINVAL))?; raw_ids.map(|i| if i == u32::MAX { None } else { Some(i) }) }; - let proc = self.processes.get_mut(&pid).ok_or(Error::new(ESRCH))?; - log::warn!("TODO: on_setresugid({pid:?}): {ids:?}"); + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + + let check = |new_ugid: u32, proc: &Process, gid_not_uid: bool| { + if proc.euid == 0 { + return Ok(()); + } + if gid_not_uid && ![proc.rgid, proc.egid, proc.sgid].contains(&new_ugid) { + return Err(Error::new(EPERM)); + } + if !gid_not_uid && ![proc.ruid, proc.euid, proc.suid].contains(&new_ugid) { + return Err(Error::new(EPERM)); + } + Ok(()) + }; + + if let Some(new_ruid) = new_ruid { + check(new_ruid, &*proc, false)?; + proc.ruid = new_ruid; + } + if let Some(new_euid) = new_euid { + check(new_euid, &*proc, false)?; + proc.euid = new_euid; + } + if let Some(new_suid) = new_suid { + check(new_suid, &*proc, false)?; + proc.suid = new_suid; + } + if let Some(new_rgid) = new_rgid { + check(new_rgid, &*proc, true)?; + proc.rgid = new_rgid; + } + if let Some(new_egid) = new_egid { + check(new_egid, &*proc, true)?; + proc.egid = new_egid; + } + if let Some(new_sgid) = new_sgid { + check(new_sgid, &*proc, true)?; + proc.sgid = new_sgid; + } Ok(()) } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { From 4d18d17dd2446c9453765e34ae05b30510b8061b Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:34:54 +0200 Subject: [PATCH 35/77] Remove redundant match arm. --- src/procmgr.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f5f5624b0d..8e5aa1fb81 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -749,10 +749,6 @@ impl<'a> ProcScheme<'a> { }); self.work_on(state, awoken) } - ProcCall::SetResugid => { - log::error!("SETRESUGID STUB"); - Ready(Response::ok(0, op)) - } ProcCall::Setpgid => { let target_pid = NonZeroUsize::new(metadata[1] as usize) .map_or(fd_pid, |n| ProcessId(n.get())); From 8434aa9fd843fd2bf4e766d0f46c452dbb7074e5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 11:41:24 +0200 Subject: [PATCH 36/77] Add ThisGroup selector for kill. --- src/procmgr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8e5aa1fb81..db18bd581d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1332,6 +1332,13 @@ impl<'a> ProcScheme<'a> { } ProcKillTarget::All => None, ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), + ProcKillTarget::ThisGroup => Some( + self.processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .pgid, + ), }; for (pid, proc_rc) in self.processes.iter() { From bdf9563688e92c2bb6ffb75ea9fa4108655fe97e Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 13:31:09 +0200 Subject: [PATCH 37/77] Make setresugid atomic. --- src/procmgr.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index db18bd581d..f7d89e321c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -645,10 +645,12 @@ impl<'a> ProcScheme<'a> { pid: pid.0 as u32, pgid: process.pgid.0 as u32, ppid: process.ppid.0 as u32, - euid: process.euid, - egid: process.egid, ruid: process.ruid, + euid: process.euid, + suid: process.suid, rgid: process.rgid, + egid: process.egid, + sgid: process.sgid, ens: process.ens, rns: process.rns, }; @@ -1093,41 +1095,39 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(ESRCH))? .borrow_mut(); - let check = |new_ugid: u32, proc: &Process, gid_not_uid: bool| { - if proc.euid == 0 { - return Ok(()); - } - if gid_not_uid && ![proc.rgid, proc.egid, proc.sgid].contains(&new_ugid) { + if proc.euid != 0 { + if ![new_ruid, new_euid, new_suid] + .iter() + .filter_map(|x| *x) + .all(|new_id| [proc.ruid, proc.euid, proc.suid].contains(&new_id)) + { return Err(Error::new(EPERM)); } - if !gid_not_uid && ![proc.ruid, proc.euid, proc.suid].contains(&new_ugid) { + if ![new_rgid, new_egid, new_sgid] + .iter() + .filter_map(|x| *x) + .all(|new_id| [proc.rgid, proc.egid, proc.sgid].contains(&new_id)) + { return Err(Error::new(EPERM)); } - Ok(()) - }; + } if let Some(new_ruid) = new_ruid { - check(new_ruid, &*proc, false)?; proc.ruid = new_ruid; } if let Some(new_euid) = new_euid { - check(new_euid, &*proc, false)?; proc.euid = new_euid; } if let Some(new_suid) = new_suid { - check(new_suid, &*proc, false)?; proc.suid = new_suid; } if let Some(new_rgid) = new_rgid { - check(new_rgid, &*proc, true)?; proc.rgid = new_rgid; } if let Some(new_egid) = new_egid { - check(new_egid, &*proc, true)?; proc.egid = new_egid; } if let Some(new_sgid) = new_sgid { - check(new_sgid, &*proc, true)?; proc.sgid = new_sgid; } Ok(()) From ae05abf69d6c9efd28ee1174dba12da27f54c9b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 14:43:35 +0200 Subject: [PATCH 38/77] Fix two instances of double RefCell borrow. --- src/procmgr.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f7d89e321c..72a46c2ed6 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -841,6 +841,16 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + // TODO: more efficient? + // POSIX: any other process's pgid matches the caller pid + if self + .processes + .values() + .any(|p| p.borrow().pgid == caller_pid) + { + return Err(Error::new(EPERM)); + } + let mut caller_proc = self .processes .get(&caller_pid) @@ -851,15 +861,6 @@ impl<'a> ProcScheme<'a> { if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - // TODO: more efficient? - // POSIX: any other process's pgid matches the caller pid - if self - .processes - .values() - .any(|p| p.borrow().pgid == caller_pid) - { - return Err(Error::new(EPERM)); - } caller_proc.pgid = caller_pid; caller_proc.sid = caller_pid; @@ -1059,8 +1060,9 @@ impl<'a> ProcScheme<'a> { let this_pgid = proc.pgid; if !self .processes - .values() - .any(|p| p.borrow().pgid == this_pgid) + .iter() + .filter(|(pid, _)| **pid != this_pid) + .any(|(_, p)| p.borrow().pgid == this_pgid) { return Ready(Err(Error::new(ECHILD))); } From 7902fc7c4d61dec920327a42084dc1b974e62fd2 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 16:19:37 +0200 Subject: [PATCH 39/77] Add thread handle type, thus wrapping open_via_dup. --- src/procmgr.rs | 73 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 72a46c2ed6..81d01e062e 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, ThreadCall, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -221,13 +221,13 @@ pub struct Page { ptr: NonNull, } impl Page { - pub fn map(fd: &FdGuard) -> Result { + pub fn map(fd: &FdGuard, offset: usize) -> Result { Ok(Self { ptr: NonNull::new(unsafe { syscall::fmap( **fd, &syscall::Map { - offset: 0, + offset, size: PAGE_SIZE, flags: MapFlags::PROT_READ, address: 0, @@ -392,6 +392,7 @@ enum WaitpidStatus { enum Handle { Init, Proc(ProcessId), + Thread(Rc>), } #[derive(Clone, Copy, Debug)] @@ -585,7 +586,7 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } - fn new_thread(&mut self, pid: ProcessId) -> Result { + fn new_thread(&mut self, pid: ProcessId) -> Result>> { // TODO: deduplicate code with fork let proc_rc = self.processes.get_mut(&pid).ok_or(Error::new(EBADFD))?; let mut proc = proc_rc.borrow_mut(); @@ -614,15 +615,15 @@ impl<'a> ProcScheme<'a> { let ident = *ctxt_fd; let thread = Rc::new(RefCell::new(Thread { - fd: FdGuard::new(syscall::dup(*ctxt_fd, &[])?), + fd: ctxt_fd, status_hndl, pid, sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); - proc.threads.push(thread); + proc.threads.push(Rc::clone(&thread)); self.thread_lookup.insert(ident, thread_weak); - Ok(ctxt_fd) + Ok(thread) } fn on_open(&mut self, path: &str, flags: usize) -> Result { if path == "init" { @@ -659,7 +660,7 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(EINVAL))? = metadata; Ok(size_of::()) } - Handle::Init => return Err(Error::new(EBADF)), + Handle::Init | Handle::Thread(_) => return Err(Error::new(EBADF)), } } fn on_dup(&mut self, old_id: usize, buf: &[u8]) -> Result { @@ -674,23 +675,37 @@ impl<'a> ProcScheme<'a> { flags: NewFdFlags::empty(), }) } - b"new-thread" => Ok(OpenResult::OtherScheme { - fd: self.new_thread(pid)?.take(), - }), + b"new-thread" => { + let thread = self.new_thread(pid)?; + Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Thread(thread)), + flags: NewFdFlags::empty(), + }) + } w if w.starts_with(b"thread-") => { let idx = core::str::from_utf8(&w["thread-".len()..]) .ok() .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); - let thread = process.threads.get(idx).ok_or(Error::new(ENOENT))?.borrow(); + let thread = Rc::clone(process.threads.get(idx).ok_or(Error::new(ENOENT))?); - return Ok(OpenResult::OtherScheme { - fd: syscall::dup(*thread.fd, &[])?, + return Ok(OpenResult::ThisScheme { + number: self.handles.insert(Handle::Thread(thread)), + flags: NewFdFlags::empty(), }); } _ => return Err(Error::new(EINVAL)), }, + Handle::Thread(ref thread_rc) => { + let thread = thread_rc.borrow(); + + // By forwarding all dup calls to the kernel, this fd is now effectively the same + // as the underlying fd since that fd can't do anything itself. + Ok(OpenResult::OtherScheme { + fd: syscall::dup(*thread.fd, buf)?, + }) + } Handle::Init => Err(Error::new(EBADF)), } } @@ -704,6 +719,17 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), + Handle::Thread(ref thr) => { + let Some(verb) = ThreadCall::try_from_raw(metadata[0] as usize) else { + return Response::ready_err(EINVAL, op); + }; + match verb { + ThreadCall::SyncSigTctl => Ready(Response::new( + Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), + op, + )), + } + } Handle::Proc(fd_pid) => { let Some(verb) = ProcCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); @@ -812,6 +838,9 @@ impl<'a> ProcScheme<'a> { op, )) } + ProcCall::SyncSigPctl => { + Ready(Response::new(self.on_sync_sigpctl(fd_pid).map(|()| 0), op)) + } } } } @@ -1605,6 +1634,22 @@ impl<'a> ProcScheme<'a> { Ok(()) } + pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { + let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sigcontrol")?); + thread.sig_ctrl.replace(Page::map(&sigcontrol_fd, 0)?); + Ok(()) + } + pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + let any_thread = proc.threads.first().ok_or(Error::new(EINVAL))?; + let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sigcontrol")?); + proc.sig_pctl.replace(Page::map(&sigcontrol_fd, PAGE_SIZE)?); + Ok(()) + } } #[derive(Clone, Copy)] pub enum KillMode { From a47396e2b953dd9e8b975fe43c79e640cdd8eab1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 31 Mar 2025 18:31:26 +0200 Subject: [PATCH 40/77] Fix SyncTctl and SyncPctl. --- src/procmgr.rs | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 81d01e062e..db70365f92 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -29,9 +29,10 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SenderInfo, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, - EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, EWOULDBLOCK, O_CLOEXEC, - O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, + EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, + EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, + SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -143,7 +144,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - log::debug!("TODO: UNKNOWN EVENT"); + log::warn!("TODO: UNKNOWN EVENT"); } } @@ -219,17 +220,19 @@ impl IntoTag for PendingState { #[derive(Debug)] pub struct Page { ptr: NonNull, + off: u16, } impl Page { - pub fn map(fd: &FdGuard, offset: usize) -> Result { + pub fn map(fd: &FdGuard, req_offset: usize, displacement: u16) -> Result { Ok(Self { + off: displacement, ptr: NonNull::new(unsafe { syscall::fmap( **fd, &syscall::Map { - offset, + offset: req_offset, size: PAGE_SIZE, - flags: MapFlags::PROT_READ, + flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_SHARED, address: 0, }, )? as *mut T @@ -242,7 +245,7 @@ impl Deref for Page { type Target = T; fn deref(&self) -> &T { - unsafe { self.ptr.as_ref() } + unsafe { &*self.ptr.as_ptr().byte_add(self.off.into()) } } } impl Drop for Page { @@ -1447,6 +1450,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { + log::trace!("No pctl {caller_pid:?}"); return SendResult::Invalid; }; @@ -1534,6 +1538,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { + log::trace!("No tctl"); return SendResult::Invalid; }; @@ -1551,10 +1556,11 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + log::trace!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; - //log::info!("QUEUEING {arg:?} RTIDX {rtidx}"); + //log::trace!("QUEUEING {arg:?} RTIDX {rtidx}"); if rtidx >= target_proc.rtqs.len() { target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); } @@ -1581,6 +1587,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { + log::trace!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1634,20 +1641,36 @@ impl<'a> ProcScheme<'a> { Ok(()) } + fn real_tctl_pctl_intra_page_offsets(fd: &FdGuard) -> Result<[u16; 2]> { + let mut buf = SetSighandlerData::default(); + let _ = syscall::read(**fd, &mut buf)?; + Ok([ + (buf.thread_control_addr % PAGE_SIZE) as u16, + (buf.proc_control_addr % PAGE_SIZE) as u16, + ]) + } pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { - let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sigcontrol")?); - thread.sig_ctrl.replace(Page::map(&sigcontrol_fd, 0)?); + log::trace!("Sync tctl {:?}", thread.pid); + let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); + let [tctl_off, _] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; + log::trace!("read intra offsets"); + thread + .sig_ctrl + .replace(Page::map(&sigcontrol_fd, 0, tctl_off)?); Ok(()) } pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + log::trace!("Sync pctl {pid:?}"); let mut proc = self .processes .get(&pid) .ok_or(Error::new(ESRCH))? .borrow_mut(); let any_thread = proc.threads.first().ok_or(Error::new(EINVAL))?; - let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sigcontrol")?); - proc.sig_pctl.replace(Page::map(&sigcontrol_fd, PAGE_SIZE)?); + let sigcontrol_fd = FdGuard::new(syscall::dup(*any_thread.borrow().fd, b"sighandler")?); + let [_, pctl_off] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; + proc.sig_pctl + .replace(Page::map(&sigcontrol_fd, PAGE_SIZE, pctl_off)?); Ok(()) } } From 7845d74df16da444294eea25f6b6ef35c4898b20 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 14:06:36 +0200 Subject: [PATCH 41/77] Impl SIGCONT and SIGSTOP. --- src/procmgr.rs | 71 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index db70365f92..b0b6055458 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -28,10 +28,10 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - sig_bit, ContextStatus, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, - Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, - EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ESRCH, - EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, + sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, + ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; @@ -468,7 +468,13 @@ impl<'a> ProcScheme<'a> { self.queue .subscribe(*fd, fd_out, EventFlags::EVENT_READ) .expect("TODO"); - let status_hndl = FdGuard::new(syscall::dup(*fd, b"status").expect("TODO")); + let status_hndl = FdGuard::new( + syscall::dup( + *fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + ) + .expect("TODO"), + ); let thread = Rc::new(RefCell::new(Thread { fd, @@ -534,7 +540,7 @@ impl<'a> ProcScheme<'a> { let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); let attr_fd = FdGuard::new(syscall::dup( *new_ctxt_fd, - alloc::format!("attrs-{}", **self.auth).as_bytes(), + alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), )?); let _ = syscall::write( *attr_fd, @@ -545,7 +551,10 @@ impl<'a> ProcScheme<'a> { ens, }, )?; - let status_fd = FdGuard::new(syscall::dup(*new_ctxt_fd, b"status")?); + let status_fd = FdGuard::new(syscall::dup( + *new_ctxt_fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + )?); self.queue .subscribe(*new_ctxt_fd, *new_ctxt_fd, EventFlags::EVENT_READ) @@ -598,7 +607,7 @@ impl<'a> ProcScheme<'a> { let attr_fd = FdGuard::new(syscall::dup( *ctxt_fd, - alloc::format!("attrs-{}", **self.auth).as_bytes(), + alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), )?); let _ = syscall::write( *attr_fd, @@ -610,7 +619,10 @@ impl<'a> ProcScheme<'a> { }, )?; - let status_hndl = FdGuard::new(syscall::dup(*ctxt_fd, b"status")?); + let status_hndl = FdGuard::new(syscall::dup( + *ctxt_fd, + alloc::format!("auth-{}-status", **self.auth).as_bytes(), + )?); self.queue .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) @@ -1496,25 +1508,25 @@ impl<'a> ProcScheme<'a> { .as_ref() .map_or(false, |proc| proc.signal_will_stop(sig))) { - todo!("tell kernel to stop process"); - /* - context_guard.status = context::Status::Blocked; - drop(context_guard); - process_lock.write().status = ProcessStatus::Stopped(sig); - */ + target_proc.status = ProcessStatus::Stopped(sig); + + for thread in &target_proc.threads { + let thread = thread.borrow(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Stop as usize).to_ne_bytes(), + ) + .expect("TODO"); + if let Some(ref tctl) = thread.sig_ctrl { + tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); + } + } // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise? sig_pctl .pending .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - for thread in target_proc.threads.iter() { - let thread = thread.borrow(); - if let Some(ref tctl) = thread.sig_ctrl { - tctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); - } - } - return SendResult::SucceededSigchld { orig_signal: sig, ppid: target_proc.ppid, @@ -1522,12 +1534,15 @@ impl<'a> ProcScheme<'a> { }; } if sig == SIGKILL { - todo!("tell kernel to kill context"); - /* - context_guard.being_sigkilled = true; - context_guard.unblock(); - drop(context_guard); - */ + for thread in &target_proc.threads { + let thread = thread.borrow(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::ForceKill as usize).to_ne_bytes(), + ) + .expect("TODO"); + } + *killed_self |= is_self; // exit() will signal the parent, rather than immediately in kill() From 362daf5ca91fd3b2f75510c003c986aab2ac65f3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 15:08:01 +0200 Subject: [PATCH 42/77] Add SIGCHLD logic. --- src/procmgr.rs | 82 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b0b6055458..20c65b904c 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -31,8 +31,8 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, - SIGTTOU, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, + SIGTTIN, SIGTTOU, }; pub fn run(write_fd: usize, auth: &FdGuard) { @@ -849,7 +849,8 @@ impl<'a> ProcScheme<'a> { let is_sigchld_to_parent = false; Ready(Response::new( - self.on_kill(fd_pid, target, signal, mode).map(|()| 0), + self.on_kill(fd_pid, target, signal, mode, awoken) + .map(|()| 0), op, )) } @@ -1359,7 +1360,9 @@ impl<'a> ProcScheme<'a> { target: ProcKillTarget, signal: u8, mode: KillMode, + awoken: &mut VecDeque, ) -> Result<()> { + log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1374,6 +1377,7 @@ impl<'a> ProcScheme<'a> { &mut killed_self, mode, is_sigchld_to_parent, + awoken, ) } ProcKillTarget::All => None, @@ -1398,6 +1402,7 @@ impl<'a> ProcScheme<'a> { &mut killed_self, mode, is_sigchld_to_parent, + awoken, ); match res { Ok(()) => (), @@ -1416,6 +1421,7 @@ impl<'a> ProcScheme<'a> { killed_self: &mut bool, mode: KillMode, is_sigchld_to_parent: bool, + awoken: &mut VecDeque, ) -> Result<()> { let sig = usize::from(signal); debug_assert!(sig <= 64); @@ -1436,7 +1442,7 @@ impl<'a> ProcScheme<'a> { enum SendResult { Succeeded, SucceededSigchld { - orig_signal: usize, + orig_signal: NonZeroU8, ppid: ProcessId, pgid: ProcessId, }, @@ -1455,9 +1461,9 @@ impl<'a> ProcScheme<'a> { // If sig = 0, test that process exists and can be signalled, but don't send any // signal. - if sig == 0 { + let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; - } + }; let mut target_proc = target_proc_rc.borrow_mut(); let target_proc = &mut *target_proc; @@ -1528,7 +1534,7 @@ impl<'a> ProcScheme<'a> { .fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); return SendResult::SucceededSigchld { - orig_signal: sig, + orig_signal: nz_signal, ppid: target_proc.ppid, pgid: target_proc.pgid, }; @@ -1563,7 +1569,6 @@ impl<'a> ProcScheme<'a> { tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - //context_guard.unblock(); *killed_self |= is_self; } } @@ -1647,10 +1652,64 @@ impl<'a> ProcScheme<'a> { ppid, pgid, orig_signal, - } => {} + } => { + { + let mut parent = self + .processes + .get(&ppid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + parent.waitpid.insert( + WaitpidKey { + pid: Some(target_pid), + pgid: Some(pgid), + }, + ( + target_pid, + WaitpidStatus::Stopped { + signal: orig_signal, + }, + ), + ); + awoken.extend(parent.waitpid_waiting.drain(..)); + } + self.on_send_sig( + // TODO? + ProcessId(1), + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + )?; + } SendResult::SucceededSigcont { ppid, pgid } => { + { + let mut parent = self + .processes + .get(&ppid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + parent.waitpid.insert( + WaitpidKey { + pid: Some(target_pid), + pgid: Some(pgid), + }, + (target_pid, WaitpidStatus::Continued), + ); + awoken.extend(parent.waitpid_waiting.drain(..)); + } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - //send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?; + self.on_send_sig( + ProcessId(1), + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + )?; } } @@ -1689,11 +1748,12 @@ impl<'a> ProcScheme<'a> { Ok(()) } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum KillMode { Idempotent, Queued(RtSigInfo), } +#[derive(Debug)] pub enum KillTarget { Proc(ProcessId), Thread(Rc>), From 53705b70495ccff497a2f93c54c208d1589d8fcf Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 1 Apr 2025 15:33:50 +0200 Subject: [PATCH 43/77] Some fixes. --- src/procmgr.rs | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 20c65b904c..8530226eb8 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1362,7 +1362,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1498,8 +1498,11 @@ impl<'a> ProcScheme<'a> { Ordering::Relaxed, ); } - // TODO - //thread.unblock(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Unstop as usize).to_ne_bytes(), + ) + .expect("TODO"); } // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. return SendResult::SucceededSigcont { @@ -1570,6 +1573,11 @@ impl<'a> ProcScheme<'a> { if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { *killed_self |= is_self; + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Interrupt as usize).to_ne_bytes(), + ) + .expect("TODO"); } } KillTarget::Proc(proc) => { @@ -1625,8 +1633,11 @@ impl<'a> ProcScheme<'a> { if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - // TODO - //thread.unblock(); + let _ = syscall::write( + *thread.status_hndl, + &(ContextVerb::Interrupt as usize).to_ne_bytes(), + ) + .expect("TODO"); *killed_self |= is_self; break; } @@ -1647,7 +1658,10 @@ impl<'a> ProcScheme<'a> { match result { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), - SendResult::Invalid => return Err(Error::new(EINVAL)), + SendResult::Invalid => { + log::debug!("Invalid signal configuration"); + return Err(Error::new(EINVAL)); + } SendResult::SucceededSigchld { ppid, pgid, @@ -1673,7 +1687,8 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - self.on_send_sig( + // TODO: Just ignore EINVAL (missing signal config) + let _ = self.on_send_sig( // TODO? ProcessId(1), KillTarget::Proc(ppid), @@ -1682,7 +1697,7 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - )?; + ); } SendResult::SucceededSigcont { ppid, pgid } => { { @@ -1701,7 +1716,8 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - self.on_send_sig( + // TODO: Just ignore EINVAL (missing signal config) + let _ = self.on_send_sig( ProcessId(1), KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1709,7 +1725,7 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - )?; + ); } } From ab3f59b22ba7074eb04fae84aba4fda3e45350b1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 3 Apr 2025 11:31:22 +0200 Subject: [PATCH 44/77] Reacquire thread fd as procmgr-managed before exec init. --- src/exec.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index 12e720e140..589c467e72 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -87,7 +87,8 @@ pub fn main() -> ! { spawn("process manager", &auth, &this_thr_fd, |write_fd| { crate::procmgr::run(write_fd, &auth) }); - let init_proc_fd = unsafe { redox_rt::proc::make_init() }; + let [init_proc_fd, init_thr_fd] = unsafe { redox_rt::proc::make_init() }; + // from this point, this_thr_fd is no longer valid const CWD: &[u8] = b"/scheme/initfs"; const DEFAULT_SCHEME: &[u8] = b"initfs"; @@ -97,7 +98,7 @@ pub fn main() -> ! { sigprocmask: 0, sigignmask: 0, umask: redox_rt::sys::get_umask(), - thr_fd: **this_thr_fd, + thr_fd: **init_thr_fd, proc_fd: **init_proc_fd, }; @@ -116,7 +117,7 @@ pub fn main() -> ! { fexec_impl( image_file, - this_thr_fd, + init_thr_fd, init_proc_fd, &memory, path.as_bytes(), From d59597b3ddba05ae7a1dcb0a7a679124e201cfee Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 11:12:29 +0200 Subject: [PATCH 45/77] Add TODO for proc termination without explicit exit. --- src/procmgr.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 8530226eb8..21d11da081 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -141,8 +141,13 @@ pub fn run(write_fd: usize, auth: &FdGuard) { } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); - log::trace!("AWAITING {}", proc.awaiting_threads_term.len(),); - awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + + if matches!(proc.status, ProcessStatus::Exiting { .. }) { + log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + } else { + todo!("handle proc termination without explicit exit"); + } } else { log::warn!("TODO: UNKNOWN EVENT"); } From 944177533118a34e4151143a299ba8a9bf2fcdca Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 11:20:20 +0200 Subject: [PATCH 46/77] Embed Id in VirtualId. --- src/procmgr.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 21d11da081..a7677d2300 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -35,6 +35,13 @@ use syscall::{ SIGTTIN, SIGTTOU, }; +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +enum VirtualId { + KernelId(Id), + // TODO: slab or something for better ID reuse + // InternalId(u64), +} + pub fn run(write_fd: usize, auth: &FdGuard) { let socket = Socket::nonblock("proc").expect("failed to open proc scheme socket"); @@ -53,8 +60,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::write(write_fd, &[0]); let _ = syscall::close(write_fd); - let mut states = HashMap::::new(); - let mut awoken = VecDeque::::new(); + let mut states = HashMap::::new(); + let mut awoken = VecDeque::::new(); let mut new_awoken = VecDeque::new(); 'outer: loop { @@ -159,12 +166,12 @@ fn handle_scheme<'a>( req: Request, socket: &'a Socket, scheme: &mut ProcScheme<'a>, - states: &mut HashMap, - awoken: &mut VecDeque, + states: &mut HashMap, + awoken: &mut VecDeque, ) -> Poll { match req.kind() { RequestKind::Call(req) => { - let req_id = req.request_id(); + let req_id = VirtualId::KernelId(req.request_id()); let op = match req.op() { Ok(op) => op, Err(req) => return Response::ready_err(ENOSYS, req), @@ -279,10 +286,10 @@ struct Process { status: ProcessStatus, - awaiting_threads_term: Vec, + awaiting_threads_term: Vec, waitpid: BTreeMap, - waitpid_waiting: VecDeque, + waitpid_waiting: VecDeque, sig_pctl: Option>, rtqs: Vec>, @@ -731,9 +738,9 @@ impl<'a> ProcScheme<'a> { } pub fn on_call( &mut self, - state: VacantEntry, + state: VacantEntry, mut op: OpCall, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Poll { let id = op.fd; let (payload, metadata) = op.payload_and_metadata(); @@ -963,8 +970,8 @@ impl<'a> ProcScheme<'a> { &mut self, pid: ProcessId, status: i32, - mut state: VacantEntry, - awoken: &mut VecDeque, + mut state: VacantEntry, + awoken: &mut VecDeque, tag: Tag, ) -> Poll { let Some(proc_rc) = self.processes.get(&pid) else { @@ -1010,7 +1017,7 @@ impl<'a> ProcScheme<'a> { this_pid: ProcessId, target: WaitpidTarget, flags: WaitFlags, - req_id: Id, + req_id: VirtualId, ) -> Poll> { if matches!( target, @@ -1276,8 +1283,8 @@ impl<'a> ProcScheme<'a> { } pub fn work_on( &mut self, - mut state_entry: OccupiedEntry, - awoken: &mut VecDeque, + mut state_entry: OccupiedEntry, + awoken: &mut VecDeque, ) -> Poll { let req_id = *state_entry.key(); let mut state = state_entry.get_mut(); @@ -1365,7 +1372,7 @@ impl<'a> ProcScheme<'a> { target: ProcKillTarget, signal: u8, mode: KillMode, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Result<()> { log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; @@ -1426,7 +1433,7 @@ impl<'a> ProcScheme<'a> { killed_self: &mut bool, mode: KillMode, is_sigchld_to_parent: bool, - awoken: &mut VecDeque, + awoken: &mut VecDeque, ) -> Result<()> { let sig = usize::from(signal); debug_assert!(sig <= 64); From 9c590bf3691fa48b0eca65f6a084a8741392546d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 12:04:38 +0200 Subject: [PATCH 47/77] Fix waitpid hang for exception-caused termination. --- src/procmgr.rs | 99 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index a7677d2300..070c979771 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -39,7 +39,7 @@ use syscall::{ enum VirtualId { KernelId(Id), // TODO: slab or something for better ID reuse - // InternalId(u64), + InternalId(u64), } pub fn run(write_fd: usize, auth: &FdGuard) { @@ -130,7 +130,8 @@ pub fn run(write_fd: usize, auth: &FdGuard) { continue; }; let thread = thread_rc.borrow(); - let Some(proc_rc) = scheme.processes.get(&thread.pid) else { + let pid = thread.pid; + let Some(proc_rc) = scheme.processes.get(&pid) else { // TODO? continue; }; @@ -153,7 +154,19 @@ pub fn run(write_fd: usize, auth: &FdGuard) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient } else { - todo!("handle proc termination without explicit exit"); + let internal_id = scheme.next_internal_id; + scheme.next_internal_id += 1; + let Entry::Vacant(entry) = states.entry(VirtualId::InternalId(internal_id)) else { + log::error!("internal ID reuse!"); + continue; + }; + drop(thread); + drop(proc); + let Pending = + scheme.on_exit_start(pid, 0 /* TODO */, entry, &mut awoken, None) + else { + unreachable!("not possible with tag=None"); + }; } } else { log::warn!("TODO: UNKNOWN EVENT"); @@ -216,10 +229,10 @@ enum PendingState { flags: WaitFlags, op: OpCall, }, - AwaitingThreadsTermination(ProcessId, Tag), + AwaitingThreadsTermination(ProcessId, Option), Placeholder, } -impl IntoTag for PendingState { +/*impl IntoTag for PendingState { fn into_tag(self) -> Tag { match self { Self::AwaitingThreadsTermination(_, tag) => tag, @@ -227,7 +240,7 @@ impl IntoTag for PendingState { Self::Placeholder => unreachable!(), } } -} +}*/ #[derive(Debug)] pub struct Page { @@ -385,6 +398,8 @@ struct ProcScheme<'a> { thread_lookup: HashMap>>, + next_internal_id: u64, + init_claimed: bool, next_id: ProcessId, @@ -458,6 +473,7 @@ impl<'a> ProcScheme<'a> { handles: Slab::new(), init_claimed: false, next_id: ProcessId(2), + next_internal_id: 1, queue, auth, } @@ -778,9 +794,13 @@ impl<'a> ProcScheme<'a> { .map(|()| 0), op, )), - ProcCall::Exit => { - self.on_exit_start(fd_pid, metadata[1] as i32, state, awoken, op.into_tag()) - } + ProcCall::Exit => self.on_exit_start( + fd_pid, + metadata[1] as i32, + state, + awoken, + Some(op.into_tag()), + ), ProcCall::Waitpid | ProcCall::Waitpgid => { let req_pid = ProcessId(metadata[1] as usize); let target = match (verb, metadata[1] == 0) { @@ -972,10 +992,14 @@ impl<'a> ProcScheme<'a> { status: i32, mut state: VacantEntry, awoken: &mut VecDeque, - tag: Tag, + tag: Option, ) -> Poll { let Some(proc_rc) = self.processes.get(&pid) else { - return Response::ready_err(EBADFD, tag); + return if let Some(tag) = tag { + Response::ready_err(EBADFD, tag) + } else { + Pending + }; }; let mut process_guard = proc_rc.borrow_mut(); let process = &mut *process_guard; @@ -983,8 +1007,20 @@ impl<'a> ProcScheme<'a> { match process.status { ProcessStatus::Stopped(_) | ProcessStatus::PossiblyRunnable => (), //ProcessStatus::Exiting => return Pending, - ProcessStatus::Exiting { .. } => return Response::ready_err(EAGAIN, tag), - ProcessStatus::Exited { .. } => return Response::ready_err(ESRCH, tag), + ProcessStatus::Exiting { .. } => { + return if let Some(tag) = tag { + Response::ready_err(EAGAIN, tag) + } else { + Pending + } + } + ProcessStatus::Exited { .. } => { + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) + } else { + Pending + } + } } // TODO: status/signal process.status = ProcessStatus::Exiting { @@ -996,8 +1032,11 @@ impl<'a> ProcScheme<'a> { // to-be-ignored cancellation request to this scheme). for thread in &process.threads { let mut thread = thread.borrow_mut(); + // TODO: cancel all threads anyway on error? if let Err(err) = syscall::write(*thread.status_hndl, &usize::MAX.to_ne_bytes()) { - return Response::ready_err(err.errno, tag); + if let Some(tag) = tag { + return Response::ready_err(err.errno, tag); + } } } @@ -1294,7 +1333,12 @@ impl<'a> ProcScheme<'a> { // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc_rc) = self.processes.get(¤t_pid) else { - return Response::ready_err(ESRCH, tag); + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) + } else { + state_entry.remove(); + Pending + }; }; let mut proc_guard = proc_rc.borrow_mut(); let proc = &mut *proc_guard; @@ -1303,8 +1347,22 @@ impl<'a> ProcScheme<'a> { log::trace!("WORKING ON AWAIT TERM"); let (signal, status) = match proc.status { ProcessStatus::Exiting { signal, status } => (signal, status), - ProcessStatus::Exited { .. } => return Response::ready_ok(0, tag), - _ => return Response::ready_err(ESRCH, tag), // TODO? + ProcessStatus::Exited { .. } => { + return if let Some(tag) = tag { + Response::ready_ok(0, tag) + } else { + state_entry.remove(); + Pending + } + } + _ => { + return if let Some(tag) = tag { + Response::ready_err(ESRCH, tag) // TODO? + } else { + state_entry.remove(); + Pending + } + } }; // TODO: Properly remove state state_entry.remove(); @@ -1325,7 +1383,12 @@ impl<'a> ProcScheme<'a> { // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } - Ready(Response::new(Ok(0), tag)) + if let Some(tag) = tag { + Ready(Response::new(Ok(0), tag)) + } else { + // state was removed earlier + Pending + } } else { log::trace!("WAITING AGAIN"); proc.awaiting_threads_term.push(req_id); From b7cb70859f1eea5fabcb4cf91366965ba93c09fe Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 4 Apr 2025 12:53:16 +0200 Subject: [PATCH 48/77] Implement ThreadCall::SignalThread. --- src/procmgr.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 070c979771..584e5e66db 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -771,6 +771,14 @@ impl<'a> ProcScheme<'a> { Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), op, )), + ThreadCall::SignalThread => { + let thr = Rc::clone(thr); + Ready(Response::new( + self.on_kill_thread(&thr, metadata[1] as u8, awoken) + .map(|()| 0), + op, + )) + } } } Handle::Proc(fd_pid) => { @@ -1361,7 +1369,7 @@ impl<'a> ProcScheme<'a> { } else { state_entry.remove(); Pending - } + }; } }; // TODO: Properly remove state @@ -1429,6 +1437,25 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } + pub fn on_kill_thread( + &mut self, + thread: &Rc>, + signal: u8, + awoken: &mut VecDeque, + ) -> Result<()> { + let mut killed_self = false; // TODO + let is_sigchld_to_parent = false; + let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + self.on_send_sig( + caller_pid, + KillTarget::Thread(Rc::clone(thread)), + signal, + &mut killed_self, + KillMode::Idempotent, + is_sigchld_to_parent, + awoken, + ) + } pub fn on_kill( &mut self, caller_pid: ProcessId, From 7edba6b84b879eccbed3f50c466c234010136a7b Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 7 Apr 2025 19:10:01 +0200 Subject: [PATCH 49/77] Fix fd mismatch for thread event handling. --- src/procmgr.rs | 66 +++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 584e5e66db..ad1a225158 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -147,6 +147,10 @@ pub fn run(write_fd: usize, auth: &FdGuard) { // spurious event continue; } + + if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { + log::error!("failed to unsubscribe from fd {}", event.data); + } scheme.thread_lookup.remove(&event.data); proc.threads.retain(|rc| !Rc::ptr_eq(rc, &thread_rc)); @@ -169,7 +173,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; } } else { - log::warn!("TODO: UNKNOWN EVENT"); + log::warn!("TODO: UNKNOWN EVENT {event:?}"); } } @@ -452,6 +456,9 @@ impl RawEventQueue { )?; Ok(()) } + pub fn unsubscribe(&self, fd: usize, ident: usize) -> Result<()> { + self.subscribe(fd, ident, EventFlags::empty()) + } pub fn next_event(&self) -> Result { let mut event = Event::default(); let read = syscall::read(*self.0, &mut event)?; @@ -653,7 +660,7 @@ impl<'a> ProcScheme<'a> { )?); self.queue - .subscribe(*ctxt_fd, *status_hndl, EventFlags::EVENT_READ) + .subscribe(*ctxt_fd, *ctxt_fd, EventFlags::EVENT_READ) .expect("TODO"); let ident = *ctxt_fd; @@ -1464,7 +1471,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1492,6 +1499,7 @@ impl<'a> ProcScheme<'a> { .pgid, ), }; + log::debug!("match group {match_grp:?}"); for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { @@ -1525,6 +1533,7 @@ impl<'a> ProcScheme<'a> { is_sigchld_to_parent: bool, awoken: &mut VecDeque, ) -> Result<()> { + log::debug!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; @@ -1570,7 +1579,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { - log::trace!("No pctl {caller_pid:?}"); + log::debug!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1664,7 +1673,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { - log::trace!("No tctl"); + log::debug!("No tctl"); return SendResult::Invalid; }; @@ -1686,7 +1695,7 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { - log::trace!("Out of range"); + log::debug!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; @@ -1717,7 +1726,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { - log::trace!("Invalid sig group"); + log::debug!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1761,7 +1770,7 @@ impl<'a> ProcScheme<'a> { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { - log::debug!("Invalid signal configuration"); + log::debug!("Invalid signal configuration for {target_pid:?}"); return Err(Error::new(EINVAL)); } SendResult::SucceededSigchld { @@ -1790,16 +1799,17 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // TODO: Just ignore EINVAL (missing signal config) - let _ = self.on_send_sig( - // TODO? - ProcessId(1), - KillTarget::Proc(ppid), - SIGCHLD as u8, - killed_self, - KillMode::Idempotent, - true, - awoken, - ); + if ppid != INIT_PID { + let _ = self.on_send_sig( + INIT_PID, // caller, TODO? + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + ); + } } SendResult::SucceededSigcont { ppid, pgid } => { { @@ -1819,15 +1829,17 @@ impl<'a> ProcScheme<'a> { } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. // TODO: Just ignore EINVAL (missing signal config) - let _ = self.on_send_sig( - ProcessId(1), - KillTarget::Proc(ppid), - SIGCHLD as u8, - killed_self, - KillMode::Idempotent, - true, - awoken, - ); + if ppid != INIT_PID { + let _ = self.on_send_sig( + INIT_PID, // caller, TODO? + KillTarget::Proc(ppid), + SIGCHLD as u8, + killed_self, + KillMode::Idempotent, + true, + awoken, + ); + } } } From 368fe1c186d580de2530c274ccf127c131ce2518 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 10 Apr 2025 15:39:40 +0200 Subject: [PATCH 50/77] Do not internally spawn exit() dying thread was last. --- src/procmgr.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index ad1a225158..c7b42843b1 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -141,12 +141,13 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); let status = usize::from_ne_bytes(buf); - log::trace!("STATUS {status}"); + log::trace!("--STATUS {status}"); if status != ContextStatus::Dead as usize { // spurious event continue; } + log::trace!("--THREAD DIED {}, {}", event.data, thread.pid.0); if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { log::error!("failed to unsubscribe from fd {}", event.data); @@ -157,7 +158,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { if matches!(proc.status, ProcessStatus::Exiting { .. }) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient - } else { + } else if proc.threads.is_empty() { let internal_id = scheme.next_internal_id; scheme.next_internal_id += 1; let Entry::Vacant(entry) = states.entry(VirtualId::InternalId(internal_id)) else { @@ -1085,11 +1086,13 @@ impl<'a> ProcScheme<'a> { } let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; + + log::trace!("WAITPID {target:?}"); + log::trace!("PROCS {:#?}", self.processes); + let mut proc_guard = proc_rc.borrow_mut(); let proc = &mut *proc_guard; - log::trace!("WAITPID"); - let recv_nonblock = |waitpid: &mut BTreeMap, key: &WaitpidKey| -> Option<(ProcessId, WaitpidStatus)> { @@ -1394,7 +1397,7 @@ impl<'a> ProcScheme<'a> { }, (current_pid, WaitpidStatus::Terminated { signal, status }), ); - //log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); + log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); // TODO: inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } @@ -1471,7 +1474,7 @@ impl<'a> ProcScheme<'a> { mode: KillMode, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; let mut killed_self = false; // TODO @@ -1499,7 +1502,7 @@ impl<'a> ProcScheme<'a> { .pgid, ), }; - log::debug!("match group {match_grp:?}"); + log::trace!("match group {match_grp:?}"); for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { From 3f54d0c80c8c9054b9abb7cf3914dff2c7c35cad Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 10 Apr 2025 15:55:18 +0200 Subject: [PATCH 51/77] Remove unnecessary debug. --- src/procmgr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index c7b42843b1..fbfe79fbb7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1536,7 +1536,7 @@ impl<'a> ProcScheme<'a> { is_sigchld_to_parent: bool, awoken: &mut VecDeque, ) -> Result<()> { - log::debug!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); + log::trace!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); debug_assert!(sig <= 64); let sig_group = (sig - 1) / 32; @@ -1582,7 +1582,7 @@ impl<'a> ProcScheme<'a> { let target_proc = &mut *target_proc; let Some(ref sig_pctl) = target_proc.sig_pctl else { - log::debug!("No pctl {caller_pid:?} => {target_pid:?}"); + log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1676,7 +1676,7 @@ impl<'a> ProcScheme<'a> { KillTarget::Thread(ref thread_rc) => { let thread = thread_rc.borrow(); let Some(ref tctl) = thread.sig_ctrl else { - log::debug!("No tctl"); + log::trace!("No tctl"); return SendResult::Invalid; }; @@ -1698,7 +1698,7 @@ impl<'a> ProcScheme<'a> { match mode { KillMode::Queued(arg) => { if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { - log::debug!("Out of range"); + log::trace!("Out of range"); return SendResult::Invalid; } let rtidx = sig_idx - 32; @@ -1729,7 +1729,7 @@ impl<'a> ProcScheme<'a> { } if sig_group != 0 { - log::debug!("Invalid sig group"); + log::trace!("Invalid sig group"); return SendResult::Invalid; } sig_pctl.sender_infos[sig_idx] @@ -1773,7 +1773,7 @@ impl<'a> ProcScheme<'a> { SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { - log::debug!("Invalid signal configuration for {target_pid:?}"); + log::trace!("Invalid signal configuration for {target_pid:?}"); return Err(Error::new(EINVAL)); } SendResult::SucceededSigchld { From 4ff5ddb2420c9e4f8ad9dbab212917070e39d397 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 13:08:54 +0200 Subject: [PATCH 52/77] Update Cargo.lock. --- Cargo.lock | 56 ++++++------------------------------------------------ 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9848284378..7e41408e6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -43,10 +31,10 @@ dependencies = [ ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" @@ -66,11 +54,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", + "foldhash", ] [[package]] @@ -115,12 +103,6 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - [[package]] name = "plain" version = "0.2.3" @@ -248,29 +230,3 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] From f6898a0e45e6d4289da3a8bb80c5a477dc0c2060 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 14:54:17 +0200 Subject: [PATCH 53/77] Pass exit status to waitpid correctly. --- src/procmgr.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index fbfe79fbb7..6547bb389d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1103,27 +1103,25 @@ impl<'a> ProcScheme<'a> { None } }; - let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| { - match status { - WaitpidStatus::Continued => { - // TODO: Handle None, i.e. restart everything until a match is found - if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, 0xffff)) - } else { - Pending - } - } - WaitpidStatus::Stopped { signal } => { - if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) - } else { - Pending - } - } - WaitpidStatus::Terminated { signal, status } => { - Ready((w_pid.0, signal.map_or(0, NonZeroU8::get).into())) + let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| match status { + WaitpidStatus::Continued => { + if flags.contains(WaitFlags::WCONTINUED) { + Ready((w_pid.0, 0xffff)) + } else { + Pending } } + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) + } else { + Pending + } + } + WaitpidStatus::Terminated { signal, status } => Ready(( + w_pid.0, + i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), + )), }; match target { From b4d42897d8ebcdbdd2c9efee495d587c11b5b7fc Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 11 Apr 2025 16:09:56 +0200 Subject: [PATCH 54/77] Return ERESTART when caller kills itself. --- src/procmgr.rs | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 6547bb389d..81d32de135 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -31,8 +31,8 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, - SIGTTIN, SIGTTOU, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, + SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -880,8 +880,6 @@ impl<'a> ProcScheme<'a> { else { return Response::ready_err(EINVAL, op); }; - let mut killed_self = false; - let mode = match verb { ProcCall::Kill => KillMode::Idempotent, ProcCall::Sigq => KillMode::Queued({ @@ -895,7 +893,6 @@ impl<'a> ProcScheme<'a> { _ => unreachable!(), }; - let is_sigchld_to_parent = false; Ready(Response::new( self.on_kill(fd_pid, target, signal, mode, awoken) .map(|()| 0), @@ -1451,9 +1448,12 @@ impl<'a> ProcScheme<'a> { signal: u8, awoken: &mut VecDeque, ) -> Result<()> { - let mut killed_self = false; // TODO + let mut killed_self = false; + let is_sigchld_to_parent = false; + let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + self.on_send_sig( caller_pid, KillTarget::Thread(Rc::clone(thread)), @@ -1462,7 +1462,14 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, is_sigchld_to_parent, awoken, - ) + )?; + + if killed_self { + // TODO: is this the most accurate error code? + Err(Error::new(ERESTART)) + } else { + Ok(()) + } } pub fn on_kill( &mut self, @@ -1475,8 +1482,12 @@ impl<'a> ProcScheme<'a> { log::trace!("KILL(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let mut num_succeeded = 0; - let mut killed_self = false; // TODO - let is_sigchld_to_parent = false; // TODO + // if this is set and we would otherwise have succeeded, return EINTR so it can check its + // own mask + let mut killed_self = false; + + // SIGCHLD to parent are not generated by on_kill, but by on_send_sig itself + let is_sigchld_to_parent = false; let match_grp = match target { ProcKillTarget::SingleProc(pid) => { @@ -1522,7 +1533,11 @@ impl<'a> ProcScheme<'a> { } } - Ok(()) + if killed_self { + Err(Error::new(ERESTART)) + } else { + Ok(()) + } } pub fn on_send_sig( &self, @@ -1567,9 +1582,11 @@ impl<'a> ProcScheme<'a> { } let result = (|| { - // FIXME - let is_self = false; - //let is_self = context::is_current(&context_lock); + // XXX: It's not currently possible for procmgr to know what thread called, so the + // EINTR will be coarser. That shouldn't affect program logic though, since the + // trampoline always checks the masks anyway. + // TODO: allow regular kill (alongside thread-kill) to operate on *thread fds*? + let is_self = target_pid == caller_pid; // If sig = 0, test that process exists and can be signalled, but don't send any // signal. From dd53cebf9177945d47c4ba3c6831aae9c56152ac Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 13 Apr 2025 21:20:42 +0200 Subject: [PATCH 55/77] Implement Sigdeq, fixing sigqueue test. --- src/procmgr.rs | 79 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 81d32de135..d86a705a0d 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,7 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, ThreadCall, WaitFlags}; +use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -29,10 +29,10 @@ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, - ProcSchemeAttrs, Result, RtSigInfo, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, - EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, - SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, + EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ERESTART, + ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, + SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -883,12 +883,12 @@ impl<'a> ProcScheme<'a> { let mode = match verb { ProcCall::Kill => KillMode::Idempotent, ProcCall::Sigq => KillMode::Queued({ - let mut buf = RtSigInfo::default(); - if payload.len() != buf.len() { + let mut buf = [0_u8; size_of::()]; + if payload.len() != size_of::() { return Response::ready_err(EINVAL, op); } buf.copy_from_slice(payload); - buf + *plain::from_bytes(&buf).unwrap() }), _ => unreachable!(), }; @@ -902,6 +902,10 @@ impl<'a> ProcScheme<'a> { ProcCall::SyncSigPctl => { Ready(Response::new(self.on_sync_sigpctl(fd_pid).map(|()| 0), op)) } + ProcCall::Sigdeq => Ready(Response::new( + self.on_sigdeq(fd_pid, payload).map(|()| 0), + op, + )), } } } @@ -1895,6 +1899,42 @@ impl<'a> ProcScheme<'a> { .replace(Page::map(&sigcontrol_fd, PAGE_SIZE, pctl_off)?); Ok(()) } + fn on_sigdeq(&mut self, pid: ProcessId, payload: &mut [u8]) -> Result<()> { + let sig_idx = { + let bytes = <[u8; 4]>::try_from(payload.get(..4).ok_or(Error::new(EINVAL))?).unwrap(); + u32::from_ne_bytes(bytes) + }; + log::trace!("SIGDEQ {pid:?} idx {sig_idx}"); + let mut dst = payload + .get_mut(..size_of::()) + .ok_or(Error::new(EINVAL))?; + if sig_idx >= 32 { + return Err(Error::new(EINVAL)); + } + let mut proc = self + .processes + .get_mut(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + let proc = &mut *proc; + + let pctl = proc.sig_pctl.as_ref().ok_or(Error::new(EBADF))?; + + let q = proc + .rtqs + .get_mut(sig_idx as usize) + .ok_or(Error::new(EAGAIN))?; + let Some(front) = q.pop_front() else { + return Err(Error::new(EAGAIN)); + }; + + if q.is_empty() { + pctl.pending + .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); + } + dst.copy_from_slice(unsafe { plain::as_bytes(&front) }); + Ok(()) + } } #[derive(Clone, Copy, Debug)] pub enum KillMode { @@ -1906,26 +1946,3 @@ pub enum KillTarget { Proc(ProcessId), Thread(Rc>), } -/* -pub fn sigdequeue(out: &mut [u8], sig_idx: u32) -> Result<()> { - let Some((_tctl, sig_pctl, st)) = current.sigcontrol() else { - return Err(Error::new(ESRCH)); - }; - if sig_idx >= 32 { - return Err(Error::new(EINVAL)); - } - let q = st - .rtqs - .get_mut(sig_idx as usize) - .ok_or(Error::new(EAGAIN))?; - let Some(front) = q.pop_front() else { - return Err(Error::new(EAGAIN)); - }; - if q.is_empty() { - sig_pctl.pending - .fetch_and(!(1 << (32 + sig_idx as usize)), Ordering::Relaxed); - } - out.copy_exactly(&front)?; - Ok(()) -} -*/ From 7bebb711d32c1b1cfcc305deb46f2fc32108fad4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 13 Apr 2025 22:36:57 +0200 Subject: [PATCH 56/77] Properly return EINVAL if sig>64. --- src/procmgr.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d86a705a0d..7a6be01512 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1555,7 +1555,11 @@ impl<'a> ProcScheme<'a> { ) -> Result<()> { log::trace!("SEND_SIG(from {caller_pid:?}) TARGET {target:?} {signal} {mode:?}"); let sig = usize::from(signal); - debug_assert!(sig <= 64); + + if sig > 64 { + return Err(Error::new(EINVAL)); + } + let sig_group = (sig - 1) / 32; let sig_idx = sig - 1; From 8784fb5010da8884c72ab7870cb9c60659bff598 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 14 Apr 2025 13:05:36 +0200 Subject: [PATCH 57/77] Handle stop signals correctly for orphaned pgrps, fixing bash. --- src/procmgr.rs | 231 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 157 insertions(+), 74 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7a6be01512..dfca883f6a 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -398,6 +398,7 @@ const INIT_PID: ProcessId = ProcessId(1); struct ProcScheme<'a> { processes: HashMap>, DefaultHashBuilder>, + groups: HashMap>>, sessions: HashSet, handles: Slab, @@ -411,6 +412,10 @@ struct ProcScheme<'a> { queue: &'a RawEventQueue, auth: &'a FdGuard, } +#[derive(Debug, Default)] +struct Pgrp { + processes: Vec>>, +} #[derive(Clone, Copy, Debug)] enum WaitpidStatus { Continued, @@ -476,6 +481,7 @@ impl<'a> ProcScheme<'a> { pub fn new(auth: &'a FdGuard, queue: &'a RawEventQueue) -> ProcScheme<'a> { ProcScheme { processes: HashMap::new(), + groups: HashMap::new(), sessions: HashSet::new(), thread_lookup: HashMap::new(), handles: Slab::new(), @@ -519,31 +525,35 @@ impl<'a> ProcScheme<'a> { sig_ctrl: None, })); let thread_weak = Rc::downgrade(&thread); - self.processes.insert( + let process = Rc::new(RefCell::new(Process { + threads: vec![thread], + ppid: INIT_PID, + sid: INIT_PID, + pgid: INIT_PID, + ruid: 0, + euid: 0, + suid: 0, + rgid: 0, + egid: 0, + sgid: 0, + rns: 1, + ens: 1, + + status: ProcessStatus::PossiblyRunnable, + awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), + + sig_pctl: None, + rtqs: Vec::new(), + })); + self.groups.insert( INIT_PID, - Rc::new(RefCell::new(Process { - threads: vec![thread], - ppid: INIT_PID, - sid: INIT_PID, - pgid: INIT_PID, - ruid: 0, - euid: 0, - suid: 0, - rgid: 0, - egid: 0, - sgid: 0, - rns: 1, - ens: 1, - - status: ProcessStatus::PossiblyRunnable, - awaiting_threads_term: Vec::new(), - waitpid: BTreeMap::new(), - waitpid_waiting: VecDeque::new(), - - sig_pctl: None, - rtqs: Vec::new(), + Rc::new(RefCell::new(Pgrp { + processes: vec![Rc::downgrade(&process)], })), ); + self.processes.insert(INIT_PID, process); self.sessions.insert(INIT_PID); self.thread_lookup.insert(fd_out, thread_weak); @@ -604,33 +614,38 @@ impl<'a> ProcScheme<'a> { sig_ctrl: None, // TODO })); let thread_weak = Rc::downgrade(&thread); + let new_process = Rc::new(RefCell::new(Process { + threads: vec![thread], + ppid: parent_pid, + pgid, + sid, + ruid, + euid, + suid, + rgid, + egid, + sgid, + rns, + ens, - self.processes.insert( - child_pid, - Rc::new(RefCell::new(Process { - threads: vec![thread], - ppid: parent_pid, - pgid, - sid, - ruid, - euid, - suid, - rgid, - egid, - sgid, - rns, - ens, + status: ProcessStatus::PossiblyRunnable, + awaiting_threads_term: Vec::new(), - status: ProcessStatus::PossiblyRunnable, - awaiting_threads_term: Vec::new(), + waitpid: BTreeMap::new(), + waitpid_waiting: VecDeque::new(), - waitpid: BTreeMap::new(), - waitpid_waiting: VecDeque::new(), + sig_pctl: None, // TODO + rtqs: Vec::new(), + })); - sig_pctl: None, // TODO - rtqs: Vec::new(), - })), - ); + if let Some(group) = self.groups.get(&pgid) { + group + .borrow_mut() + .processes + .push(Rc::downgrade(&new_process)); + } + + self.processes.insert(child_pid, new_process); self.thread_lookup.insert(thread_ident, thread_weak); Ok(child_pid) } @@ -760,7 +775,7 @@ impl<'a> ProcScheme<'a> { Handle::Init => Err(Error::new(EBADF)), } } - pub fn on_call( + fn on_call( &mut self, state: VacantEntry, mut op: OpCall, @@ -910,11 +925,7 @@ impl<'a> ProcScheme<'a> { } } } - pub fn on_getpgid( - &mut self, - caller_pid: ProcessId, - target_pid: ProcessId, - ) -> Result { + fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { let caller_proc = self .processes .get(&caller_pid) @@ -934,7 +945,7 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } - pub fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { // TODO: more efficient? // POSIX: any other process's pgid matches the caller pid if self @@ -945,24 +956,26 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - let mut caller_proc = self - .processes - .get(&caller_pid) - .ok_or(Error::new(ESRCH))? - .borrow_mut(); + let caller_proc_rc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + let mut caller_proc = caller_proc_rc.borrow_mut(); // POSIX: already a process group leader if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - caller_proc.pgid = caller_pid; caller_proc.sid = caller_pid; + Self::set_pgid( + caller_proc_rc, + &mut *caller_proc, + &mut self.groups, + caller_pid, + )?; // TODO: Remove controlling terminal Ok(()) } - pub fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { + fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes .get(&caller_pid) @@ -982,7 +995,7 @@ impl<'a> ProcScheme<'a> { Ok(requested_proc.sid) } - pub fn on_setpgid( + fn on_setpgid( &mut self, caller_pid: ProcessId, target_pid: ProcessId, @@ -993,17 +1006,48 @@ impl<'a> ProcScheme<'a> { let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; let mut proc = proc_rc.borrow_mut(); + if proc.pgid == new_pgid { + return Ok(()); + } + // Session leaders cannot have their pgid changed. if proc.sid == target_pid { return Err(Error::new(EPERM)); } // TODO: other security checks + Self::set_pgid(proc_rc, &mut *proc, &mut self.groups, new_pgid)?; + Ok(()) + } + fn set_pgid( + proc_rc: &Rc>, + proc: &mut Process, + groups: &mut HashMap>>, + new_pgid: ProcessId, + ) -> Result<()> { + let old_pgid = proc.pgid; + assert_ne!(old_pgid, new_pgid); + + let proc_weak = Rc::downgrade(&proc_rc); + let shall_remove = { + let mut old_group = groups.get(&old_pgid).ok_or(Error::new(ESRCH))?.borrow_mut(); + old_group.processes.retain(|w| !Weak::ptr_eq(w, &proc_weak)); + old_group.processes.is_empty() + }; + if shall_remove { + groups.remove(&old_pgid); + } + groups + .entry(new_pgid) + .or_default() + .borrow_mut() + .processes + .push(proc_weak); proc.pgid = new_pgid; Ok(()) } - pub fn on_exit_start( + fn on_exit_start( &mut self, pid: ProcessId, status: i32, @@ -1068,7 +1112,7 @@ impl<'a> ProcScheme<'a> { awoken, ) } - pub fn on_waitpid( + fn on_waitpid( &mut self, this_pid: ProcessId, target: WaitpidTarget, @@ -1196,7 +1240,7 @@ impl<'a> ProcScheme<'a> { } } } - pub fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { + fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) .unwrap() @@ -1278,7 +1322,7 @@ impl<'a> ProcScheme<'a> { }*/ todo!() } - pub fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { + fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let mut process = proc_rc.borrow_mut(); @@ -1337,7 +1381,7 @@ impl<'a> ProcScheme<'a> { } Ok(()) } - pub fn work_on( + fn work_on( &mut self, mut state_entry: OccupiedEntry, awoken: &mut VecDeque, @@ -1446,7 +1490,7 @@ impl<'a> ProcScheme<'a> { log::trace!("PROCESSES\n{:#?}", self.processes,); log::trace!("HANDLES\n{:#?}", self.handles,); } - pub fn on_kill_thread( + fn on_kill_thread( &mut self, thread: &Rc>, signal: u8, @@ -1475,7 +1519,7 @@ impl<'a> ProcScheme<'a> { Ok(()) } } - pub fn on_kill( + fn on_kill( &mut self, caller_pid: ProcessId, target: ProcKillTarget, @@ -1543,7 +1587,7 @@ impl<'a> ProcScheme<'a> { Ok(()) } } - pub fn on_send_sig( + fn on_send_sig( &self, caller_pid: ProcessId, target: KillTarget, @@ -1601,10 +1645,10 @@ impl<'a> ProcScheme<'a> { let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; }; - let mut target_proc = target_proc_rc.borrow_mut(); - let target_proc = &mut *target_proc; + let mut target_proc_guard = target_proc_rc.borrow_mut(); + let mut target_proc = &mut *target_proc_guard; - let Some(ref sig_pctl) = target_proc.sig_pctl else { + let Some(mut sig_pctl) = target_proc.sig_pctl.as_ref() else { log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); return SendResult::Invalid; }; @@ -1647,13 +1691,29 @@ impl<'a> ProcScheme<'a> { pgid: target_proc.pgid, }; } + let is_conditional_stop = matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP); if sig == SIGSTOP - || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) + || (is_conditional_stop && target_proc .sig_pctl .as_ref() .map_or(false, |proc| proc.signal_will_stop(sig))) { + if is_conditional_stop { + let pgid = target_proc.pgid; + drop(target_proc_guard); + + if self.pgrp_is_orphaned(pgid).unwrap_or(true) { + // POSIX requires that processes in orphaned process groups never be stopped in + // due to SIGTTIN/SIGTTOU/SIGTSTP. + return SendResult::Succeeded; + } + + target_proc_guard = target_proc_rc.borrow_mut(); + target_proc = &mut *target_proc_guard; + sig_pctl = target_proc.sig_pctl.as_mut().expect("already checked"); + } + target_proc.status = ProcessStatus::Stopped(sig); for thread in &target_proc.threads { @@ -1879,7 +1939,7 @@ impl<'a> ProcScheme<'a> { (buf.proc_control_addr % PAGE_SIZE) as u16, ]) } - pub fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { + fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { log::trace!("Sync tctl {:?}", thread.pid); let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); let [tctl_off, _] = Self::real_tctl_pctl_intra_page_offsets(&sigcontrol_fd)?; @@ -1889,7 +1949,7 @@ impl<'a> ProcScheme<'a> { .replace(Page::map(&sigcontrol_fd, 0, tctl_off)?); Ok(()) } - pub fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { + fn on_sync_sigpctl(&mut self, pid: ProcessId) -> Result<()> { log::trace!("Sync pctl {pid:?}"); let mut proc = self .processes @@ -1939,6 +1999,29 @@ impl<'a> ProcScheme<'a> { dst.copy_from_slice(unsafe { plain::as_bytes(&front) }); Ok(()) } + fn pgrp_is_orphaned(&self, grp: ProcessId) -> Option { + let group = self.groups.get(&grp)?.borrow(); + + let mut still_true = true; + + for process_rc in group.processes.iter().filter_map(Weak::upgrade) { + let process = process_rc.borrow(); + let Some(parent_rc) = self.processes.get(&process_rc.borrow().ppid) else { + // TODO: what to do here? + continue; + }; + let parent = parent_rc.borrow(); + + // POSIX defines orphaned process groups as those where + // + // forall process in group, + // process's parent pgid == process's pgid + // OR + // process's session id != process's session id + still_true &= parent.pgid == process.pgid || parent.sid != process.sid; + } + Some(still_true) + } } #[derive(Clone, Copy, Debug)] pub enum KillMode { From 0036fffd8299a2193be973992206ab26222b77c5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 15 Apr 2025 15:23:43 +0200 Subject: [PATCH 58/77] WIP: Recognize SIGKILL and unhandled exceptions. --- src/procmgr.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index dfca883f6a..977e267278 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -6,8 +6,8 @@ use core::num::{NonZeroU8, NonZeroUsize}; use core::ops::Deref; use core::ptr::NonNull; use core::sync::atomic::Ordering; -use core::task::Poll; use core::task::Poll::*; +use core::task::{Context, Poll}; use alloc::collections::btree_map::BTreeMap; use alloc::collections::VecDeque; @@ -28,11 +28,11 @@ use redox_scheme::{ use slab::Slab; use syscall::schemev2::NewFdFlags; use syscall::{ - sig_bit, ContextStatus, ContextVerb, Error, Event, EventFlags, FobtainFdFlags, MapFlags, - ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, - EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, ERESTART, - ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, - SIGTTIN, SIGTTOU, + sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, + MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, + SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -137,16 +137,24 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; let mut proc = proc_rc.borrow_mut(); log::trace!("THREAD EVENT FROM {}, {}", event.data, thread.pid.0); - let mut buf = 0_usize.to_ne_bytes(); - let _ = syscall::read(*thread.status_hndl, &mut buf).unwrap(); - let status = usize::from_ne_bytes(buf); + let mut sts_buf = CtxtStsBuf::default(); + let _ = syscall::read(*thread.status_hndl, &mut sts_buf).unwrap(); - log::trace!("--STATUS {status}"); - - if status != ContextStatus::Dead as usize { + let status = if sts_buf.status == ContextStatus::Dead as usize { + // dont-care, already called explicit exit() + 0 + } else if sts_buf.status == ContextStatus::ForceKilled as usize { + // TODO: "killed by SIGKILL" + 1 + } else if sts_buf.status == ContextStatus::UnhandledExcp as usize { + // TODO: translate arch-specific exception kind + // into signal (SIGSEGV, SIGBUS, SIGILL, SIGFPE) + 1 + } else { // spurious event continue; - } + }; + log::trace!("--THREAD DIED {}, {}", event.data, thread.pid.0); if let Err(err) = scheme.queue.unsubscribe(event.data, event.data) { @@ -167,9 +175,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { }; drop(thread); drop(proc); - let Pending = - scheme.on_exit_start(pid, 0 /* TODO */, entry, &mut awoken, None) - else { + let Pending = scheme.on_exit_start(pid, status, entry, &mut awoken, None) else { unreachable!("not possible with tag=None"); }; } From baeb9d0f9c3c8ad76936cc5059f75a6b5a22230d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 11:18:07 +0200 Subject: [PATCH 59/77] Add reference to NLnet project. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 2396cc28a1..a56ea88e81 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,10 @@ To learn how to do development with this system component inside the Redox build To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. + +## Funding - Process Manager (`src/procmgr.rs`) + +The _Unix-style Signals and Process Management_ project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals). + +[NLnet foundation logo](https://nlnet.nl) +[NGI Zero Logo](https://nlnet.nl/core) From 713ba3b23dd3239802a599b9c23abc940dc721b0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 11:58:44 +0200 Subject: [PATCH 60/77] Import sig numbers from redox_rt rather than syscall. --- src/procmgr.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 977e267278..b677414d32 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -19,7 +19,10 @@ use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; use redox_rt::proc::FdGuard; -use redox_rt::protocol::{ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags}; +use redox_rt::protocol::{ + ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT, + SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, +}; use redox_scheme::scheme::{IntoTag, Op, OpCall}; use redox_scheme::{ CallerCtx, Id, OpenResult, Request, RequestKind, Response, SendFdRequest, SignalBehavior, @@ -31,8 +34,7 @@ use syscall::{ sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, SIGCHLD, SIGCONT, SIGKILL, - SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, + ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] From 4b27f83bf9337044653fe81f37c719c981394209 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:01:09 +0200 Subject: [PATCH 61/77] Handle both 'any' and 'any pgid-matching' child in waitpid. --- src/procmgr.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index b677414d32..643bbdeede 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1178,11 +1178,17 @@ impl<'a> ProcScheme<'a> { }; match target { - // TODO: not the same WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { - if let Some((wid, (w_pid, status))) = - proc.waitpid.first_key_value().map(|(k, v)| (*k, *v)) - { + let kv = (if matches!(target, WaitpidTarget::AnyChild) { + proc.waitpid.first_key_value() + } else { + proc.waitpid.get_key_value(&WaitpidKey { + pid: None, + pgid: Some(proc.pgid), + }) + }) + .map(|(k, v)| (*k, *v)); + if let Some((wid, (w_pid, status))) = kv { let _ = proc.waitpid.remove(&wid); grim_reaper(w_pid, status).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { @@ -1399,7 +1405,6 @@ impl<'a> ProcScheme<'a> { let this_state = core::mem::replace(state, PendingState::Placeholder); match this_state { PendingState::Placeholder => return Pending, // unreachable!(), - // TODO PendingState::AwaitingThreadsTermination(current_pid, tag) => { let Some(proc_rc) = self.processes.get(¤t_pid) else { return if let Some(tag) = tag { @@ -1892,7 +1897,7 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - // TODO: Just ignore EINVAL (missing signal config) + // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -1922,7 +1927,7 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - // TODO: Just ignore EINVAL (missing signal config) + // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? From 898c5268ef54af338c16944963898214ead01de4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:05:33 +0200 Subject: [PATCH 62/77] Fix misc TODOs. --- src/procmgr.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 643bbdeede..f15d78cffb 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -134,7 +134,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let thread = thread_rc.borrow(); let pid = thread.pid; let Some(proc_rc) = scheme.processes.get(&pid) else { - // TODO? + // TODO(err)? continue; }; let mut proc = proc_rc.borrow_mut(); @@ -167,7 +167,7 @@ pub fn run(write_fd: usize, auth: &FdGuard) { if matches!(proc.status, ProcessStatus::Exiting { .. }) { log::trace!("WAKING UP {}", proc.awaiting_threads_term.len(),); - awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO: inefficient + awoken.extend(proc.awaiting_threads_term.drain(..)); // TODO(opt) } else if proc.threads.is_empty() { let internal_id = scheme.next_internal_id; scheme.next_internal_id += 1; @@ -450,7 +450,7 @@ enum WaitpidTarget { AnyChild, AnyGroupMember, } -// TODO: Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and +// TODO(feat): Add 'syscall' backend for redox-event so it can act both as library-ABI frontend and // backend struct RawEventQueue(FdGuard); impl RawEventQueue { @@ -1132,7 +1132,7 @@ impl<'a> ProcScheme<'a> { WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember ) { // Check for existence of child. - // TODO: inefficient, keep refcount? + // TODO(opt): inefficient, keep refcount? if !self.processes.values().any(|p| p.borrow().ppid == this_pid) { return Ready(Err(Error::new(ECHILD))); } @@ -1445,7 +1445,7 @@ impl<'a> ProcScheme<'a> { let (ppid, pgid) = (proc.ppid, proc.pgid); if let Some(parent_rc) = self.processes.get(&ppid) { let mut parent = parent_rc.borrow_mut(); - // TODO: transfer children to parent, and all of self.waitpid + // TODO(posix): transfer children to parent, and all of self.waitpid parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -1454,7 +1454,7 @@ impl<'a> ProcScheme<'a> { (current_pid, WaitpidStatus::Terminated { signal, status }), ); log::trace!("AWAKING WAITPID {:?}", parent.waitpid_waiting); - // TODO: inefficient + // TODO(opt): inefficient awoken.extend(parent.waitpid_waiting.drain(..)); } if let Some(tag) = tag { @@ -1513,7 +1513,7 @@ impl<'a> ProcScheme<'a> { let is_sigchld_to_parent = false; - let caller_pid = thread.borrow().pid; // TODO: allow this to be specified? + let caller_pid = thread.borrow().pid; // TODO(feat): allow this to be specified? self.on_send_sig( caller_pid, @@ -1628,7 +1628,12 @@ impl<'a> ProcScheme<'a> { let sender = SenderInfo { pid: caller_pid.0 as u32, - ruid: 0, // TODO + ruid: self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .ruid, }; enum SendResult { @@ -1650,7 +1655,7 @@ impl<'a> ProcScheme<'a> { // XXX: It's not currently possible for procmgr to know what thread called, so the // EINTR will be coarser. That shouldn't affect program logic though, since the // trampoline always checks the masks anyway. - // TODO: allow regular kill (alongside thread-kill) to operate on *thread fds*? + // TODO(feat): allow regular kill (alongside thread-kill) to operate on *thread fds*? let is_self = target_pid == caller_pid; // If sig = 0, test that process exists and can be signalled, but don't send any @@ -1804,7 +1809,7 @@ impl<'a> ProcScheme<'a> { } let rtq = target_proc.rtqs.get_mut(rtidx).unwrap(); - // TODO: configurable limit? + // TODO(feat): configurable limit? if rtq.len() > 32 { return SendResult::FullQ; } @@ -1897,7 +1902,7 @@ impl<'a> ProcScheme<'a> { ); awoken.extend(parent.waitpid_waiting.drain(..)); } - // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? + // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -1927,7 +1932,7 @@ impl<'a> ProcScheme<'a> { awoken.extend(parent.waitpid_waiting.drain(..)); } // POSIX XSI allows but does not require SIGCONT to send signals to the parent. - // TODO: Just ignore EINVAL (missing signal config), otherwise handle error? + // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { let _ = self.on_send_sig( INIT_PID, // caller, TODO? @@ -2020,7 +2025,7 @@ impl<'a> ProcScheme<'a> { for process_rc in group.processes.iter().filter_map(Weak::upgrade) { let process = process_rc.borrow(); let Some(parent_rc) = self.processes.get(&process_rc.borrow().ppid) else { - // TODO: what to do here? + // TODO(err): what to do here? continue; }; let parent = parent_rc.borrow(); From c881b1469862bda41782176f880693be1815dcdd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 16 Apr 2025 23:45:06 +0200 Subject: [PATCH 63/77] Allow indicating 'exited from signal'. --- src/procmgr.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index f15d78cffb..d2804c4cc6 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -835,7 +835,7 @@ impl<'a> ProcScheme<'a> { )), ProcCall::Exit => self.on_exit_start( fd_pid, - metadata[1] as i32, + metadata[1] as u16, state, awoken, Some(op.into_tag()), @@ -1058,7 +1058,7 @@ impl<'a> ProcScheme<'a> { fn on_exit_start( &mut self, pid: ProcessId, - status: i32, + status: u16, mut state: VacantEntry, awoken: &mut VecDeque, tag: Option, @@ -1091,11 +1091,29 @@ impl<'a> ProcScheme<'a> { } } } - // TODO: status/signal - process.status = ProcessStatus::Exiting { - status: status as u8, - signal: None, + + // Forbid the caller from giving statuses corresponding to e.g. WIFCONTINUED which exit() + // obviously can never be. + + log::debug!("Killed with raw status {status:?}"); + + // TODO: Are WIFEXITED and WIFSIGNALED mutually exclusive? + let (status, signal) = if status & 0xff == status { + (status as u8, None) + } else { + // TODO: Only allow valid and catchable signal numbers. + let sig = (status >> 8) as u8; + if !matches!(sig, 1..=64) { + return if let Some(tag) = tag { + Response::ready_err(EINVAL, tag) + } else { + Pending + }; + } + (0, NonZeroU8::new(sig)) }; + + process.status = ProcessStatus::Exiting { status, signal }; if !process.threads.is_empty() { // terminate all threads (possibly including the caller, resulting in EINTR and a // to-be-ignored cancellation request to this scheme). From 26371995cbc72ecf2ea35eccbe6ab15b37b10ed6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:21:21 +0200 Subject: [PATCH 64/77] Fix kill(getpid(), ...). --- src/procmgr.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index d2804c4cc6..7f59565a66 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1095,7 +1095,7 @@ impl<'a> ProcScheme<'a> { // Forbid the caller from giving statuses corresponding to e.g. WIFCONTINUED which exit() // obviously can never be. - log::debug!("Killed with raw status {status:?}"); + log::trace!("Killed with raw status {status:?}"); // TODO: Are WIFEXITED and WIFSIGNALED mutually exclusive? let (status, signal) = if status & 0xff == status { @@ -1570,7 +1570,7 @@ impl<'a> ProcScheme<'a> { let match_grp = match target { ProcKillTarget::SingleProc(pid) => { - return self.on_send_sig( + self.on_send_sig( caller_pid, KillTarget::Proc(ProcessId(pid)), signal, @@ -1578,7 +1578,12 @@ impl<'a> ProcScheme<'a> { mode, is_sigchld_to_parent, awoken, - ) + )?; + return if killed_self { + Err(Error::new(ERESTART)) + } else { + Ok(()) + }; } ProcKillTarget::All => None, ProcKillTarget::ProcGroup(grp) => Some(ProcessId(grp)), From 486c8496b567c7ae07060152ea6ef9e2fd0935dd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:28:52 +0200 Subject: [PATCH 65/77] Add missing kill uid checks. --- src/procmgr.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7f59565a66..12659ae3bf 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1660,6 +1660,7 @@ impl<'a> ProcScheme<'a> { }; enum SendResult { + LacksPermission, Succeeded, SucceededSigchld { orig_signal: NonZeroU8, @@ -1673,6 +1674,14 @@ impl<'a> ProcScheme<'a> { FullQ, Invalid, } + let (caller_euid, caller_ruid) = { + let caller = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow(); + (caller.euid, caller.ruid) + }; let result = (|| { // XXX: It's not currently possible for procmgr to know what thread called, so the @@ -1681,13 +1690,21 @@ impl<'a> ProcScheme<'a> { // TODO(feat): allow regular kill (alongside thread-kill) to operate on *thread fds*? let is_self = target_pid == caller_pid; + let mut target_proc_guard = target_proc_rc.borrow_mut(); + let mut target_proc = &mut *target_proc_guard; + + if caller_euid != 0 + && caller_euid != target_proc.ruid + && caller_ruid != target_proc.ruid + { + return SendResult::LacksPermission; + } + // If sig = 0, test that process exists and can be signalled, but don't send any // signal. let Some(nz_signal) = NonZeroU8::new(signal) else { return SendResult::Succeeded; }; - let mut target_proc_guard = target_proc_rc.borrow_mut(); - let mut target_proc = &mut *target_proc_guard; let Some(mut sig_pctl) = target_proc.sig_pctl.as_ref() else { log::trace!("No pctl {caller_pid:?} => {target_pid:?}"); @@ -1894,6 +1911,9 @@ impl<'a> ProcScheme<'a> { })(); match result { + // TODO: succeed even if *some* (when group/all procs is specified) fail? + SendResult::LacksPermission => return Err(Error::new(EPERM)), + SendResult::Succeeded => (), SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { From 3e58f6ad16bfba8762b2e7ef7503bbb10856ec98 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 00:56:16 +0200 Subject: [PATCH 66/77] Mod sig_idx by 32. --- src/procmgr.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 12659ae3bf..e765023875 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1641,7 +1641,7 @@ impl<'a> ProcScheme<'a> { } let sig_group = (sig - 1) / 32; - let sig_idx = sig - 1; + let sig_idx = (sig - 1) % 32; let target_pid = match target { KillTarget::Proc(pid) => pid, @@ -1838,11 +1838,11 @@ impl<'a> ProcScheme<'a> { KillTarget::Proc(proc) => { match mode { KillMode::Queued(arg) => { - if sig_group != 1 || sig_idx < 32 || sig_idx >= 64 { + if sig_group != 1 { log::trace!("Out of range"); return SendResult::Invalid; } - let rtidx = sig_idx - 32; + let rtidx = sig_idx; //log::trace!("QUEUEING {arg:?} RTIDX {rtidx}"); if rtidx >= target_proc.rtqs.len() { target_proc.rtqs.resize_with(rtidx + 1, VecDeque::new); @@ -1918,7 +1918,7 @@ impl<'a> ProcScheme<'a> { SendResult::FullQ => return Err(Error::new(EAGAIN)), SendResult::Invalid => { log::trace!("Invalid signal configuration for {target_pid:?}"); - return Err(Error::new(EINVAL)); + return Err(Error::new(ESRCH)); } SendResult::SucceededSigchld { ppid, From 771993b260b4a50ea3bdb47be6f7fb7e92839b3f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 17 Apr 2025 13:15:34 +0200 Subject: [PATCH 67/77] Potential fix for KillThread for sig>32. --- src/procmgr.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index e765023875..302ddd9056 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -1822,11 +1822,10 @@ impl<'a> ProcScheme<'a> { }; tctl.sender_infos[sig_idx].store(sender.raw(), Ordering::Relaxed); + let bit = 1 << sig_idx; - let _was_new = - tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Release); - if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 - { + let _was_new = tctl.word[sig_group].fetch_or(bit, Ordering::Release); + if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & bit != 0 { *killed_self |= is_self; let _ = syscall::write( *thread.status_hndl, From 5cbcb06727aa8436a72282b89e8c6eb2cf94abc6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 10:48:48 +0200 Subject: [PATCH 68/77] Fix ECHILD check and reap, fixing bash. --- src/procmgr.rs | 199 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 59 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 302ddd9056..7e335248b7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -876,7 +876,8 @@ impl<'a> ProcScheme<'a> { )) } else { Ready(Response::new( - self.on_setpgid(fd_pid, target_pid, new_pgid).map(|()| 0), + self.on_setpgid(fd_pid, target_pid, new_pgid, awoken) + .map(|()| 0), op, )) } @@ -889,9 +890,10 @@ impl<'a> ProcScheme<'a> { op, )) } - ProcCall::Setsid => { - Ready(Response::new(self.on_setsid(fd_pid).map(|()| 0), op)) - } + ProcCall::Setsid => Ready(Response::new( + self.on_setsid(fd_pid, awoken).map(|()| 0), + op, + )), ProcCall::SetResugid => Ready(Response::new( self.on_setresugid(fd_pid, payload).map(|()| 0), op, @@ -934,6 +936,7 @@ impl<'a> ProcScheme<'a> { } } fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { + log::trace!("GETPGID from {caller_pid:?} target {target_pid:?}"); let caller_proc = self .processes .get(&caller_pid) @@ -953,7 +956,7 @@ impl<'a> ProcScheme<'a> { Ok(target_proc.pgid) } - fn on_setsid(&mut self, caller_pid: ProcessId) -> Result<()> { + fn on_setsid(&mut self, caller_pid: ProcessId, awoken: &mut VecDeque) -> Result<()> { // TODO: more efficient? // POSIX: any other process's pgid matches the caller pid if self @@ -966,19 +969,28 @@ impl<'a> ProcScheme<'a> { let caller_proc_rc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; let mut caller_proc = caller_proc_rc.borrow_mut(); + let mut parent = (caller_proc.ppid != caller_pid) + .then(|| { + self.processes + .get(&caller_proc.ppid) + .map(|p| p.borrow_mut()) + }) + .ok_or(Error::new(ESRCH))?; // POSIX: already a process group leader if caller_proc.pgid == caller_pid { return Err(Error::new(EPERM)); } - caller_proc.sid = caller_pid; Self::set_pgid( caller_proc_rc, &mut *caller_proc, + parent.as_deref_mut(), &mut self.groups, caller_pid, + awoken, )?; + caller_proc.sid = caller_pid; // TODO: Remove controlling terminal Ok(()) @@ -1008,12 +1020,17 @@ impl<'a> ProcScheme<'a> { caller_pid: ProcessId, target_pid: ProcessId, new_pgid: ProcessId, + awoken: &mut VecDeque, ) -> Result<()> { - let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; + //let caller_proc = self.processes.get(&caller_pid).ok_or(Error::new(ESRCH))?; let proc_rc = self.processes.get(&target_pid).ok_or(Error::new(ESRCH))?; let mut proc = proc_rc.borrow_mut(); + let mut parent = (proc.ppid != target_pid) + .then(|| self.processes.get(&proc.ppid).map(|p| p.borrow_mut())) + .ok_or(Error::new(ESRCH))?; + if proc.pgid == new_pgid { return Ok(()); } @@ -1023,20 +1040,36 @@ impl<'a> ProcScheme<'a> { return Err(Error::new(EPERM)); } - // TODO: other security checks - Self::set_pgid(proc_rc, &mut *proc, &mut self.groups, new_pgid)?; + // TODO: other security checks? + Self::set_pgid( + proc_rc, + &mut *proc, + parent.as_deref_mut(), + &mut self.groups, + new_pgid, + awoken, + )?; Ok(()) } fn set_pgid( proc_rc: &Rc>, proc: &mut Process, + parent: Option<&mut Process>, groups: &mut HashMap>>, new_pgid: ProcessId, + awoken: &mut VecDeque, ) -> Result<()> { let old_pgid = proc.pgid; assert_ne!(old_pgid, new_pgid); + if let Some(parent) = parent { + // Some waitpid waiters may end up waiting for no children, if a child sets its pgid + // and the parent was waiting with a pgid filter. Ensure the waiter is awoken and + // possibly returns ECHILD. + awoken.extend(parent.waitpid_waiting.drain(..)); + } + let proc_weak = Rc::downgrade(&proc_rc); let shall_remove = { let mut old_group = groups.get(&old_pgid).ok_or(Error::new(ESRCH))?.borrow_mut(); @@ -1158,7 +1191,6 @@ impl<'a> ProcScheme<'a> { let proc_rc = self.processes.get(&this_pid).ok_or(Error::new(ESRCH))?; - log::trace!("WAITPID {target:?}"); log::trace!("PROCS {:#?}", self.processes); let mut proc_guard = proc_rc.borrow_mut(); @@ -1174,26 +1206,30 @@ impl<'a> ProcScheme<'a> { None } }; - let grim_reaper = |w_pid: ProcessId, status: WaitpidStatus| match status { - WaitpidStatus::Continued => { - if flags.contains(WaitFlags::WCONTINUED) { - Ready((w_pid.0, 0xffff)) - } else { - Pending + let mut grim_reaper = + |w_pid: ProcessId, status: WaitpidStatus, scheme: &mut ProcScheme| match status { + WaitpidStatus::Continued => { + if flags.contains(WaitFlags::WCONTINUED) { + Ready((w_pid.0, 0xffff)) + } else { + Pending + } } - } - WaitpidStatus::Stopped { signal } => { - if flags.contains(WaitFlags::WUNTRACED) { - Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) - } else { - Pending + WaitpidStatus::Stopped { signal } => { + if flags.contains(WaitFlags::WUNTRACED) { + Ready((w_pid.0, 0x7f | (i32::from(signal.get()) << 8))) + } else { + Pending + } } - } - WaitpidStatus::Terminated { signal, status } => Ready(( - w_pid.0, - i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), - )), - }; + WaitpidStatus::Terminated { signal, status } => { + scheme.reap(w_pid); + Ready(( + w_pid.0, + i32::from(signal.map_or(0, NonZeroU8::get)) | (i32::from(status) << 8), + )) + } + }; match target { WaitpidTarget::AnyChild | WaitpidTarget::AnyGroupMember => { @@ -1208,7 +1244,8 @@ impl<'a> ProcScheme<'a> { .map(|(k, v)| (*k, *v)); if let Some((wid, (w_pid, status))) = kv { let _ = proc.waitpid.remove(&wid); - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1232,11 +1269,15 @@ impl<'a> ProcScheme<'a> { }; if let ProcessStatus::Exited { status, signal } = target_proc.status { let _ = recv_nonblock(&mut proc.waitpid, &key); - grim_reaper(pid, WaitpidStatus::Terminated { signal, status }).map(Ok) + drop(proc_guard); + drop(target_proc); + grim_reaper(pid, WaitpidStatus::Terminated { signal, status }, self).map(Ok) } else { let res = recv_nonblock(&mut proc.waitpid, &key); if let Some((w_pid, status)) = res { - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + drop(target_proc); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1246,13 +1287,18 @@ impl<'a> ProcScheme<'a> { } } WaitpidTarget::ProcGroup(pgid) => { - let this_pgid = proc.pgid; - if !self - .processes - .iter() - .filter(|(pid, _)| **pid != this_pid) - .any(|(_, p)| p.borrow().pgid == this_pgid) - { + if let Some(group_rc) = self.groups.get(&pgid) { + let group = group_rc.borrow(); + if !group + .processes + .iter() + .filter_map(Weak::upgrade) + .filter(|r| !Rc::ptr_eq(r, proc_rc)) + .any(|p| p.borrow().ppid == this_pid) + { + return Ready(Err(Error::new(ECHILD))); + } + } else { return Ready(Err(Error::new(ECHILD))); } @@ -1262,7 +1308,8 @@ impl<'a> ProcScheme<'a> { }; if let Some(&(w_pid, status)) = proc.waitpid.get(&key) { let _ = proc.waitpid.remove(&key); - grim_reaper(w_pid, status).map(Ok) + drop(proc_guard); + grim_reaper(w_pid, status, self).map(Ok) } else if flags.contains(WaitFlags::WNOHANG) { Ready(Ok((0, 0))) } else { @@ -1272,6 +1319,38 @@ impl<'a> ProcScheme<'a> { } } } + fn reap(&mut self, pid: ProcessId) { + let Entry::Occupied(entry) = self.processes.entry(pid) else { + return; + }; + let pgid = { + let proc = entry.get().borrow(); + if !proc.threads.is_empty() { + log::error!( + "reaping process (pid {pid:?} with remaining threads: {:#?}", + proc.threads + ); + return; + } + proc.pgid + }; + let proc_rc = entry.remove(); + let proc_weak = Rc::downgrade(&proc_rc); + + let Entry::Occupied(group) = self.groups.entry(pgid) else { + log::error!("Process missing from its group"); + return; + }; + group + .get() + .borrow_mut() + .processes + .retain(|p| !Weak::ptr_eq(&proc_weak, p)); + if group.get().borrow_mut().processes.is_empty() { + group.remove(); + } + // TODO: notify parent's other waiters if ECHILD would now occur? + } fn on_setresugid(&mut self, pid: ProcessId, raw_buf: &[u8]) -> Result<()> { let [new_ruid, new_euid, new_suid, new_rgid, new_egid, new_sgid] = { let raw_ids: [u32; 6] = plain::slice_from_bytes::(raw_buf) @@ -1343,17 +1422,6 @@ impl<'a> ProcScheme<'a> { procs: &self.processes, } } - fn check_waitpid_queues( - &mut self, - waiter: ProcessId, - target: WaitpidTarget, - mask: WaitFlags, - ) -> Option<(ProcessId, i32)> { - /*match target { - //WaitpidTarget::SingleProc(target_pid) => , - }*/ - todo!() - } fn on_setrens(&mut self, pid: ProcessId, rns: Option, ens: Option) -> Result<()> { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; let mut process = proc_rc.borrow_mut(); @@ -1494,9 +1562,13 @@ impl<'a> ProcScheme<'a> { flags, mut op, } => { - log::trace!("WORKING ON AWAIT STS CHANGE"); + log::trace!("WAITPID {req_id:?}, {waiter:?}: {target:?} flags {flags:?}"); + let res = self.on_waitpid(waiter, target, flags, req_id); + log::trace!( + "WAITPID {req_id:?}, {waiter:?}: {target:?} flags {flags:?} -> {res:?}" + ); - match self.on_waitpid(waiter, target, flags, req_id) { + match res { Ready(Ok((pid, status))) => { if let Ok(status_out) = plain::from_mut_bytes::(op.payload()) { *status_out = status; @@ -1597,6 +1669,8 @@ impl<'a> ProcScheme<'a> { }; log::trace!("match group {match_grp:?}"); + let mut err_opt = None; + for (pid, proc_rc) in self.processes.iter() { if match_grp.map_or(false, |g| proc_rc.borrow().pgid != g) { continue; @@ -1612,12 +1686,15 @@ impl<'a> ProcScheme<'a> { ); match res { Ok(()) => (), - Err(err) if num_succeeded > 0 => break, - Err(err) => return Err(err), + Err(err) => err_opt = Some(err), } } - if killed_self { + if num_succeeded == 0 + && let Some(err) = err_opt + { + Err(err) + } else if killed_self { Err(Error::new(ERESTART)) } else { Ok(()) @@ -1946,7 +2023,7 @@ impl<'a> ProcScheme<'a> { } // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { - let _ = self.on_send_sig( + if let Err(err) = self.on_send_sig( INIT_PID, // caller, TODO? KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1954,7 +2031,9 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - ); + ) { + log::trace!("failed to SIGCHLD parent (SIGSTOP): {err}"); + } } } SendResult::SucceededSigcont { ppid, pgid } => { @@ -1976,7 +2055,7 @@ impl<'a> ProcScheme<'a> { // POSIX XSI allows but does not require SIGCONT to send signals to the parent. // TODO(err): Just ignore EINVAL (missing signal config), otherwise handle error? if ppid != INIT_PID { - let _ = self.on_send_sig( + if let Err(err) = self.on_send_sig( INIT_PID, // caller, TODO? KillTarget::Proc(ppid), SIGCHLD as u8, @@ -1984,7 +2063,9 @@ impl<'a> ProcScheme<'a> { KillMode::Idempotent, true, awoken, - ); + ) { + log::trace!("failed to SIGCHLD parent (SIGCONT): {err}"); + } } } } From ba823cf5f1fc52986e8c6a1c7f0cd2ffcec0c01c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 20:07:59 +0200 Subject: [PATCH 69/77] Add ProcCall::Getppid and implement reparenting. --- src/procmgr.rs | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 7e335248b7..4869a6de61 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -321,9 +321,9 @@ struct Process { rtqs: Vec>, } #[derive(Copy, Clone, Debug)] -pub struct WaitpidKey { - pub pid: Option, - pub pgid: Option, +struct WaitpidKey { + pid: Option, + pgid: Option, } // TODO: Is this valid? (transitive?) @@ -890,6 +890,10 @@ impl<'a> ProcScheme<'a> { op, )) } + ProcCall::Getppid => Ready(Response::new( + self.on_getppid(fd_pid).map(|ProcessId(p)| p), + op, + )), ProcCall::Setsid => Ready(Response::new( self.on_setsid(fd_pid, awoken).map(|()| 0), op, @@ -995,6 +999,17 @@ impl<'a> ProcScheme<'a> { // TODO: Remove controlling terminal Ok(()) } + fn on_getppid(&mut self, caller_pid: ProcessId) -> Result { + log::trace!("GETPPID {caller_pid:?}"); + let ppid = self + .processes + .get(&caller_pid) + .ok_or(Error::new(ESRCH))? + .borrow() + .ppid; + log::trace!("GETPPID {caller_pid:?} -> {ppid:?}"); + Ok(ppid) + } fn on_getsid(&mut self, caller_pid: ProcessId, req_pid: ProcessId) -> Result { let caller_proc = self .processes @@ -1528,10 +1543,25 @@ impl<'a> ProcScheme<'a> { state_entry.remove(); proc.status = ProcessStatus::Exited { signal, status }; + let (ppid, pgid) = (proc.ppid, proc.pgid); + drop(proc_guard); + if let Some(parent_rc) = self.processes.get(&ppid) { let mut parent = parent_rc.borrow_mut(); - // TODO(posix): transfer children to parent, and all of self.waitpid + + // Transfer children to parent (TODO: to init) + for child_rc in self + .processes + .values() + .filter(|p| !Rc::ptr_eq(p, parent_rc)) + .filter(|p| p.borrow().ppid == current_pid) + { + let mut child = child_rc.borrow_mut(); + child.ppid = ppid; + parent.waitpid.append(&mut child.waitpid); + } + parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -2165,12 +2195,12 @@ impl<'a> ProcScheme<'a> { } } #[derive(Clone, Copy, Debug)] -pub enum KillMode { +enum KillMode { Idempotent, Queued(RtSigInfo), } #[derive(Debug)] -pub enum KillTarget { +enum KillTarget { Proc(ProcessId), Thread(Rc>), } From 7fc0f04f0354154d4fea5b776990112795250871 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 22:07:40 +0200 Subject: [PATCH 70/77] Store process name. --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/procmgr.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7e41408e6c..c348ca3848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.4.0" @@ -18,6 +24,7 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" name = "bootstrap" version = "0.0.0" dependencies = [ + "arrayvec", "hashbrown", "linked_list_allocator", "log", diff --git a/Cargo.toml b/Cargo.toml index 4b2536203d..37064b84b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" slab = { version = "0.4.9", default-features = false } +arrayvec = { version = "0.7.6", default-features = false } [profile.release] panic = "abort" diff --git a/src/procmgr.rs b/src/procmgr.rs index 4869a6de61..1e37ea3ff7 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -5,6 +5,7 @@ use core::mem::size_of; use core::num::{NonZeroU8, NonZeroUsize}; use core::ops::Deref; use core::ptr::NonNull; +use core::str::FromStr; use core::sync::atomic::Ordering; use core::task::Poll::*; use core::task::{Context, Poll}; @@ -15,6 +16,7 @@ use alloc::rc::{Rc, Weak}; use alloc::vec; use alloc::vec::Vec; +use arrayvec::ArrayString; use hashbrown::hash_map::{Entry, OccupiedEntry, VacantEntry}; use hashbrown::{DefaultHashBuilder, HashMap, HashSet}; @@ -294,12 +296,15 @@ impl Drop for Page { } } +const NAME_CAPAC: usize = 32; + #[derive(Debug)] struct Process { threads: Vec>>, ppid: ProcessId, pgid: ProcessId, sid: ProcessId, + name: ArrayString, ruid: u32, euid: u32, @@ -546,6 +551,7 @@ impl<'a> ProcScheme<'a> { sgid: 0, rns: 1, ens: 1, + name: ArrayString::<32>::from_str("[init]").unwrap(), status: ProcessStatus::PossiblyRunnable, awaiting_threads_term: Vec::new(), @@ -588,6 +594,7 @@ impl<'a> ProcScheme<'a> { sgid, ens, rns, + name, .. } = *proc_guard.borrow(); @@ -603,6 +610,7 @@ impl<'a> ProcScheme<'a> { euid, egid, ens, + debug_name: arraystring_to_bytes(name), }, )?; let status_fd = FdGuard::new(syscall::dup( @@ -635,6 +643,7 @@ impl<'a> ProcScheme<'a> { sgid, rns, ens, + name, status: ProcessStatus::PossiblyRunnable, awaiting_threads_term: Vec::new(), @@ -675,6 +684,7 @@ impl<'a> ProcScheme<'a> { euid: proc.euid, egid: proc.egid, ens: proc.ens, + debug_name: arraystring_to_bytes(proc.name), }, )?; @@ -935,6 +945,10 @@ impl<'a> ProcScheme<'a> { self.on_sigdeq(fd_pid, payload).map(|()| 0), op, )), + ProcCall::Rename => Ready(Response::new( + self.on_proc_rename(fd_pid, payload).map(|()| 0), + op, + )), } } } @@ -2110,6 +2124,16 @@ impl<'a> ProcScheme<'a> { (buf.proc_control_addr % PAGE_SIZE) as u16, ]) } + fn on_proc_rename(&mut self, pid: ProcessId, new_name_raw: &[u8]) -> Result<()> { + let new_name = core::str::from_utf8(new_name_raw).map_err(|_| Error::new(EINVAL))?; + let mut proc = self + .processes + .get(&pid) + .ok_or(Error::new(ESRCH))? + .borrow_mut(); + proc.name = ArrayString::from_str(&new_name[..new_name.len().min(NAME_CAPAC)]).unwrap(); + Ok(()) + } fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { log::trace!("Sync tctl {:?}", thread.pid); let sigcontrol_fd = FdGuard::new(syscall::dup(*thread.fd, b"sighandler")?); @@ -2204,3 +2228,9 @@ enum KillTarget { Proc(ProcessId), Thread(Rc>), } +fn arraystring_to_bytes(s: ArrayString) -> [u8; C] { + let mut buf = [0_u8; C]; + let min = buf.len().min(s.len()); + buf[..min].copy_from_slice(&s.as_bytes()[..min]); + buf +} From 1b092a11d5d2052edff2dc283a96dd8f3d8af6f6 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 18 Apr 2025 22:44:11 +0200 Subject: [PATCH 71/77] Add /scheme/proc/ps handler for reading info. --- src/procmgr.rs | 121 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 15 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 1e37ea3ff7..403bb1cc2b 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -199,6 +199,7 @@ fn handle_scheme<'a>( ) -> Poll { match req.kind() { RequestKind::Call(req) => { + let caller = req.caller(); let req_id = VirtualId::KernelId(req.request_id()); let op = match req.op() { Ok(op) => op, @@ -206,11 +207,14 @@ fn handle_scheme<'a>( }; match op { Op::Open(op) => Ready(Response::open_dup_like( - scheme.on_open(op.path(), op.flags), + scheme.on_open(op.path(), op.flags, &caller), op, )), Op::Dup(op) => Ready(Response::open_dup_like(scheme.on_dup(op.fd, op.buf()), op)), - Op::Read(mut op) => Ready(Response::new(scheme.on_read(op.fd, op.buf()), op)), + Op::Read(mut op) => Ready(Response::new( + scheme.on_read(op.fd, op.offset, op.buf()), + op, + )), Op::Call(op) => scheme.on_call( { // TODO: cleanup @@ -224,9 +228,25 @@ fn handle_scheme<'a>( op, awoken, ), + Op::Fsize { req, fd } => { + if let Handle::Ps(ref b) = &scheme.handles[fd] { + Response::ready_ok(b.len(), req) + } else { + Response::ready_err(EOPNOTSUPP, req) + } + } + Op::Fstat(mut op) => { + if let Handle::Ps(ref b) = &scheme.handles[op.fd] { + op.buf().st_size = b.len() as _; + op.buf().st_mode = syscall::MODE_FILE | 0o444; + Response::ready_ok(0, op) + } else { + Response::ready_err(EOPNOTSUPP, op) + } + } _ => { log::trace!("UNKNOWN: {op:?}"); - Ready(Response::new(Err(Error::new(ENOSYS)), op)) + Response::ready_err(ENOSYS, op) } } } @@ -446,6 +466,9 @@ enum Handle { Init, Proc(ProcessId), Thread(Rc>), + + // TODO: stateless API, perhaps using intermediate daemon for providing a file-like API + Ps(Vec), } #[derive(Clone, Copy, Debug)] @@ -709,19 +732,30 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(ident, thread_weak); Ok(thread) } - fn on_open(&mut self, path: &str, flags: usize) -> Result { - if path == "init" { - if core::mem::replace(&mut self.init_claimed, true) { - return Err(Error::new(EEXIST)); + fn on_open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + let path = path.trim_start_matches('/'); + Ok(match path { + "init" => { + if core::mem::replace(&mut self.init_claimed, true) { + return Err(Error::new(EEXIST)); + } + OpenResult::ThisScheme { + number: self.handles.insert(Handle::Init), + flags: NewFdFlags::empty(), + } } - return Ok(OpenResult::ThisScheme { - number: self.handles.insert(Handle::Init), - flags: NewFdFlags::empty(), - }); - } - Err(Error::new(ENOENT)) + "ps" => { + let data = self.ps_data(ctx)?; + OpenResult::ThisScheme { + number: self.handles.insert(Handle::Ps(data)), + flags: NewFdFlags::POSITIONED, + } + } + + _ => return Err(Error::new(ENOENT)), + }) } - fn on_read(&mut self, id: usize, buf: &mut [u8]) -> Result { + fn on_read(&mut self, id: usize, offset: u64, buf: &mut [u8]) -> Result { match self.handles[id] { Handle::Proc(pid) => { let proc_rc = self.processes.get(&pid).ok_or(Error::new(EBADFD))?; @@ -744,6 +778,15 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(EINVAL))? = metadata; Ok(size_of::()) } + Handle::Ps(ref src_buf) => { + let src_buf = usize::try_from(offset) + .ok() + .and_then(|o| src_buf.get(o..)) + .unwrap_or(&[]); + let len = src_buf.len().min(buf.len()); + buf[..len].copy_from_slice(&src_buf[..len]); + Ok(len) + } Handle::Init | Handle::Thread(_) => return Err(Error::new(EBADF)), } } @@ -790,7 +833,7 @@ impl<'a> ProcScheme<'a> { fd: syscall::dup(*thread.fd, buf)?, }) } - Handle::Init => Err(Error::new(EBADF)), + Handle::Init | Handle::Ps(_) => Err(Error::new(EBADF)), } } fn on_call( @@ -951,6 +994,7 @@ impl<'a> ProcScheme<'a> { )), } } + Handle::Ps(_) => Response::ready_err(EOPNOTSUPP, op), } } fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { @@ -2217,6 +2261,53 @@ impl<'a> ProcScheme<'a> { } Some(still_true) } + fn ps_data(&mut self, _ctx: &CallerCtx) -> Result> { + // TODO: enforce uid == 0? + + let mut string = alloc::format!( + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", + "PID", + "PGID", + "PPID", + "SID", + "RUID", + "RGID", + "RNS", + "EUID", + "EGID", + "ENS", + "STATUS", + "NAME", + ); + for (pid, process_rc) in self.processes.iter() { + let process = process_rc.borrow(); + let status = match process.status { + ProcessStatus::PossiblyRunnable => "R", + ProcessStatus::Stopped(_) => "S", + ProcessStatus::Exiting { .. } => "E", + ProcessStatus::Exited { .. } => "X", + }; + use core::fmt::Write; + writeln!( + string, + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", + pid.0, + process.pgid.0, + process.ppid.0, + process.sid.0, + process.ruid, + process.rgid, + process.rns, + process.euid, + process.egid, + process.ens, + status, + process.name, + ) + .unwrap(); + } + Ok(string.into_bytes()) + } } #[derive(Clone, Copy, Debug)] enum KillMode { From c6de9f768c1fa8b2d9398251083925b65e411f52 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 00:10:23 +0200 Subject: [PATCH 72/77] Make thread fds Weak; reparent to init, not ppid. --- src/procmgr.rs | 77 +++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index 403bb1cc2b..cde4beb374 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -35,8 +35,8 @@ use syscall::schemev2::NewFdFlags; use syscall::{ sig_bit, ContextStatus, ContextVerb, CtxtStsBuf, Error, Event, EventFlags, FobtainFdFlags, MapFlags, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, - EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, EPERM, - ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, + EAGAIN, EBADF, EBADFD, ECHILD, EEXIST, EINTR, EINVAL, EIO, ENOENT, ENOSYS, EOPNOTSUPP, + EOWNERDEAD, EPERM, ERESTART, ESRCH, EWOULDBLOCK, O_CLOEXEC, O_CREAT, PAGE_SIZE, }; #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -465,7 +465,11 @@ enum WaitpidStatus { enum Handle { Init, Proc(ProcessId), - Thread(Rc>), + + // Needs to be weak so the thread is owned only by the process. Otherwise there would be a + // cyclic reference since the underlying context's file table almost certainly contains the + // thread fd itself, linked to this handle. + Thread(Weak>), // TODO: stateless API, perhaps using intermediate daemon for providing a file-like API Ps(Vec), @@ -805,7 +809,7 @@ impl<'a> ProcScheme<'a> { b"new-thread" => { let thread = self.new_thread(pid)?; Ok(OpenResult::ThisScheme { - number: self.handles.insert(Handle::Thread(thread)), + number: self.handles.insert(Handle::Thread(Rc::downgrade(&thread))), flags: NewFdFlags::empty(), }) } @@ -815,7 +819,7 @@ impl<'a> ProcScheme<'a> { .and_then(|s| s.parse::().ok()) .ok_or(Error::new(EINVAL))?; let process = self.processes.get(&pid).ok_or(Error::new(EBADFD))?.borrow(); - let thread = Rc::clone(process.threads.get(idx).ok_or(Error::new(ENOENT))?); + let thread = Rc::downgrade(process.threads.get(idx).ok_or(Error::new(ENOENT))?); return Ok(OpenResult::ThisScheme { number: self.handles.insert(Handle::Thread(thread)), @@ -824,7 +828,8 @@ impl<'a> ProcScheme<'a> { } _ => return Err(Error::new(EINVAL)), }, - Handle::Thread(ref thread_rc) => { + Handle::Thread(ref thread_weak) => { + let thread_rc = thread_weak.upgrade().ok_or(Error::new(EOWNERDEAD))?; let thread = thread_rc.borrow(); // By forwarding all dup calls to the kernel, this fd is now effectively the same @@ -846,7 +851,10 @@ impl<'a> ProcScheme<'a> { let (payload, metadata) = op.payload_and_metadata(); match self.handles[id] { Handle::Init => Response::ready_err(EBADF, op), - Handle::Thread(ref thr) => { + Handle::Thread(ref thr_weak) => { + let Some(thr) = thr_weak.upgrade() else { + return Response::ready_err(EOWNERDEAD, op); + }; let Some(verb) = ThreadCall::try_from_raw(metadata[0] as usize) else { return Response::ready_err(EINVAL, op); }; @@ -855,14 +863,11 @@ impl<'a> ProcScheme<'a> { Self::on_sync_sigtctl(&mut *thr.borrow_mut()).map(|()| 0), op, )), - ThreadCall::SignalThread => { - let thr = Rc::clone(thr); - Ready(Response::new( - self.on_kill_thread(&thr, metadata[1] as u8, awoken) - .map(|()| 0), - op, - )) - } + ThreadCall::SignalThread => Ready(Response::new( + self.on_kill_thread(&thr, metadata[1] as u8, awoken) + .map(|()| 0), + op, + )), } } Handle::Proc(fd_pid) => { @@ -1606,20 +1611,24 @@ impl<'a> ProcScheme<'a> { drop(proc_guard); if let Some(parent_rc) = self.processes.get(&ppid) { - let mut parent = parent_rc.borrow_mut(); - - // Transfer children to parent (TODO: to init) - for child_rc in self - .processes - .values() - .filter(|p| !Rc::ptr_eq(p, parent_rc)) - .filter(|p| p.borrow().ppid == current_pid) - { - let mut child = child_rc.borrow_mut(); - child.ppid = ppid; - parent.waitpid.append(&mut child.waitpid); + if let Some(init_rc) = self.processes.get(&INIT_PID) { + let mut init = init_rc.borrow_mut(); + // Transfer children to init + for child_rc in self + .processes + .values() + .filter(|p| !Rc::ptr_eq(p, init_rc)) + .filter(|p| p.borrow().ppid == current_pid) + { + let mut child = child_rc.borrow_mut(); + child.ppid = INIT_PID; + init.waitpid.append(&mut child.waitpid); + } + awoken.extend(init.waitpid_waiting.drain(..)); } + let mut parent = parent_rc.borrow_mut(); + parent.waitpid.insert( WaitpidKey { pid: Some(current_pid), @@ -2265,7 +2274,7 @@ impl<'a> ProcScheme<'a> { // TODO: enforce uid == 0? let mut string = alloc::format!( - "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}\n", "PID", "PGID", "PPID", @@ -2276,6 +2285,7 @@ impl<'a> ProcScheme<'a> { "EUID", "EGID", "ENS", + "NTHRD", "STATUS", "NAME", ); @@ -2290,7 +2300,7 @@ impl<'a> ProcScheme<'a> { use core::fmt::Write; writeln!( string, - "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", + "{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{:<16}", pid.0, process.pgid.0, process.ppid.0, @@ -2301,11 +2311,20 @@ impl<'a> ProcScheme<'a> { process.euid, process.egid, process.ens, + process.threads.len(), status, process.name, ) .unwrap(); } + + // Useful for debugging memory leaks. + log::trace!("NEXT FD: {}", { + let nextfd = syscall::dup(0, &[]).unwrap(); + syscall::close(nextfd); + nextfd + }); + Ok(string.into_bytes()) } } From 02e9a2a19c46eb863e6a97836b4ea0f2b0c5d627 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 12:53:06 +0200 Subject: [PATCH 73/77] Update redox_scheme. --- Cargo.lock | 3 ++- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c348ca3848..e133cfc084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,8 @@ dependencies = [ [[package]] name = "redox-scheme" version = "0.6.0" -source = "git+https://gitlab.redox-os.org/redox-os/redox-scheme.git#aee599d75a51cda40c23ca91689b1c92cc4a8557" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6469e083bf650071e9a80003c53171dfccc5ef17ae1d24972396c3c792e7c709" dependencies = [ "libredox", "redox_syscall", diff --git a/Cargo.toml b/Cargo.toml index 37064b84b5..fd7dcd39e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ log = { version = "0.4", default-features = false } plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox-scheme = { version = "0.6", default-features = false } #redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } redox-path = "0.3.1" From 10a41dbcf35e88c2ef8e58365e31bfb3d7c485ae Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 13:17:48 +0200 Subject: [PATCH 74/77] Synchronize kernel proc attrs properly. --- src/procmgr.rs | 56 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/procmgr.rs b/src/procmgr.rs index cde4beb374..299307e446 100644 --- a/src/procmgr.rs +++ b/src/procmgr.rs @@ -626,20 +626,6 @@ impl<'a> ProcScheme<'a> { } = *proc_guard.borrow(); let new_ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); - let attr_fd = FdGuard::new(syscall::dup( - *new_ctxt_fd, - alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), - )?); - let _ = syscall::write( - *attr_fd, - &ProcSchemeAttrs { - pid: child_pid.0 as u32, - euid, - egid, - ens, - debug_name: arraystring_to_bytes(name), - }, - )?; let status_fd = FdGuard::new(syscall::dup( *new_ctxt_fd, alloc::format!("auth-{}-status", **self.auth).as_bytes(), @@ -681,6 +667,12 @@ impl<'a> ProcScheme<'a> { sig_pctl: None, // TODO rtqs: Vec::new(), })); + if let Err(err) = new_process + .borrow_mut() + .sync_kernel_attrs(child_pid, self.auth) + { + log::warn!("Failed to set kernel attrs when forking: {err}"); + } if let Some(group) = self.groups.get(&pgid) { group @@ -700,6 +692,7 @@ impl<'a> ProcScheme<'a> { let ctxt_fd = FdGuard::new(syscall::dup(**self.auth, b"new-context")?); + // TODO: sync_kernel_attrs? let attr_fd = FdGuard::new(syscall::dup( *ctxt_fd, alloc::format!("auth-{}-attrs", **self.auth).as_bytes(), @@ -1478,6 +1471,9 @@ impl<'a> ProcScheme<'a> { if let Some(new_sgid) = new_sgid { proc.sgid = new_sgid; } + if let Err(err) = proc.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to sync proc attrs in setresugid: {err}"); + } Ok(()) } fn ancestors(&self, pid: ProcessId) -> impl Iterator + '_ { @@ -1553,10 +1549,14 @@ impl<'a> ProcScheme<'a> { if setrns { process.rns = rns.unwrap(); } - if setens { process.ens = ens.unwrap(); } + if setrns || setens { + if let Err(err) = process.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to sync kernel attrs in setrens: {err}"); + } + } Ok(()) } fn work_on( @@ -2185,6 +2185,9 @@ impl<'a> ProcScheme<'a> { .ok_or(Error::new(ESRCH))? .borrow_mut(); proc.name = ArrayString::from_str(&new_name[..new_name.len().min(NAME_CAPAC)]).unwrap(); + if let Err(err) = proc.sync_kernel_attrs(pid, self.auth) { + log::warn!("Failed to set kernel attrs when renaming proc: {err}"); + } Ok(()) } fn on_sync_sigtctl(thread: &mut Thread) -> Result<()> { @@ -2344,3 +2347,26 @@ fn arraystring_to_bytes(s: ArrayString) -> [u8; C] { buf[..min].copy_from_slice(&s.as_bytes()[..min]); buf } +impl Process { + fn sync_kernel_attrs(&mut self, my_pid: ProcessId, auth: &FdGuard) -> Result<()> { + // TODO: continue with other threads if one fails? + for thread_rc in &self.threads { + let thread = thread_rc.borrow(); + let attr_fd = FdGuard::new(syscall::dup( + *thread.fd, + alloc::format!("auth-{}-attrs", **auth).as_bytes(), + )?); + let _ = syscall::write( + *attr_fd, + &ProcSchemeAttrs { + pid: my_pid.0 as u32, + euid: self.euid, + egid: self.egid, + ens: self.ens, + debug_name: arraystring_to_bytes(self.name), + }, + )?; + } + Ok(()) + } +} From 47784aa56d45085b6959d4dd817ec395989b39a7 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 18:50:31 +0200 Subject: [PATCH 75/77] Update redox-rt. --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e133cfc084..5378f7e163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#e93311da1f12c942118cbde2b38643f574348606" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" dependencies = [ "bitflags", "generic-rt", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.11" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8a80d1f58bc92d8c0eb73a939420fb30fe1e85d0" +source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" dependencies = [ "bitflags", ] From 0c72bd778d0def87668a077c7b48b00d5b3b9c5c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 19:29:17 +0200 Subject: [PATCH 76/77] Use redox-os/syscall git dep. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5378f7e163..52ed00be1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#a2ab572c8b9f1de8592d68c632b9910a191034fc" +source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" dependencies = [ "bitflags", "generic-rt", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "redox_syscall" version = "0.5.11" -source = "git+https://gitlab.redox-os.org/4lDO2/syscall.git?branch=nuke_proc#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" +source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index fd7dcd39e3..be04431e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ lto = "fat" panic = "abort" [patch.crates-io] -redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "nuke_proc" } +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" } From 883b05b89981790719cadc6f9faf5fa7d87742f0 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 19 Apr 2025 20:10:04 +0200 Subject: [PATCH 77/77] Use redox-os/relibc git dep. --- Cargo.lock | 4 ++-- Cargo.toml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52ed00be1b..13ab2e20b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,7 +46,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" [[package]] name = "goblin" @@ -151,7 +151,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/4lDO2/relibc.git?branch=userspace_proc#9e73f93c5cd1bf4c7c2c2f530d2c0420737ad751" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#040009ab3d384e75d62b7e7dd3176d910203501a" dependencies = [ "bitflags", "generic-rt", diff --git a/Cargo.toml b/Cargo.toml index be04431e45..b41643737a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,7 @@ plain = "0.2" redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", default-features = false } redox_syscall = { version = "0.5.4", default-features = false } redox-scheme = { version = "0.6", default-features = false } -#redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false } -redox-rt = { git = "https://gitlab.redox-os.org/4lDO2/relibc.git", branch = "userspace_proc", default-features = false } +redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", default-features = false } redox-path = "0.3.1" slab = { version = "0.4.9", default-features = false } arrayvec = { version = "0.7.6", default-features = false }