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