From 57675c64e9c65e6cd9a636d4f928ff9a3abf87b6 Mon Sep 17 00:00:00 2001 From: Weidong Cui Date: Wed, 4 Feb 2026 05:41:26 +0000 Subject: [PATCH 1/4] Implement getpgrp syscall for bash support - Add Getpgrp variant to SyscallRequest enum in litebox_common_linux - Add syscall number mapping for Sysno::getpgrp - Implement sys_getpgrp() in litebox_shim_linux/syscalls/process.rs - Add dispatch case for SyscallRequest::Getpgrp in litebox_shim_linux - Add unit test for getpgrp syscall The getpgrp syscall returns the process group ID. This implementation returns the process ID, which is the default behavior for a process that hasn't explicitly joined another process group via setpgid. This is required for bash initialization. --- litebox_common_linux/src/lib.rs | 2 ++ litebox_shim_linux/src/lib.rs | 1 + litebox_shim_linux/src/syscalls/process.rs | 25 ++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/litebox_common_linux/src/lib.rs b/litebox_common_linux/src/lib.rs index 9108995d3..9faf0025a 100644 --- a/litebox_common_linux/src/lib.rs +++ b/litebox_common_linux/src/lib.rs @@ -2217,6 +2217,7 @@ pub enum SyscallRequest { }, Getpid, Getppid, + Getpgrp, Getuid, Geteuid, Getgid, @@ -2587,6 +2588,7 @@ impl SyscallRequest { Sysno::prlimit64 => sys_req!(Prlimit { pid, resource:?, new_limit:*, old_limit:* }), Sysno::getpid => SyscallRequest::Getpid, Sysno::getppid => SyscallRequest::Getppid, + Sysno::getpgrp => SyscallRequest::Getpgrp, Sysno::getuid => SyscallRequest::Getuid, Sysno::getgid => SyscallRequest::Getgid, Sysno::geteuid => SyscallRequest::Geteuid, diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index f4dccc729..8c874619e 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -1088,6 +1088,7 @@ impl Task { } SyscallRequest::Getpid => Ok(self.sys_getpid().reinterpret_as_unsigned() as usize), SyscallRequest::Getppid => Ok(self.sys_getppid().reinterpret_as_unsigned() as usize), + SyscallRequest::Getpgrp => Ok(self.sys_getpgrp().reinterpret_as_unsigned() as usize), SyscallRequest::Getuid => Ok(self.sys_getuid() as usize), SyscallRequest::Getgid => Ok(self.sys_getgid() as usize), SyscallRequest::Geteuid => Ok(self.sys_geteuid() as usize), diff --git a/litebox_shim_linux/src/syscalls/process.rs b/litebox_shim_linux/src/syscalls/process.rs index 4f0abf1e9..1b990c3e6 100644 --- a/litebox_shim_linux/src/syscalls/process.rs +++ b/litebox_shim_linux/src/syscalls/process.rs @@ -1148,10 +1148,22 @@ impl Task { self.pid } + /// Handle syscall `getppid`. pub(crate) fn sys_getppid(&self) -> i32 { self.ppid } + /// Handle syscall `getpgrp`. + /// + /// Returns the process group ID. For simplicity, this implementation returns + /// the process ID, which is the default behavior for a process that hasn't + /// explicitly joined another process group via `setpgid`. + pub(crate) fn sys_getpgrp(&self) -> i32 { + // In a full implementation, we'd track pgid separately. For now, return pid + // which is the default pgid for a new process. + self.pid + } + /// Handle syscall `getuid`. pub(crate) fn sys_getuid(&self) -> u32 { self.credentials.uid @@ -1606,4 +1618,17 @@ mod tests { "prctl get_name returned unexpected comm for too long name" ); } + + #[test] + fn test_getpgrp() { + let task = crate::syscalls::tests::init_platform(None); + + // getpgrp should return the pid (default behavior for new processes) + let pgrp = task.sys_getpgrp(); + let pid = task.sys_getpid(); + assert_eq!( + pgrp, pid, + "getpgrp should return pid for a new process that hasn't joined another process group" + ); + } } From 60514d0c18457758ff23b56a93d5af3096b5c24b Mon Sep 17 00:00:00 2001 From: Weidong Cui Date: Wed, 4 Feb 2026 14:46:02 +0000 Subject: [PATCH 2/4] Add TIOCGPGRP ioctl support for terminal process group queries - Add TIOCGPGRP constant (0x540F) to litebox_common_linux - Add TIOCGPGRP variant to IoctlArg enum - Add TIOCGPGRP to ioctl syscall parsing - Implement TIOCGPGRP handler in litebox_shim_linux/syscalls/file.rs TIOCGPGRP returns the process group ID of the foreground process group on the terminal. In LiteBox's simplified model, each process forms its own process group where PGID equals PID. This is required for bash shell initialization along with getpgrp syscall. --- litebox_common_linux/src/lib.rs | 4 ++++ litebox_shim_linux/src/syscalls/file.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/litebox_common_linux/src/lib.rs b/litebox_common_linux/src/lib.rs index 9faf0025a..d5e665c90 100644 --- a/litebox_common_linux/src/lib.rs +++ b/litebox_common_linux/src/lib.rs @@ -588,6 +588,7 @@ pub struct Winsize { pub const TCGETS: u32 = 0x5401; pub const TCSETS: u32 = 0x5402; +pub const TIOCGPGRP: u32 = 0x540F; pub const TIOCGWINSZ: u32 = 0x5413; pub const FIONBIO: u32 = 0x5421; pub const FIOCLEX: u32 = 0x5451; @@ -601,6 +602,8 @@ pub enum IoctlArg { TCGETS(Platform::RawMutPointer), /// Set the current serial port settings. TCSETS(Platform::RawConstPointer), + /// Get the process group ID of the foreground process group on the terminal. + TIOCGPGRP(Platform::RawMutPointer), /// Get window size. TIOCGWINSZ(Platform::RawMutPointer), /// Obtain device unit number, which can be used to generate @@ -2409,6 +2412,7 @@ impl SyscallRequest { match cmd { TCGETS => IoctlArg::TCGETS(ctx.sys_req_ptr(2)), TCSETS => IoctlArg::TCSETS(ctx.sys_req_ptr(2)), + TIOCGPGRP => IoctlArg::TIOCGPGRP(ctx.sys_req_ptr(2)), TIOCGWINSZ => IoctlArg::TIOCGWINSZ(ctx.sys_req_ptr(2)), TIOCGPTN => IoctlArg::TIOCGPTN(ctx.sys_req_ptr(2)), FIONBIO => IoctlArg::FIONBIO(ctx.sys_req_ptr(2)), diff --git a/litebox_shim_linux/src/syscalls/file.rs b/litebox_shim_linux/src/syscalls/file.rs index 8c7283712..0e0e89217 100644 --- a/litebox_shim_linux/src/syscalls/file.rs +++ b/litebox_shim_linux/src/syscalls/file.rs @@ -1275,6 +1275,15 @@ impl Task { Ok(0) } IoctlArg::TCSETS(_) => Ok(0), // TODO: implement + IoctlArg::TIOCGPGRP(pgrp) => { + // Get the process group ID of the foreground process group on the terminal. + // In LiteBox, processes are simplified: each process forms its own process group + // where the process group ID equals the process ID. This is a simplification + // compared to standard Unix where processes can explicitly join different groups. + let pid = self.sys_getpid(); + pgrp.write_at_offset(0, pid).ok_or(Errno::EFAULT)?; + Ok(0) + } IoctlArg::TIOCGWINSZ(ws) => { ws.write_at_offset( 0, @@ -1389,6 +1398,7 @@ impl Task { }, IoctlArg::TCGETS(..) | IoctlArg::TCSETS(..) + | IoctlArg::TIOCGPGRP(..) | IoctlArg::TIOCGPTN(..) | IoctlArg::TIOCGWINSZ(..) => match desc { Descriptor::LiteBoxRawFd(raw_fd) => files.run_on_raw_fd( From bf807a43742be964af1669feccbdbbc004fa4911 Mon Sep 17 00:00:00 2001 From: Weidong Cui Date: Thu, 5 Feb 2026 04:45:46 +0000 Subject: [PATCH 3/4] Add setpgid/getpgid syscalls and TIOCSPGRP ioctl for complete process group support Extends the partial getpgrp/TIOCGPGRP implementation with proper pgrp tracking per task, setpgid/getpgid syscalls, and TIOCSPGRP ioctl backed by a global foreground process group state. Co-Authored-By: Claude Opus 4.5 --- litebox_common_linux/src/lib.rs | 13 +++ litebox_shim_linux/src/lib.rs | 12 +++ litebox_shim_linux/src/syscalls/file.rs | 22 +++-- litebox_shim_linux/src/syscalls/process.rs | 98 ++++++++++++++++++++-- 4 files changed, 132 insertions(+), 13 deletions(-) diff --git a/litebox_common_linux/src/lib.rs b/litebox_common_linux/src/lib.rs index d5e665c90..ebc977899 100644 --- a/litebox_common_linux/src/lib.rs +++ b/litebox_common_linux/src/lib.rs @@ -589,6 +589,7 @@ pub struct Winsize { pub const TCGETS: u32 = 0x5401; pub const TCSETS: u32 = 0x5402; pub const TIOCGPGRP: u32 = 0x540F; +pub const TIOCSPGRP: u32 = 0x5410; pub const TIOCGWINSZ: u32 = 0x5413; pub const FIONBIO: u32 = 0x5421; pub const FIOCLEX: u32 = 0x5451; @@ -604,6 +605,8 @@ pub enum IoctlArg { TCSETS(Platform::RawConstPointer), /// Get the process group ID of the foreground process group on the terminal. TIOCGPGRP(Platform::RawMutPointer), + /// Set the foreground process group ID on the terminal. + TIOCSPGRP(Platform::RawConstPointer), /// Get window size. TIOCGWINSZ(Platform::RawMutPointer), /// Obtain device unit number, which can be used to generate @@ -2221,6 +2224,13 @@ pub enum SyscallRequest { Getpid, Getppid, Getpgrp, + Setpgid { + pid: i32, + pgid: i32, + }, + Getpgid { + pid: i32, + }, Getuid, Geteuid, Getgid, @@ -2413,6 +2423,7 @@ impl SyscallRequest { TCGETS => IoctlArg::TCGETS(ctx.sys_req_ptr(2)), TCSETS => IoctlArg::TCSETS(ctx.sys_req_ptr(2)), TIOCGPGRP => IoctlArg::TIOCGPGRP(ctx.sys_req_ptr(2)), + TIOCSPGRP => IoctlArg::TIOCSPGRP(ctx.sys_req_ptr(2)), TIOCGWINSZ => IoctlArg::TIOCGWINSZ(ctx.sys_req_ptr(2)), TIOCGPTN => IoctlArg::TIOCGPTN(ctx.sys_req_ptr(2)), FIONBIO => IoctlArg::FIONBIO(ctx.sys_req_ptr(2)), @@ -2593,6 +2604,8 @@ impl SyscallRequest { Sysno::getpid => SyscallRequest::Getpid, Sysno::getppid => SyscallRequest::Getppid, Sysno::getpgrp => SyscallRequest::Getpgrp, + Sysno::setpgid => sys_req!(Setpgid { pid, pgid }), + Sysno::getpgid => sys_req!(Getpgid { pid }), Sysno::getuid => SyscallRequest::Getuid, Sysno::getgid => SyscallRequest::Getgid, Sysno::geteuid => SyscallRequest::Geteuid, diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index 8c874619e..a4fa29f74 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -194,6 +194,7 @@ impl LinuxShimBuilder { boot_time: self.platform.now(), load_filter: self.load_filter, next_thread_id: 2.into(), // start from 2, as 1 is used by the main thread + fg_pgrp: core::sync::atomic::AtomicI32::new(1), litebox: self.litebox, unix_addr_table: litebox::sync::RwLock::new(syscalls::unix::UnixAddrTable::new()), }); @@ -234,6 +235,7 @@ impl LinuxShim { wait_state: wait::WaitState::new(self.0.platform), pid, ppid, + pgrp: Cell::new(pid), tid: pid, credentials: syscalls::process::Credentials { uid, @@ -1089,6 +1091,10 @@ impl Task { SyscallRequest::Getpid => Ok(self.sys_getpid().reinterpret_as_unsigned() as usize), SyscallRequest::Getppid => Ok(self.sys_getppid().reinterpret_as_unsigned() as usize), SyscallRequest::Getpgrp => Ok(self.sys_getpgrp().reinterpret_as_unsigned() as usize), + SyscallRequest::Setpgid { pid, pgid } => self.sys_setpgid(pid, pgid), + SyscallRequest::Getpgid { pid } => self + .sys_getpgid(pid) + .map(|v| v.reinterpret_as_unsigned() as usize), SyscallRequest::Getuid => Ok(self.sys_getuid() as usize), SyscallRequest::Getgid => Ok(self.sys_getgid() as usize), SyscallRequest::Geteuid => Ok(self.sys_geteuid() as usize), @@ -1162,6 +1168,8 @@ struct GlobalState { /// Next thread ID to assign. // TODO: better management of thread IDs next_thread_id: core::sync::atomic::AtomicI32, + /// Foreground process group ID for the terminal. + fg_pgrp: core::sync::atomic::AtomicI32, /// UNIX domain socket address table unix_addr_table: litebox::sync::RwLock, } @@ -1174,6 +1182,8 @@ struct Task { pid: i32, /// Parent Process ID ppid: i32, + /// Process group ID. Defaults to `pid`. + pgrp: Cell, /// Thread ID tid: i32, /// Task credentials. These are set per task but are Arc'd to save space @@ -1215,6 +1225,7 @@ mod test_utils { thread: syscalls::process::ThreadState::new_process(pid), pid, ppid: 0, + pgrp: Cell::new(pid), tid: pid, credentials: Arc::new(syscalls::process::Credentials { uid: 0, @@ -1244,6 +1255,7 @@ mod test_utils { thread: self.thread.new_thread(tid)?, pid: self.pid, ppid: self.ppid, + pgrp: self.pgrp.clone(), tid, credentials: self.credentials.clone(), comm: self.comm.clone(), diff --git a/litebox_shim_linux/src/syscalls/file.rs b/litebox_shim_linux/src/syscalls/file.rs index 0e0e89217..98a757942 100644 --- a/litebox_shim_linux/src/syscalls/file.rs +++ b/litebox_shim_linux/src/syscalls/file.rs @@ -1276,12 +1276,21 @@ impl Task { } IoctlArg::TCSETS(_) => Ok(0), // TODO: implement IoctlArg::TIOCGPGRP(pgrp) => { - // Get the process group ID of the foreground process group on the terminal. - // In LiteBox, processes are simplified: each process forms its own process group - // where the process group ID equals the process ID. This is a simplification - // compared to standard Unix where processes can explicitly join different groups. - let pid = self.sys_getpid(); - pgrp.write_at_offset(0, pid).ok_or(Errno::EFAULT)?; + let fg = self + .global + .fg_pgrp + .load(core::sync::atomic::Ordering::Relaxed); + pgrp.write_at_offset(0, fg).ok_or(Errno::EFAULT)?; + Ok(0) + } + IoctlArg::TIOCSPGRP(pgrp) => { + let pgid: i32 = pgrp.read_at_offset(0).ok_or(Errno::EFAULT)?; + if pgid <= 0 { + return Err(Errno::EINVAL); + } + self.global + .fg_pgrp + .store(pgid, core::sync::atomic::Ordering::Relaxed); Ok(0) } IoctlArg::TIOCGWINSZ(ws) => { @@ -1399,6 +1408,7 @@ impl Task { IoctlArg::TCGETS(..) | IoctlArg::TCSETS(..) | IoctlArg::TIOCGPGRP(..) + | IoctlArg::TIOCSPGRP(..) | IoctlArg::TIOCGPTN(..) | IoctlArg::TIOCGWINSZ(..) => match desc { Descriptor::LiteBoxRawFd(raw_fd) => files.run_on_raw_fd( diff --git a/litebox_shim_linux/src/syscalls/process.rs b/litebox_shim_linux/src/syscalls/process.rs index 1b990c3e6..181fcf243 100644 --- a/litebox_shim_linux/src/syscalls/process.rs +++ b/litebox_shim_linux/src/syscalls/process.rs @@ -752,6 +752,7 @@ impl Task { pid: self.pid, tid: child_tid, ppid: self.ppid, + pgrp: self.pgrp.clone(), credentials: self.credentials.clone(), comm: self.comm.clone(), fs: fs.into(), @@ -1154,14 +1155,32 @@ impl Task { } /// Handle syscall `getpgrp`. - /// - /// Returns the process group ID. For simplicity, this implementation returns - /// the process ID, which is the default behavior for a process that hasn't - /// explicitly joined another process group via `setpgid`. pub(crate) fn sys_getpgrp(&self) -> i32 { - // In a full implementation, we'd track pgid separately. For now, return pid - // which is the default pgid for a new process. - self.pid + self.pgrp.get() + } + + /// Handle syscall `getpgid`. + pub(crate) fn sys_getpgid(&self, pid: i32) -> Result { + if pid == 0 || pid == self.pid { + Ok(self.pgrp.get()) + } else { + Err(Errno::ESRCH) + } + } + + /// Handle syscall `setpgid`. + #[allow(clippy::similar_names)] + pub(crate) fn sys_setpgid(&self, pid: i32, pgid: i32) -> Result { + let target_pid = if pid == 0 { self.pid } else { pid }; + let target_pgid = if pgid == 0 { target_pid } else { pgid }; + if target_pgid < 0 { + return Err(Errno::EINVAL); + } + if target_pid != self.pid { + return Err(Errno::ESRCH); + } + self.pgrp.set(target_pgid); + Ok(0) } /// Handle syscall `getuid`. @@ -1630,5 +1649,70 @@ mod tests { pgrp, pid, "getpgrp should return pid for a new process that hasn't joined another process group" ); + + // After setpgid, getpgrp should reflect the new value + task.sys_setpgid(0, 42).expect("setpgid failed"); + assert_eq!(task.sys_getpgrp(), 42); + } + + #[test] + fn test_getpgid_self() { + let task = crate::syscalls::tests::init_platform(None); + let pid = task.sys_getpid(); + + // getpgid(0) should return own pgrp + assert_eq!(task.sys_getpgid(0).unwrap(), pid); + // getpgid(own_pid) should return own pgrp + assert_eq!(task.sys_getpgid(pid).unwrap(), pid); + } + + #[test] + fn test_getpgid_unknown() { + use crate::Errno; + let task = crate::syscalls::tests::init_platform(None); + + // getpgid for an unknown pid should return ESRCH + assert_eq!(task.sys_getpgid(99999), Err(Errno::ESRCH)); + } + + #[test] + fn test_setpgid() { + let task = crate::syscalls::tests::init_platform(None); + let pid = task.sys_getpid(); + + // setpgid(0, 0) is equivalent to setpgrp: sets pgrp to own pid + task.sys_setpgid(0, 0).expect("setpgid(0, 0) failed"); + assert_eq!(task.sys_getpgrp(), pid); + assert_eq!(task.sys_getpgid(0).unwrap(), pid); + + // setpgid(0, 42) sets pgrp to 42 + task.sys_setpgid(0, 42).expect("setpgid(0, 42) failed"); + assert_eq!(task.sys_getpgrp(), 42); + assert_eq!(task.sys_getpgid(pid).unwrap(), 42); + } + + #[test] + fn test_setpgid_invalid() { + use crate::Errno; + let task = crate::syscalls::tests::init_platform(None); + + // Negative pgid should return EINVAL + assert_eq!(task.sys_setpgid(0, -1), Err(Errno::EINVAL)); + + // Unknown pid should return ESRCH + assert_eq!(task.sys_setpgid(99999, 1), Err(Errno::ESRCH)); + } + + #[test] + fn test_pgrp_inherited() { + let task = crate::syscalls::tests::init_platform(None); + + // Set pgrp to a custom value + task.sys_setpgid(0, 42).expect("setpgid failed"); + assert_eq!(task.sys_getpgrp(), 42); + + // Clone and verify child inherits the pgrp + let child = task.clone_for_test().expect("clone_for_test failed"); + assert_eq!(child.sys_getpgrp(), 42); } } From 7f62435f4f709f657987b9f1a62a0452974ac93c Mon Sep 17 00:00:00 2001 From: Weidong Cui Date: Thu, 5 Feb 2026 05:10:10 +0000 Subject: [PATCH 4/4] Fix process group sharing and validation --- litebox_shim_linux/src/lib.rs | 10 ++++--- litebox_shim_linux/src/syscalls/process.rs | 31 +++++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index a4fa29f74..af78e0e52 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -235,7 +235,7 @@ impl LinuxShim { wait_state: wait::WaitState::new(self.0.platform), pid, ppid, - pgrp: Cell::new(pid), + pgrp: Arc::new(core::sync::atomic::AtomicI32::new(pid)), tid: pid, credentials: syscalls::process::Credentials { uid, @@ -250,6 +250,10 @@ impl LinuxShim { signals: syscalls::signal::SignalState::new_process(), }, }; + // Default foreground process group to the initial process. + self.0 + .fg_pgrp + .store(pid, core::sync::atomic::Ordering::Relaxed); entrypoints.task.load_program( loader::elf::ElfLoader::new(&entrypoints.task, path)?, argv, @@ -1183,7 +1187,7 @@ struct Task { /// Parent Process ID ppid: i32, /// Process group ID. Defaults to `pid`. - pgrp: Cell, + pgrp: Arc, /// Thread ID tid: i32, /// Task credentials. These are set per task but are Arc'd to save space @@ -1225,7 +1229,7 @@ mod test_utils { thread: syscalls::process::ThreadState::new_process(pid), pid, ppid: 0, - pgrp: Cell::new(pid), + pgrp: Arc::new(core::sync::atomic::AtomicI32::new(pid)), tid: pid, credentials: Arc::new(syscalls::process::Credentials { uid: 0, diff --git a/litebox_shim_linux/src/syscalls/process.rs b/litebox_shim_linux/src/syscalls/process.rs index 181fcf243..e8ec0eec4 100644 --- a/litebox_shim_linux/src/syscalls/process.rs +++ b/litebox_shim_linux/src/syscalls/process.rs @@ -1156,13 +1156,16 @@ impl Task { /// Handle syscall `getpgrp`. pub(crate) fn sys_getpgrp(&self) -> i32 { - self.pgrp.get() + self.pgrp.load(core::sync::atomic::Ordering::Relaxed) } /// Handle syscall `getpgid`. pub(crate) fn sys_getpgid(&self, pid: i32) -> Result { + if pid < 0 { + return Err(Errno::EINVAL); + } if pid == 0 || pid == self.pid { - Ok(self.pgrp.get()) + Ok(self.pgrp.load(core::sync::atomic::Ordering::Relaxed)) } else { Err(Errno::ESRCH) } @@ -1171,6 +1174,9 @@ impl Task { /// Handle syscall `setpgid`. #[allow(clippy::similar_names)] pub(crate) fn sys_setpgid(&self, pid: i32, pgid: i32) -> Result { + if pid < 0 { + return Err(Errno::EINVAL); + } let target_pid = if pid == 0 { self.pid } else { pid }; let target_pgid = if pgid == 0 { target_pid } else { pgid }; if target_pgid < 0 { @@ -1179,7 +1185,8 @@ impl Task { if target_pid != self.pid { return Err(Errno::ESRCH); } - self.pgrp.set(target_pgid); + self.pgrp + .store(target_pgid, core::sync::atomic::Ordering::Relaxed); Ok(0) } @@ -1671,6 +1678,9 @@ mod tests { use crate::Errno; let task = crate::syscalls::tests::init_platform(None); + // getpgid for a negative pid should return EINVAL + assert_eq!(task.sys_getpgid(-1), Err(Errno::EINVAL)); + // getpgid for an unknown pid should return ESRCH assert_eq!(task.sys_getpgid(99999), Err(Errno::ESRCH)); } @@ -1696,6 +1706,9 @@ mod tests { use crate::Errno; let task = crate::syscalls::tests::init_platform(None); + // Negative pid should return EINVAL + assert_eq!(task.sys_setpgid(-1, 1), Err(Errno::EINVAL)); + // Negative pgid should return EINVAL assert_eq!(task.sys_setpgid(0, -1), Err(Errno::EINVAL)); @@ -1715,4 +1728,16 @@ mod tests { let child = task.clone_for_test().expect("clone_for_test failed"); assert_eq!(child.sys_getpgrp(), 42); } + + #[test] + fn test_pgrp_shared_between_threads() { + let task = crate::syscalls::tests::init_platform(None); + let child = task.clone_for_test().expect("clone_for_test failed"); + + task.sys_setpgid(0, 42).expect("setpgid failed"); + assert_eq!(child.sys_getpgrp(), 42); + + task.sys_setpgid(0, 7).expect("setpgid failed"); + assert_eq!(child.sys_getpgrp(), 7); + } }