From 0a3b4dd1a196b8d916e9fc10dc3cdca8943070be Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 6 Jul 2017 23:14:52 -0400 Subject: [PATCH 01/15] Improve Wait Documentation --- src/sys/wait.rs | 66 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index ee0beade24..98088c977b 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -16,7 +16,9 @@ mod ffi { target_os = "android")))] libc_bitflags!( pub flags WaitPidFlag: c_int { + /// Returns immediately if no child has exited WNOHANG, + /// Returns if a child has been stopped, but isn't being traced by ptrace. WUNTRACED, } ); @@ -25,13 +27,24 @@ libc_bitflags!( target_os = "android"))] libc_bitflags!( pub flags WaitPidFlag: c_int { + /// Returns immediately if no child has exited WNOHANG, + /// Returns if a child has been stopped, but isn't being traced by ptrace. WUNTRACED, + /// Waits for children that have terminated. WEXITED, + /// Waits for previously stopped children that have been resumed with `SIGCONT`. WCONTINUED, - WNOWAIT, // Don't reap, just poll status. - __WNOTHREAD, // Don't wait on children of other threads in this group - __WALL, // Wait on all children, regardless of type + /// Leave the child in a waitable state; a later wait call can be used to again retrieve + /// the child status information. + WNOWAIT, + /// Don't wait on children of other threads in this group + __WNOTHREAD, + /// Wait for all children, regardless of type (clone or non-clone) + __WALL, + /// Wait for "clone" children only. If omitted then wait for "non-clone" children only. + /// (A "clone" child is one which delivers no signal, or a signal other than `SIGCHLD` to + /// its parent upon termination.) This option is ignored if `__WALL` is also specified. __WCLONE, } ); @@ -42,11 +55,15 @@ const WSTOPPED: WaitPidFlag = WUNTRACED; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum WaitStatus { + /// Signifies that the process has exited, providing the PID and associated exit status. Exited(Pid, i8), + /// Signifies that the process was killed by a signal, providing the PID and associated signal. Signaled(Pid, Signal, bool), + /// Signifies that the process was stopped by a signal, providing the PID and associated signal. Stopped(Pid, Signal), #[cfg(any(target_os = "linux", target_os = "android"))] PtraceEvent(Pid, Signal, c_int), + /// Signifies that the process received a `SIGCONT` signal, and thus continued. Continued(Pid), StillAlive } @@ -215,24 +232,53 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { } } -pub fn waitpid>>(pid: P, options: Option) -> Result { +/// Waits for and returns events that are received from the given supplied process or process group +/// ID, and associated options. +/// +/// # Usage Notes +/// +/// - If the value of PID is greater than `0`, it will wait on the child with that PID. +/// - If the value of the PID is less than `-1`, it will wait on any child that +/// belongs to the process group with the absolute value of the supplied PID. +/// - If the value of the PID is `0`, it will wait on any child process that has the same +/// group ID as the current process. +/// - If the value of the PID is `-1`, it will wait on any child process of the current process. +/// - If the value of the PID is `None`, the value of PID is set to `-1`. +/// +/// # Possible Error Values +/// +/// If this function returns an error, the error value will be one of the following: +/// +/// - **ECHILD**: The process does not exist or is not a child of the current process. +/// - This may also happen if a child process has the `SIGCHLD` signal masked or set to +/// `SIG_IGN`. +/// - **EINTR**: `WNOHANG` was not set and either an unblocked signal or a `SIGCHLD` was caught. +/// - **EINVAL**: The supplied options were invalid. +pub fn waitpid(pid: P, options: O) -> Result + where P: Into>, + O: Into> +{ use self::WaitStatus::*; let mut status: i32 = 0; - let option_bits = match options { - Some(bits) => bits.bits(), - None => 0 - }; + let option_bits = options.into().map_or(0, |bits| bits.bits()); - let res = unsafe { ffi::waitpid(pid.into().unwrap_or(Pid::from_raw(-1)).into(), &mut status as *mut c_int, option_bits) }; + let res = unsafe { + ffi::waitpid(pid.into().unwrap_or ( + Pid::from_raw(-1)).into(), + &mut status as *mut c_int, + option_bits + ) + }; - Ok(match try!(Errno::result(res)) { + Errno::result(res).map(|res| match res { 0 => StillAlive, res => decode(Pid::from_raw(res), status), }) } +/// Equivalent to `waitpid(-1, None)`, which waits on any child process of the current process. pub fn wait() -> Result { waitpid(None, None) } From 79c19d82410b2315a43ad11e38f336e6626070f5 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 03:26:12 -0400 Subject: [PATCH 02/15] Implement PidGroup & More Documentation --- src/sys/wait.rs | 110 ++++++++++++++++++++++++++++++++---------------- src/unistd.rs | 3 +- 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 98088c977b..f52a913f62 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -4,48 +4,41 @@ use unistd::Pid; use sys::signal::Signal; -mod ffi { - use libc::{pid_t, c_int}; - - extern { - pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int) -> pid_t; - } -} - -#[cfg(not(any(target_os = "linux", - target_os = "android")))] -libc_bitflags!( - pub flags WaitPidFlag: c_int { - /// Returns immediately if no child has exited - WNOHANG, - /// Returns if a child has been stopped, but isn't being traced by ptrace. - WUNTRACED, - } -); - -#[cfg(any(target_os = "linux", - target_os = "android"))] libc_bitflags!( + /// Defines optional flags for the `waitpid` function. pub flags WaitPidFlag: c_int { /// Returns immediately if no child has exited WNOHANG, /// Returns if a child has been stopped, but isn't being traced by ptrace. WUNTRACED, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Waits for children that have terminated. WEXITED, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Waits for previously stopped children that have been resumed with `SIGCONT`. WCONTINUED, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Leave the child in a waitable state; a later wait call can be used to again retrieve /// the child status information. WNOWAIT, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Don't wait on children of other threads in this group __WNOTHREAD, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Wait for all children, regardless of type (clone or non-clone) __WALL, + #[cfg(any(target_os = "linux", + target_os = "android"))] /// Wait for "clone" children only. If omitted then wait for "non-clone" children only. /// (A "clone" child is one which delivers no signal, or a signal other than `SIGCHLD` to /// its parent upon termination.) This option is ignored if `__WALL` is also specified. __WCLONE, + } ); @@ -54,6 +47,7 @@ libc_bitflags!( const WSTOPPED: WaitPidFlag = WUNTRACED; #[derive(Eq, PartialEq, Clone, Copy, Debug)] +/// Contains the status returned by the `wait` and `waitpid` functions. pub enum WaitStatus { /// Signifies that the process has exited, providing the PID and associated exit status. Exited(Pid, i8), @@ -65,6 +59,7 @@ pub enum WaitStatus { PtraceEvent(Pid, Signal, c_int), /// Signifies that the process received a `SIGCONT` signal, and thus continued. Continued(Pid), + /// if `WNOHANG` was set, this value is returned when no children have changed state. StillAlive } @@ -232,6 +227,55 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +/// Designates whether the supplied `Pid` value is a process ID, process group ID, +/// specifies any child of the current process's group ID, or any child of the current process. +pub enum PidGroup { + ProcessID(u32), + ProcessGroupID(u32), + AnyGroupChild, + AnyChild, +} + +impl From for PidGroup { + fn from(pid: Pid) -> PidGroup { + if pid > Pid::from_raw(0) { + PidGroup::ProcessID(i32::from(pid) as u32) + } else if pid < Pid::from_raw(-1) { + PidGroup::ProcessGroupID(-i32::from(pid) as u32) + } else if pid == Pid::from_raw(0) { + PidGroup::AnyGroupChild + } else { + PidGroup::AnyChild + } + } +} + +impl From for PidGroup { + fn from(pid: i32) -> PidGroup { + if pid > 0 { + PidGroup::ProcessID(pid as u32) + } else if pid < -1 { + PidGroup::ProcessGroupID(-pid as u32) + } else if pid == 0 { + PidGroup::AnyGroupChild + } else { + PidGroup::AnyChild + } + } +} + +impl Into for PidGroup { + fn into(self) -> i32 { + match self { + PidGroup::ProcessID(pid) => pid as i32, + PidGroup::ProcessGroupID(pid) => -(pid as i32), + PidGroup::AnyGroupChild => 0, + PidGroup::AnyChild => -1 + } + } +} + /// Waits for and returns events that are received from the given supplied process or process group /// ID, and associated options. /// @@ -254,31 +298,23 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { /// `SIG_IGN`. /// - **EINTR**: `WNOHANG` was not set and either an unblocked signal or a `SIGCHLD` was caught. /// - **EINVAL**: The supplied options were invalid. -pub fn waitpid(pid: P, options: O) -> Result - where P: Into>, - O: Into> +pub fn waitpid(pid: PidGroup, options: O) -> Result + where O: Into> { use self::WaitStatus::*; - let mut status: i32 = 0; - - let option_bits = options.into().map_or(0, |bits| bits.bits()); + let mut status = 0; + let options = options.into().map_or(0, |o| o.bits()); - let res = unsafe { - ffi::waitpid(pid.into().unwrap_or ( - Pid::from_raw(-1)).into(), - &mut status as *mut c_int, - option_bits - ) - }; + let res = unsafe { libc::waitpid(pid.into(), &mut status as *mut c_int, options) }; Errno::result(res).map(|res| match res { - 0 => StillAlive, + 0 => StillAlive, res => decode(Pid::from_raw(res), status), }) } -/// Equivalent to `waitpid(-1, None)`, which waits on any child process of the current process. +/// Waits on any child of the current process. pub fn wait() -> Result { - waitpid(None, None) + waitpid(PidGroup::AnyChild, None) } diff --git a/src/unistd.rs b/src/unistd.rs index 25237a07ef..b28cfa55c5 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -102,7 +102,7 @@ impl fmt::Display for Gid { /// /// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally /// passing wrong value. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd)] pub struct Pid(pid_t); impl Pid { @@ -236,6 +236,7 @@ pub fn setpgid(pid: Pid, pgid: Pid) -> Result<()> { let res = unsafe { libc::setpgid(pid.into(), pgid.into()) }; Errno::result(res).map(drop) } + #[inline] pub fn getpgid(pid: Option) -> Result { let res = unsafe { libc::getpgid(pid.unwrap_or(Pid(0)).into()) }; From ad4e1382e02e2bb4afd223745953372b0bc24718 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 03:36:10 -0400 Subject: [PATCH 03/15] PtraveEvent Documentation --- src/sys/wait.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index f52a913f62..be52140826 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -7,9 +7,12 @@ use sys::signal::Signal; libc_bitflags!( /// Defines optional flags for the `waitpid` function. pub flags WaitPidFlag: c_int { - /// Returns immediately if no child has exited + /// Do not suspend execution of the calling thread if the status is not immediately + /// available for one of the child processes specified by pid. WNOHANG, - /// Returns if a child has been stopped, but isn't being traced by ptrace. + /// The status of any child processes specified by pid that are stopped, and whose status + /// has not yet been reported since they stopped, shall also be reported to the requesting + /// process WUNTRACED, #[cfg(any(target_os = "linux", target_os = "android"))] @@ -17,7 +20,8 @@ libc_bitflags!( WEXITED, #[cfg(any(target_os = "linux", target_os = "android"))] - /// Waits for previously stopped children that have been resumed with `SIGCONT`. + /// Report the status of any continued child process specified by pid whose status has not + /// been reported since it continued from a job control stop. WCONTINUED, #[cfg(any(target_os = "linux", target_os = "android"))] @@ -51,11 +55,15 @@ const WSTOPPED: WaitPidFlag = WUNTRACED; pub enum WaitStatus { /// Signifies that the process has exited, providing the PID and associated exit status. Exited(Pid, i8), - /// Signifies that the process was killed by a signal, providing the PID and associated signal. + /// Signifies that the process was killed by a signal, providing the PID, the associated + /// signal, and a boolean value that is set to `true` when a core dump was produced. Signaled(Pid, Signal, bool), - /// Signifies that the process was stopped by a signal, providing the PID and associated signal. + /// Signifies that the process was stopped by a signal, providing the PID and associated + /// signal. Stopped(Pid, Signal), #[cfg(any(target_os = "linux", target_os = "android"))] + /// Signifies that the process was stopped due to a ptrace event, providing the PID, the + /// associated signal, and an integer that represents the status of the event. PtraceEvent(Pid, Signal, c_int), /// Signifies that the process received a `SIGCONT` signal, and thus continued. Continued(Pid), @@ -207,11 +215,12 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { cfg_if! { if #[cfg(any(target_os = "linux", target_os = "android"))] { fn decode_stopped(pid: Pid, status: i32) -> WaitStatus { - let status_additional = status::stop_additional(status); - if status_additional == 0 { - WaitStatus::Stopped(pid, status::stop_signal(status)) + let status = status::stop_additional(status); + let signal = status::stop_signal(status); + if status == 0 { + WaitStatus::Stopped(pid, signal) } else { - WaitStatus::PtraceEvent(pid, status::stop_signal(status), status::stop_additional(status)) + WaitStatus::PtraceEvent(pid, signal, status) } } } else { From 77e5d49173106b7b028feedf2cb9a8b84010b3c3 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 12:53:26 -0400 Subject: [PATCH 04/15] Fix Tests & Comments --- src/sys/wait.rs | 35 ++++++++++++++--------------------- test/sys/test_wait.rs | 4 ++-- test/test_unistd.rs | 4 ++-- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index be52140826..52a10edbaf 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,3 +1,5 @@ +//! This module contains the `wait()` and `waitpid()` functions, which are used + use libc::{self, c_int}; use {Errno, Result}; use unistd::Pid; @@ -14,40 +16,33 @@ libc_bitflags!( /// has not yet been reported since they stopped, shall also be reported to the requesting /// process WUNTRACED, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Waits for children that have terminated. + #[cfg(any(target_os = "linux", target_os = "android"))] WEXITED, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Report the status of any continued child process specified by pid whose status has not /// been reported since it continued from a job control stop. + #[cfg(any(target_os = "linux", target_os = "android"))] WCONTINUED, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Leave the child in a waitable state; a later wait call can be used to again retrieve /// the child status information. + #[cfg(any(target_os = "linux", target_os = "android"))] WNOWAIT, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Don't wait on children of other threads in this group + #[cfg(any(target_os = "linux", target_os = "android"))] __WNOTHREAD, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Wait for all children, regardless of type (clone or non-clone) + #[cfg(any(target_os = "linux", target_os = "android"))] __WALL, - #[cfg(any(target_os = "linux", - target_os = "android"))] /// Wait for "clone" children only. If omitted then wait for "non-clone" children only. /// (A "clone" child is one which delivers no signal, or a signal other than `SIGCHLD` to /// its parent upon termination.) This option is ignored if `__WALL` is also specified. + #[cfg(any(target_os = "linux", target_os = "android"))] __WCLONE, } ); -#[cfg(any(target_os = "linux", - target_os = "android"))] +#[cfg(any(target_os = "linux",target_os = "android"))] const WSTOPPED: WaitPidFlag = WUNTRACED; #[derive(Eq, PartialEq, Clone, Copy, Debug)] @@ -61,18 +56,17 @@ pub enum WaitStatus { /// Signifies that the process was stopped by a signal, providing the PID and associated /// signal. Stopped(Pid, Signal), - #[cfg(any(target_os = "linux", target_os = "android"))] /// Signifies that the process was stopped due to a ptrace event, providing the PID, the /// associated signal, and an integer that represents the status of the event. + #[cfg(any(target_os = "linux", target_os = "android"))] PtraceEvent(Pid, Signal, c_int), /// Signifies that the process received a `SIGCONT` signal, and thus continued. Continued(Pid), - /// if `WNOHANG` was set, this value is returned when no children have changed state. + /// If `WNOHANG` was set, this value is returned when no children have changed state. StillAlive } -#[cfg(any(target_os = "linux", - target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] mod status { use sys::signal::Signal; use libc::c_int; @@ -114,8 +108,7 @@ mod status { } } -#[cfg(any(target_os = "macos", - target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios"))] mod status { use sys::signal::{Signal,SIGCONT}; @@ -236,9 +229,9 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] /// Designates whether the supplied `Pid` value is a process ID, process group ID, /// specifies any child of the current process's group ID, or any child of the current process. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum PidGroup { ProcessID(u32), ProcessGroupID(u32), diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 2e28d9e78f..d181fb9700 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -13,7 +13,7 @@ fn test_wait_signal() { Ok(Child) => pause().unwrap_or(()), Ok(Parent { child }) => { kill(child, Some(SIGKILL)).ok().expect("Error: Kill Failed"); - assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); + assert_eq!(waitpid(child.into(), None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); }, // panic, fork should never fail unless there is a serious problem with the OS Err(_) => panic!("Error: Fork Failed") @@ -28,7 +28,7 @@ fn test_wait_exit() { match fork() { Ok(Child) => unsafe { exit(12); }, Ok(Parent { child }) => { - assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); + assert_eq!(waitpid(child.into(), None), Ok(WaitStatus::Exited(child, 12))); }, // panic, fork should never fail unless there is a serious problem with the OS Err(_) => panic!("Error: Fork Failed") diff --git a/test/test_unistd.rs b/test/test_unistd.rs index fd4e327b56..ea05fe06d7 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -25,7 +25,7 @@ fn test_fork_and_waitpid() { // assert that child was created and pid > 0 let child_raw: ::libc::pid_t = child.into(); assert!(child_raw > 0); - let wait_status = waitpid(child, None); + let wait_status = waitpid(child.into(), None); match wait_status { // assert that waitpid returned correct status and the pid is the one of the child Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child), @@ -135,7 +135,7 @@ macro_rules! execve_test_factory( }, Parent { child } => { // Wait for the child to exit. - waitpid(child, None).unwrap(); + waitpid(child.into(), None).unwrap(); // Read 1024 bytes. let mut buf = [0u8; 1024]; read(reader, &mut buf).unwrap(); From 8607d9386a69568dd3d20fd2eca3e6044de1d917 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 13:42:54 -0400 Subject: [PATCH 05/15] Require Runtime Checks on `Pid` Values in `PidGroup` --- src/sys/wait.rs | 37 ++++++++++++------------------------- test/sys/test_wait.rs | 4 ++-- test/test_unistd.rs | 4 ++-- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 52a10edbaf..fd8d288644 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,6 +1,6 @@ //! This module contains the `wait()` and `waitpid()` functions, which are used -use libc::{self, c_int}; +use libc::{self, c_int, pid_t}; use {Errno, Result}; use unistd::Pid; @@ -233,32 +233,18 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { /// specifies any child of the current process's group ID, or any child of the current process. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum PidGroup { - ProcessID(u32), - ProcessGroupID(u32), + ProcessID(Pid), + ProcessGroupID(Pid), AnyGroupChild, AnyChild, } -impl From for PidGroup { - fn from(pid: Pid) -> PidGroup { - if pid > Pid::from_raw(0) { - PidGroup::ProcessID(i32::from(pid) as u32) - } else if pid < Pid::from_raw(-1) { - PidGroup::ProcessGroupID(-i32::from(pid) as u32) - } else if pid == Pid::from_raw(0) { - PidGroup::AnyGroupChild - } else { - PidGroup::AnyChild - } - } -} - impl From for PidGroup { fn from(pid: i32) -> PidGroup { if pid > 0 { - PidGroup::ProcessID(pid as u32) + PidGroup::ProcessID(Pid::from_raw(pid)) } else if pid < -1 { - PidGroup::ProcessGroupID(-pid as u32) + PidGroup::ProcessGroupID(Pid::from_raw(pid)) } else if pid == 0 { PidGroup::AnyGroupChild } else { @@ -267,13 +253,14 @@ impl From for PidGroup { } } -impl Into for PidGroup { - fn into(self) -> i32 { +impl Into for PidGroup { + fn into(self) -> pid_t { match self { - PidGroup::ProcessID(pid) => pid as i32, - PidGroup::ProcessGroupID(pid) => -(pid as i32), - PidGroup::AnyGroupChild => 0, - PidGroup::AnyChild => -1 + PidGroup::ProcessID(pid) if pid < Pid::from_raw(-1) => -pid_t::from(pid), + PidGroup::ProcessGroupID(pid) if pid > Pid::from_raw(0) => -pid_t::from(pid), + PidGroup::ProcessID(pid) | PidGroup::ProcessGroupID(pid) => pid_t::from(pid), + PidGroup::AnyGroupChild => 0, + PidGroup::AnyChild => -1, } } } diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index d181fb9700..eff566dc02 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -13,7 +13,7 @@ fn test_wait_signal() { Ok(Child) => pause().unwrap_or(()), Ok(Parent { child }) => { kill(child, Some(SIGKILL)).ok().expect("Error: Kill Failed"); - assert_eq!(waitpid(child.into(), None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); + assert_eq!(waitpid(PidGroup::ProcessID(child), None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); }, // panic, fork should never fail unless there is a serious problem with the OS Err(_) => panic!("Error: Fork Failed") @@ -28,7 +28,7 @@ fn test_wait_exit() { match fork() { Ok(Child) => unsafe { exit(12); }, Ok(Parent { child }) => { - assert_eq!(waitpid(child.into(), None), Ok(WaitStatus::Exited(child, 12))); + assert_eq!(waitpid(PidGroup::ProcessID(child), None), Ok(WaitStatus::Exited(child, 12))); }, // panic, fork should never fail unless there is a serious problem with the OS Err(_) => panic!("Error: Fork Failed") diff --git a/test/test_unistd.rs b/test/test_unistd.rs index ea05fe06d7..29186d2f0a 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -25,7 +25,7 @@ fn test_fork_and_waitpid() { // assert that child was created and pid > 0 let child_raw: ::libc::pid_t = child.into(); assert!(child_raw > 0); - let wait_status = waitpid(child.into(), None); + let wait_status = waitpid(PidGroup::ProcessID(child), None); match wait_status { // assert that waitpid returned correct status and the pid is the one of the child Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child), @@ -135,7 +135,7 @@ macro_rules! execve_test_factory( }, Parent { child } => { // Wait for the child to exit. - waitpid(child.into(), None).unwrap(); + waitpid(PidGroup::ProcessID(child), None).unwrap(); // Read 1024 bytes. let mut buf = [0u8; 1024]; read(reader, &mut buf).unwrap(); From 384fb04ac90c3752f062da6a37f94f57a7e9b826 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 16:59:46 -0400 Subject: [PATCH 06/15] Remove Into for PidGroup --- src/sys/wait.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index fd8d288644..4efcd0d43b 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -253,18 +253,6 @@ impl From for PidGroup { } } -impl Into for PidGroup { - fn into(self) -> pid_t { - match self { - PidGroup::ProcessID(pid) if pid < Pid::from_raw(-1) => -pid_t::from(pid), - PidGroup::ProcessGroupID(pid) if pid > Pid::from_raw(0) => -pid_t::from(pid), - PidGroup::ProcessID(pid) | PidGroup::ProcessGroupID(pid) => pid_t::from(pid), - PidGroup::AnyGroupChild => 0, - PidGroup::AnyChild => -1, - } - } -} - /// Waits for and returns events that are received from the given supplied process or process group /// ID, and associated options. /// @@ -295,6 +283,14 @@ pub fn waitpid(pid: PidGroup, options: O) -> Result let mut status = 0; let options = options.into().map_or(0, |o| o.bits()); + let pid = match pid { + PidGroup::ProcessID(pid) if pid < Pid::from_raw(-1) => -pid_t::from(pid), + PidGroup::ProcessGroupID(pid) if pid > Pid::from_raw(0) => -pid_t::from(pid), + PidGroup::ProcessID(pid) | PidGroup::ProcessGroupID(pid) => pid_t::from(pid), + PidGroup::AnyGroupChild => 0, + PidGroup::AnyChild => -1, + }; + let res = unsafe { libc::waitpid(pid.into(), &mut status as *mut c_int, options) }; Errno::result(res).map(|res| match res { From ed82b310cd729248dcaa59f7a039f508a883f9d2 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 7 Jul 2017 22:54:46 -0400 Subject: [PATCH 07/15] More Documentation Updates --- src/sys/wait.rs | 73 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 4efcd0d43b..cb22340211 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,4 +1,32 @@ -//! This module contains the `wait()` and `waitpid()` functions, which are used +//! This module contains the `wait()` and `waitpid()` functions, which are used to wait on and +//! obtain status information from child processes. These provide more granular control over +//! child management than the primitives provided by Rust's standard library, and are critical +//! in the creation of shells and managing jobs on *nix platforms. +//! +//! # Example +//! +//! ```rust +//! use nix::sys::wait::*; +//! loop { +//! match waitpid(PidGroup::ProcessGroupID(pid), WUNTRACED) { +//! Ok(WaitStatus::Exited(pid, status)) => { +//! eprintln!("Process '{}' exited with status '{}'", pid, status); +//! break +//! }, +//! Ok(WaitStatus::Stopped(pid, signal)) => { +//! eprintln!("Process '{}' stopped with signal '{}'", pid, signal); +//! }, +//! Ok(WaitStatus::Continued(pid)) => { +//! eprintln!("Process '{}' continued", pid); +//! }, +//! Ok(_) => (), +//! Err(why) => { +//! eprintln!("waitpid returned an error code: {}" why); +//! break +//! } +//! } +//! } +//! ``` use libc::{self, c_int, pid_t}; use {Errno, Result}; @@ -17,32 +45,32 @@ libc_bitflags!( /// process WUNTRACED, /// Waits for children that have terminated. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] WEXITED, /// Report the status of any continued child process specified by pid whose status has not /// been reported since it continued from a job control stop. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] WCONTINUED, /// Leave the child in a waitable state; a later wait call can be used to again retrieve /// the child status information. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] WNOWAIT, /// Don't wait on children of other threads in this group - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] __WNOTHREAD, /// Wait for all children, regardless of type (clone or non-clone) - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] __WALL, /// Wait for "clone" children only. If omitted then wait for "non-clone" children only. /// (A "clone" child is one which delivers no signal, or a signal other than `SIGCHLD` to /// its parent upon termination.) This option is ignored if `__WALL` is also specified. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] __WCLONE, } ); -#[cfg(any(target_os = "linux",target_os = "android"))] +#[cfg(any(target_os = "android", target_os = "linux"))] const WSTOPPED: WaitPidFlag = WUNTRACED; #[derive(Eq, PartialEq, Clone, Copy, Debug)] @@ -58,7 +86,7 @@ pub enum WaitStatus { Stopped(Pid, Signal), /// Signifies that the process was stopped due to a ptrace event, providing the PID, the /// associated signal, and an integer that represents the status of the event. - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "android", target_os = "linux"))] PtraceEvent(Pid, Signal, c_int), /// Signifies that the process received a `SIGCONT` signal, and thus continued. Continued(Pid), @@ -66,7 +94,7 @@ pub enum WaitStatus { StillAlive } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "android", target_os = "linux"))] mod status { use sys::signal::Signal; use libc::c_int; @@ -231,11 +259,16 @@ fn decode(pid : Pid, status: i32) -> WaitStatus { /// Designates whether the supplied `Pid` value is a process ID, process group ID, /// specifies any child of the current process's group ID, or any child of the current process. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PidGroup { + /// Signifies that the `Pid` is supposed to represent a process ID. ProcessID(Pid), + /// Signifies that the `Pid` is supposed to represent a process group ID. ProcessGroupID(Pid), + /// Signifies that any child process that belongs to the process group of the current process + /// should be selected. AnyGroupChild, + /// Signifies that any child process that belongs to the current process should be selected. AnyChild, } @@ -258,13 +291,14 @@ impl From for PidGroup { /// /// # Usage Notes /// -/// - If the value of PID is greater than `0`, it will wait on the child with that PID. -/// - If the value of the PID is less than `-1`, it will wait on any child that -/// belongs to the process group with the absolute value of the supplied PID. -/// - If the value of the PID is `0`, it will wait on any child process that has the same -/// group ID as the current process. -/// - If the value of the PID is `-1`, it will wait on any child process of the current process. -/// - If the value of the PID is `None`, the value of PID is set to `-1`. +/// - If the value of the PID is `PidGroup::ProcessID(Pid)`, it will wait on the child +/// with that has the specified process ID. +/// - If the value of the PID is `PidGroup::ProcessGroupID(Pid)`, it will wait on any child that +/// belongs to the specified process group ID. +/// - If the value of the PID is `PidGroup::AnyGroupChild`, it will wait on any child process +/// that has the same group ID as the current process. +/// - If the value of the PID is `PidGroup::AnyChild`, it will wait on any child process of the +/// current process. /// /// # Possible Error Values /// @@ -299,7 +333,8 @@ pub fn waitpid(pid: PidGroup, options: O) -> Result }) } -/// Waits on any child of the current process. +/// Waits on any child of the current process, returning on events that change the status of +/// of that child. It is directly equivalent to `waitpid(PidGroup::AnyChild, None)`. pub fn wait() -> Result { waitpid(PidGroup::AnyChild, None) } From fb69fbbfdd7889785d7ba2e11df3e780815d6e3b Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 20:45:33 -0400 Subject: [PATCH 08/15] Fix Document Build Errors --- src/sys/wait.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index cb22340211..92820f77a3 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -10,18 +10,18 @@ //! loop { //! match waitpid(PidGroup::ProcessGroupID(pid), WUNTRACED) { //! Ok(WaitStatus::Exited(pid, status)) => { -//! eprintln!("Process '{}' exited with status '{}'", pid, status); +//! println!("Process '{}' exited with status '{}'", pid, status); //! break //! }, //! Ok(WaitStatus::Stopped(pid, signal)) => { -//! eprintln!("Process '{}' stopped with signal '{}'", pid, signal); +//! println!("Process '{}' stopped with signal '{}'", pid, signal); //! }, //! Ok(WaitStatus::Continued(pid)) => { -//! eprintln!("Process '{}' continued", pid); +//! println!("Process '{}' continued", pid); //! }, //! Ok(_) => (), //! Err(why) => { -//! eprintln!("waitpid returned an error code: {}" why); +//! println!("waitpid returned an error code: {}" why); //! break //! } //! } @@ -319,16 +319,16 @@ pub fn waitpid(pid: PidGroup, options: O) -> Result let pid = match pid { PidGroup::ProcessID(pid) if pid < Pid::from_raw(-1) => -pid_t::from(pid), - PidGroup::ProcessGroupID(pid) if pid > Pid::from_raw(0) => -pid_t::from(pid), + PidGroup::ProcessGroupID(pid) if pid > Pid::from_raw(0) => -pid_t::from(pid), PidGroup::ProcessID(pid) | PidGroup::ProcessGroupID(pid) => pid_t::from(pid), PidGroup::AnyGroupChild => 0, - PidGroup::AnyChild => -1, + PidGroup::AnyChild => -1, }; let res = unsafe { libc::waitpid(pid.into(), &mut status as *mut c_int, options) }; Errno::result(res).map(|res| match res { - 0 => StillAlive, + 0 => StillAlive, res => decode(Pid::from_raw(res), status), }) } From 519303cca4b1b4a6991be7d0b8b2ef4bbbe234ab Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 20:51:04 -0400 Subject: [PATCH 09/15] Remove WSTOPPED --- src/sys/wait.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 92820f77a3..9b196cebb4 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -70,9 +70,6 @@ libc_bitflags!( } ); -#[cfg(any(target_os = "android", target_os = "linux"))] -const WSTOPPED: WaitPidFlag = WUNTRACED; - #[derive(Eq, PartialEq, Clone, Copy, Debug)] /// Contains the status returned by the `wait` and `waitpid` functions. pub enum WaitStatus { From ccfa6ee3999b3c867de644b1bd9663ac523c681c Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 20:54:47 -0400 Subject: [PATCH 10/15] Fix Document Build Errors --- src/sys/wait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 9b196cebb4..d6cd09d42c 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -21,7 +21,7 @@ //! }, //! Ok(_) => (), //! Err(why) => { -//! println!("waitpid returned an error code: {}" why); +//! println!("waitpid returned an error code: {}", why); //! break //! } //! } From 224d36d768c18112e4a841c7db5d3194db7f70eb Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 21:15:55 -0400 Subject: [PATCH 11/15] More Doc Build Fixes --- src/sys/wait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index d6cd09d42c..8584e0e65b 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -14,7 +14,7 @@ //! break //! }, //! Ok(WaitStatus::Stopped(pid, signal)) => { -//! println!("Process '{}' stopped with signal '{}'", pid, signal); +//! println!("Process '{}' stopped with signal '{:?}'", pid, signal); //! }, //! Ok(WaitStatus::Continued(pid)) => { //! println!("Process '{}' continued", pid); From 0711f21f02099b91109711790a3fcc3711a0dd36 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 21:20:01 -0400 Subject: [PATCH 12/15] Another Doc Build Fix --- src/sys/wait.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 8584e0e65b..2ce747c7ca 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -7,6 +7,7 @@ //! //! ```rust //! use nix::sys::wait::*; +//! let pid = Pid::from_raw(17563); //! loop { //! match waitpid(PidGroup::ProcessGroupID(pid), WUNTRACED) { //! Ok(WaitStatus::Exited(pid, status)) => { From 1b4715bd9c79371913bafa66d1fa06127851b679 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 10 Jul 2017 21:52:28 -0400 Subject: [PATCH 13/15] Fix Document Build Errors --- src/sys/wait.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 2ce747c7ca..5fd35f07ec 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -7,6 +7,8 @@ //! //! ```rust //! use nix::sys::wait::*; +//! use nix::unistd::Pid; +//! //! let pid = Pid::from_raw(17563); //! loop { //! match waitpid(PidGroup::ProcessGroupID(pid), WUNTRACED) { From 64dde10c21aa745783453be3026285b38aed276e Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 11 Jul 2017 11:46:11 -0400 Subject: [PATCH 14/15] Add BSD targets --- src/sys/wait.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 5fd35f07ec..7332a3b8a9 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -48,15 +48,20 @@ libc_bitflags!( /// process WUNTRACED, /// Waits for children that have terminated. - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] WEXITED, /// Report the status of any continued child process specified by pid whose status has not /// been reported since it continued from a job control stop. - #[cfg(any(target_os = "android", target_os = "linux"))] WCONTINUED, /// Leave the child in a waitable state; a later wait call can be used to again retrieve /// the child status information. - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] WNOWAIT, /// Don't wait on children of other threads in this group #[cfg(any(target_os = "android", target_os = "linux"))] From 8632b569e2321b14a185eb427c0d830f85c8943b Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 19 Jul 2017 21:04:30 -0400 Subject: [PATCH 15/15] Improve Documentation --- src/sys/wait.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 7332a3b8a9..91e165d048 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,9 +1,12 @@ -//! This module contains the `wait()` and `waitpid()` functions, which are used to wait on and -//! obtain status information from child processes. These provide more granular control over -//! child management than the primitives provided by Rust's standard library, and are critical -//! in the creation of shells and managing jobs on *nix platforms. +//! This module contains the `wait()` and `waitpid()` functions. //! -//! # Example +//! These are used to wait on and obtain status information from child processes, which provide +//! more granular control over child management than the primitives provided by Rust's standard +//! library, and are critical in the creation of shells and managing jobs on *nix platforms. +//! +//! Manual Page: http://pubs.opengroup.org/onlinepubs/007908799/xsh/wait.html +//! +//! # Examples //! //! ```rust //! use nix::sys::wait::*; @@ -291,8 +294,10 @@ impl From for PidGroup { } } -/// Waits for and returns events that are received from the given supplied process or process group -/// ID, and associated options. +/// Waits for and returns events that are received, with additional options. +/// +/// The `pid` value may indicate either a process group ID, or process ID. The options +/// parameter controls the behavior of the function. /// /// # Usage Notes /// @@ -305,9 +310,7 @@ impl From for PidGroup { /// - If the value of the PID is `PidGroup::AnyChild`, it will wait on any child process of the /// current process. /// -/// # Possible Error Values -/// -/// If this function returns an error, the error value will be one of the following: +/// # Errors /// /// - **ECHILD**: The process does not exist or is not a child of the current process. /// - This may also happen if a child process has the `SIGCHLD` signal masked or set to @@ -338,8 +341,16 @@ pub fn waitpid(pid: PidGroup, options: O) -> Result }) } -/// Waits on any child of the current process, returning on events that change the status of -/// of that child. It is directly equivalent to `waitpid(PidGroup::AnyChild, None)`. +/// Waits for and returns events from any child of the current process. +/// +/// While waiting on the child, this function will return on events that indicate that the status +/// of that child has changed. It is directly equivalent to `waitpid(PidGroup::AnyChild, None)`. +/// +/// # Errors +/// +/// - **ECHILD**: The process does not exist or is not a child of the current process. +/// - This may also happen if a child process has the `SIGCHLD` signal masked or set to +/// `SIG_IGN`. pub fn wait() -> Result { waitpid(PidGroup::AnyChild, None) }