From 083c444a6823530e77dc0eb45f990f078348807e Mon Sep 17 00:00:00 2001 From: Jeremy Soller <jackpot51@gmail.com> Date: Fri, 5 Jan 2018 20:31:15 -0700 Subject: [PATCH] Implement waitpid on PGID --- Cargo.lock | 4 +-- src/context/context.rs | 62 ++++++++++++++++++++++++++++++++++- src/context/mod.rs | 2 +- src/context/signal.rs | 32 ++++++++++-------- src/syscall/process.rs | 74 +++++++++++++++++++++++++++++------------- syscall | 2 +- 6 files changed, 136 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 564c1f61..f35f1798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,7 +135,7 @@ dependencies = [ "clippy 0.0.177 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.35", + "redox_syscall 0.1.37", "spin 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "x86 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -224,7 +224,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.35" +version = "0.1.37" [[package]] name = "regex" diff --git a/src/context/context.rs b/src/context/context.rs index 1ef168f8..f9c01ea4 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -1,6 +1,7 @@ use alloc::arc::Arc; use alloc::boxed::Box; use alloc::{BTreeMap, Vec, VecDeque}; +use core::cmp::Ordering; use core::mem; use spin::Mutex; @@ -27,6 +28,65 @@ pub enum Status { Exited(usize) } +#[derive(Copy, Clone, Debug)] +pub struct WaitpidKey { + pub pid: Option<ContextId>, + pub pgid: Option<ContextId>, +} + +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<Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialEq for WaitpidKey { + fn eq(&self, other: &WaitpidKey) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for WaitpidKey {} + /// A context, which identifies either a process or a thread #[derive(Debug)] pub struct Context { @@ -59,7 +119,7 @@ pub struct Context { /// Context is halting parent pub vfork: bool, /// Context is being waited on - pub waitpid: Arc<WaitMap<ContextId, usize>>, + pub waitpid: Arc<WaitMap<WaitpidKey, (ContextId, usize)>>, /// Context should handle pending signals pub pending: VecDeque<u8>, /// Context should wake up at specified time diff --git a/src/context/mod.rs b/src/context/mod.rs index dc4ef3f9..c715a7ec 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -5,7 +5,7 @@ use alloc::heap::Heap; use core::sync::atomic::Ordering; use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -pub use self::context::{Context, ContextId, Status}; +pub use self::context::{Context, ContextId, Status, WaitpidKey}; pub use self::list::ContextList; pub use self::switch::switch; diff --git a/src/context/signal.rs b/src/context/signal.rs index f51deb4f..ad70ca6c 100644 --- a/src/context/signal.rs +++ b/src/context/signal.rs @@ -1,7 +1,7 @@ use alloc::arc::Arc; use core::mem; -use context::{contexts, switch, Status}; +use context::{contexts, switch, Status, WaitpidKey}; use start::usermode; use syscall; use syscall::flag::{SIG_DFL, SIG_IGN, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU}; @@ -19,19 +19,19 @@ pub extern "C" fn signal_handler(sig: usize) { if handler == SIG_DFL { match sig { SIGCHLD => { - println!("SIGCHLD"); + // println!("SIGCHLD"); }, SIGCONT => { - println!("Continue"); + // println!("Continue"); { let contexts = contexts(); - let (pid, ppid) = { + let (pid, pgid, ppid) = { let context_lock = contexts.current().expect("context::signal_handler not inside of context"); let mut context = context_lock.write(); context.status = Status::Runnable; - (context.id, context.ppid) + (context.id, context.pgid, context.ppid) }; if let Some(parent_lock) = contexts.get(ppid) { @@ -40,23 +40,26 @@ pub extern "C" fn signal_handler(sig: usize) { Arc::clone(&parent.waitpid) }; - waitpid.send(pid, 0xFFFF); + waitpid.send(WaitpidKey { + pid: Some(pid), + pgid: Some(pgid) + }, (pid, 0xFFFF)); } else { println!("{}: {} not found for continue", pid.into(), ppid.into()); } } }, SIGSTOP | SIGTSTP | SIGTTIN | SIGTTOU => { - println!("Stop {}", sig); + // println!("Stop {}", sig); { let contexts = contexts(); - let (pid, ppid) = { + let (pid, pgid, ppid) = { let context_lock = contexts.current().expect("context::signal_handler not inside of context"); let mut context = context_lock.write(); context.status = Status::Stopped(sig); - (context.id, context.ppid) + (context.id, context.pgid, context.ppid) }; if let Some(parent_lock) = contexts.get(ppid) { @@ -65,7 +68,10 @@ pub extern "C" fn signal_handler(sig: usize) { Arc::clone(&parent.waitpid) }; - waitpid.send(pid, (sig << 8) | 0x7F); + waitpid.send(WaitpidKey { + pid: Some(pid), + pgid: Some(pgid) + }, (pid, (sig << 8) | 0x7F)); } else { println!("{}: {} not found for stop", pid.into(), ppid.into()); } @@ -74,14 +80,14 @@ pub extern "C" fn signal_handler(sig: usize) { unsafe { switch() }; }, _ => { - println!("Exit {}", sig); + // println!("Exit {}", sig); syscall::exit(sig); } } } else if handler == SIG_IGN { - println!("Ignore"); + // println!("Ignore"); } else { - println!("Call {:X}", handler); + // println!("Call {:X}", handler); unsafe { let mut sp = ::USER_SIGSTACK_OFFSET + ::USER_SIGSTACK_SIZE - 256; diff --git a/src/syscall/process.rs b/src/syscall/process.rs index b627d7b4..b64f3645 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -14,7 +14,7 @@ use paging::temporary_page::TemporaryPage; use start::usermode; use interrupt; use context; -use context::ContextId; +use context::{ContextId, WaitpidKey}; use context::file::FileDescriptor; #[cfg(not(feature="doc"))] use elf::{self, program_header}; @@ -22,7 +22,7 @@ use scheme::FileHandle; use syscall; use syscall::data::{SigAction, Stat}; use syscall::error::*; -use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, SIG_DFL, SIGCONT, SIGTERM, WCONTINUED, WNOHANG, WUNTRACED}; +use syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, SIG_DFL, SIGCONT, SIGTERM, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped}; use syscall::validate::{validate_slice, validate_slice_mut}; pub fn brk(address: usize) -> Result<usize> { @@ -863,10 +863,10 @@ pub fn exit(status: usize) -> ! { } } - // PPID must be grabbed after close, as context switches could change PPID if parent exits - let ppid = { + // PGID and PPID must be grabbed after close, as context switches could change PGID or PPID if parent exits + let (pgid, ppid) = { let context = context_lock.read(); - context.ppid + (context.pgid, context.ppid) }; // Transfer child processes to parent @@ -912,7 +912,11 @@ pub fn exit(status: usize) -> ! { for (c_pid, c_status) in children { waitpid.send(c_pid, c_status); } - waitpid.send(pid, status); + + waitpid.send(WaitpidKey { + pid: Some(pid), + pgid: Some(pgid) + }, (pid, status)); } else { println!("{}: {} not found for exit vfork unblock", pid.into(), ppid.into()); } @@ -965,8 +969,6 @@ pub fn getppid() -> Result<ContextId> { } pub fn kill(pid: ContextId, sig: usize) -> Result<usize> { - println!("Kill {} {}", pid.into() as isize, sig); - let (ruid, euid, current_pgid) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -1109,8 +1111,6 @@ pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mu } pub fn sigreturn() -> Result<usize> { - println!("Sigreturn"); - { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -1165,14 +1165,14 @@ pub fn waitpid(pid: ContextId, status_ptr: usize, flags: usize) -> Result<Contex }; let mut grim_reaper = |w_pid: ContextId, status: usize| -> Option<Result<ContextId>> { - if status == 0xFFFF { + if wifcontinued(status) { if flags & WCONTINUED == WCONTINUED { status_slice[0] = status; Some(Ok(w_pid)) } else { None } - } else if status & 0xFF == 0x7F { + } else if wifstopped(status) { if flags & WUNTRACED == WUNTRACED { status_slice[0] = status; Some(Ok(w_pid)) @@ -1188,17 +1188,36 @@ pub fn waitpid(pid: ContextId, status_ptr: usize, flags: usize) -> Result<Contex loop { let res_opt = if pid.into() == 0 { if flags & WNOHANG == WNOHANG { - if let Some((w_pid, status)) = waitpid.receive_any_nonblock() { + if let Some((_wid, (w_pid, status))) = waitpid.receive_any_nonblock() { grim_reaper(w_pid, status) } else { Some(Ok(ContextId::from(0))) } } else { - let (w_pid, status) = waitpid.receive_any(); + let (_wid, (w_pid, status)) = waitpid.receive_any(); + grim_reaper(w_pid, status) + } + } else if (pid.into() as isize) < 0 { + let pgid = ContextId::from(-(pid.into() as isize) as usize); + //TODO: Check for existence of child in process group PGID + if flags & WNOHANG == WNOHANG { + if let Some((w_pid, status)) = waitpid.receive_nonblock(&WaitpidKey { + pid: None, + pgid: Some(pgid) + }) { + grim_reaper(w_pid, status) + } else { + Some(Ok(ContextId::from(0))) + } + } else { + let (w_pid, status) = waitpid.receive(&WaitpidKey { + pid: None, + pgid: Some(pgid) + }); grim_reaper(w_pid, status) } } else { - let status = { + let hack_status = { let contexts = context::contexts(); let context_lock = contexts.get(pid).ok_or(Error::new(ECHILD))?; let mut context = context_lock.write(); @@ -1206,22 +1225,33 @@ pub fn waitpid(pid: ContextId, status_ptr: usize, flags: usize) -> Result<Contex println!("Hack for rustc - changing ppid of {} from {} to {}", context.id.into(), context.ppid.into(), ppid.into()); context.ppid = ppid; //return Err(Error::new(ECHILD)); + Some(context.status) + } else { + None } - context.status }; - if let context::Status::Exited(status) = status { - let _ = waitpid.receive_nonblock(&pid); + if let Some(context::Status::Exited(status)) = hack_status { + let _ = waitpid.receive_nonblock(&WaitpidKey { + pid: Some(pid), + pgid: None + }); grim_reaper(pid, status) } else if flags & WNOHANG == WNOHANG { - if let Some(status) = waitpid.receive_nonblock(&pid) { - grim_reaper(pid, status) + if let Some((w_pid, status)) = waitpid.receive_nonblock(&WaitpidKey { + pid: Some(pid), + pgid: None + }) { + grim_reaper(w_pid, status) } else { Some(Ok(ContextId::from(0))) } } else { - let status = waitpid.receive(&pid); - grim_reaper(pid, status) + let (w_pid, status) = waitpid.receive(&WaitpidKey { + pid: Some(pid), + pgid: None + }); + grim_reaper(w_pid, status) } }; diff --git a/syscall b/syscall index 8f012900..7c805d2a 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 8f012900589854cd54ac0a6edb9a3f2b0e017669 +Subproject commit 7c805d2a289ec19bf505256eb90395913d9f50b9 -- GitLab