From 996bc6b76a4830dfb0a1f28ee62f5d8b17febbac Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Mon, 21 Aug 2017 15:07:37 +1200 Subject: [PATCH] Expose `from_raw` on `WaitStatus` and make it return a `Result` --- CHANGELOG.md | 3 ++ src/sys/wait.rs | 85 ++++++++++++++++++++++++++----------------- test/sys/test_wait.rs | 9 +++++ 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e3994c823..06d2b79db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#785](https://github.com/nix-rust/nix/pull/785)) - Added `nix::unistd::execveat` on Linux and Android. ([#800](https://github.com/nix-rust/nix/pull/800)) +- Added the `from_raw()` method to `WaitStatus` for converting raw status values + to `WaitStatus` independent of syscalls. + ([#741](https://github.com/nix-rust/nix/pull/741)) ### Changed - Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692)) diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 019b751f9c..4c36cee5e0 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -129,8 +129,8 @@ fn signaled(status: i32) -> bool { unsafe { libc::WIFSIGNALED(status) } } -fn term_signal(status: i32) -> Signal { - Signal::from_c_int(unsafe { libc::WTERMSIG(status) }).unwrap() +fn term_signal(status: i32) -> Result { + Signal::from_c_int(unsafe { libc::WTERMSIG(status) }) } fn dumped_core(status: i32) -> bool { @@ -141,8 +141,8 @@ fn stopped(status: i32) -> bool { unsafe { libc::WIFSTOPPED(status) } } -fn stop_signal(status: i32) -> Signal { - Signal::from_c_int(unsafe { libc::WSTOPSIG(status) }).unwrap() +fn stop_signal(status: i32) -> Result { + Signal::from_c_int(unsafe { libc::WSTOPSIG(status) }) } fn syscall_stop(status: i32) -> bool { @@ -161,34 +161,53 @@ fn continued(status: i32) -> bool { unsafe { libc::WIFCONTINUED(status) } } -fn decode(pid: Pid, status: i32) -> WaitStatus { - if exited(status) { - WaitStatus::Exited(pid, exit_status(status)) - } else if signaled(status) { - WaitStatus::Signaled(pid, term_signal(status), dumped_core(status)) - } else if stopped(status) { - cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "android"))] { - fn decode_stopped(pid: Pid, status: i32) -> WaitStatus { - let status_additional = stop_additional(status); - if syscall_stop(status) { - WaitStatus::PtraceSyscall(pid) - } else if status_additional == 0 { - WaitStatus::Stopped(pid, stop_signal(status)) - } else { - WaitStatus::PtraceEvent(pid, stop_signal(status), stop_additional(status)) +impl WaitStatus { + /// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus` + /// + /// # Errors + /// + /// Returns an `Error` corresponding to `EINVAL` for invalid status values. + /// + /// # Examples + /// + /// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`: + /// + /// ``` + /// use nix::sys::wait::WaitStatus; + /// use nix::sys::signal::Signal; + /// let pid = nix::unistd::Pid::from_raw(1); + /// let status = WaitStatus::from_raw(pid, 0x0002); + /// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); + /// ``` + pub fn from_raw(pid: Pid, status: i32) -> Result { + Ok(if exited(status) { + WaitStatus::Exited(pid, exit_status(status)) + } else if signaled(status) { + WaitStatus::Signaled(pid, try!(term_signal(status)), dumped_core(status)) + } else if stopped(status) { + cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "android"))] { + fn decode_stopped(pid: Pid, status: i32) -> Result { + let status_additional = stop_additional(status); + Ok(if syscall_stop(status) { + WaitStatus::PtraceSyscall(pid) + } else if status_additional == 0 { + WaitStatus::Stopped(pid, try!(stop_signal(status))) + } else { + WaitStatus::PtraceEvent(pid, try!(stop_signal(status)), stop_additional(status)) + }) + } + } else { + fn decode_stopped(pid: Pid, status: i32) -> Result { + Ok(WaitStatus::Stopped(pid, try!(stop_signal(status)))) } - } - } else { - fn decode_stopped(pid: Pid, status: i32) -> WaitStatus { - WaitStatus::Stopped(pid, stop_signal(status)) } } - } - decode_stopped(pid, status) - } else { - assert!(continued(status)); - WaitStatus::Continued(pid) + return decode_stopped(pid, status); + } else { + assert!(continued(status)); + WaitStatus::Continued(pid) + }) } } @@ -210,10 +229,10 @@ pub fn waitpid>>(pid: P, options: Option) -> Re ) }; - Ok(match try!(Errno::result(res)) { - 0 => StillAlive, - res => decode(Pid::from_raw(res), status), - }) + match try!(Errno::result(res)) { + 0 => Ok(StillAlive), + res => WaitStatus::from_raw(Pid::from_raw(res), status), + } } pub fn wait() -> Result { diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 44a111a358..02f32734aa 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -1,3 +1,4 @@ +use nix::Error; use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::signal::*; @@ -37,6 +38,14 @@ fn test_wait_exit() { } } +#[test] +fn test_waitstatus_from_raw() { + let pid = Pid::from_raw(1); + assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); + assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2))); + assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Error::invalid_argument())); +} + #[test] fn test_waitstatus_pid() { let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");