nix/sys/wait.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
//! Wait for a process to change status
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int};
use std::convert::TryFrom;
libc_bitflags!(
/// Controls the behavior of [`waitpid`].
pub struct WaitPidFlag: c_int {
/// Do not block when there are no processes wishing to report status.
WNOHANG;
/// Report the status of selected processes which are stopped due to a
/// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
/// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
/// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
/// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
WUNTRACED;
/// Report the status of selected processes which have terminated.
#[cfg(any(target_os = "android",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
WEXITED;
/// Report the status of selected processes that have continued from a
/// job control stop by receiving a
/// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
WCONTINUED;
/// An alias for WUNTRACED.
#[cfg(any(target_os = "android",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
WSTOPPED;
/// Don't reap, just poll status.
#[cfg(any(target_os = "android",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "redox",
target_os = "macos",
target_os = "netbsd"))]
WNOWAIT;
/// Don't wait on children of other threads in this group
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
__WNOTHREAD;
/// Wait on all children, regardless of type
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
__WALL;
/// Wait for "clone" children only.
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
__WCLONE;
}
);
/// Possible return values from `wait()` or `waitpid()`.
///
/// Each status (other than `StillAlive`) describes a state transition
/// in a child process `Pid`, such as the process exiting or stopping,
/// plus additional data about the transition if any.
///
/// Note that there are two Linux-specific enum variants, `PtraceEvent`
/// and `PtraceSyscall`. Portable code should avoid exhaustively
/// matching on `WaitStatus`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WaitStatus {
/// The process exited normally (as with `exit()` or returning from
/// `main`) with the given exit code. This case matches the C macro
/// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
Exited(Pid, i32),
/// The process was killed by the given signal. The third field
/// indicates whether the signal generated a core dump. This case
/// matches the C macro `WIFSIGNALED(status)`; the last two fields
/// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
Signaled(Pid, Signal, bool),
/// The process is alive, but was stopped by the given signal. This
/// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
/// case matches the C macro `WIFSTOPPED(status)`; the second field
/// is `WSTOPSIG(status)`.
Stopped(Pid, Signal),
/// The traced process was stopped by a `PTRACE_EVENT_*` event. See
/// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
/// currently-defined events use `SIGTRAP` as the signal; the third
/// field is the `PTRACE_EVENT_*` value of the event.
///
/// [`nix::sys::ptrace`]: ../ptrace/index.html
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
PtraceEvent(Pid, Signal, c_int),
/// The traced process was stopped by execution of a system call,
/// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
/// more information.
///
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(any(target_os = "linux", target_os = "android"))]
PtraceSyscall(Pid),
/// The process was previously stopped but has resumed execution
/// after receiving a `SIGCONT` signal. This is only reported if
/// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
/// macro `WIFCONTINUED(status)`.
Continued(Pid),
/// There are currently no state changes to report in any awaited
/// child process. This is only returned if `WaitPidFlag::WNOHANG`
/// was used (otherwise `wait()` or `waitpid()` would block until
/// there was something to report).
StillAlive,
}
impl WaitStatus {
/// Extracts the PID from the WaitStatus unless it equals StillAlive.
pub fn pid(&self) -> Option<Pid> {
use self::WaitStatus::*;
match *self {
Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => Some(p),
StillAlive => None,
#[cfg(any(target_os = "android", target_os = "linux"))]
PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
}
}
}
fn exited(status: i32) -> bool {
libc::WIFEXITED(status)
}
fn exit_status(status: i32) -> i32 {
libc::WEXITSTATUS(status)
}
fn signaled(status: i32) -> bool {
libc::WIFSIGNALED(status)
}
fn term_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WTERMSIG(status))
}
fn dumped_core(status: i32) -> bool {
libc::WCOREDUMP(status)
}
fn stopped(status: i32) -> bool {
libc::WIFSTOPPED(status)
}
fn stop_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WSTOPSIG(status))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn syscall_stop(status: i32) -> bool {
// From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
// of delivering SIGTRAP | 0x80 as the signal number for syscall
// stops. This allows easily distinguishing syscall stops from
// genuine SIGTRAP signals.
libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn stop_additional(status: i32) -> c_int {
(status >> 16) as c_int
}
fn continued(status: i32) -> bool {
libc::WIFCONTINUED(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<WaitStatus> {
Ok(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 = "android", target_os = "linux"))] {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
let status_additional = stop_additional(status);
Ok(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))
})
}
} else {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
Ok(WaitStatus::Stopped(pid, stop_signal(status)?))
}
}
}
return decode_stopped(pid, status);
} else {
assert!(continued(status));
WaitStatus::Continued(pid)
})
}
}
/// Wait for a process to change status
///
/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
use self::WaitStatus::*;
let mut status: i32 = 0;
let option_bits = match options {
Some(bits) => bits.bits(),
None => 0,
};
let res = unsafe {
libc::waitpid(
pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
&mut status as *mut c_int,
option_bits,
)
};
match Errno::result(res)? {
0 => Ok(StillAlive),
res => WaitStatus::from_raw(Pid::from_raw(res), status),
}
}
/// Wait for any child process to change status or a signal is received.
///
/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
pub fn wait() -> Result<WaitStatus> {
waitpid(None, None)
}