1 use libc::{self, c_int};
2 use {Errno, Result};
3 use unistd::Pid;
4 
5 use sys::signal::Signal;
6 
7 mod ffi {
8     use libc::{pid_t, c_int};
9 
10     extern {
waitpid(pid: pid_t, status: *mut c_int, options: c_int) -> pid_t11         pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int) -> pid_t;
12     }
13 }
14 
15 #[cfg(not(any(target_os = "linux",
16               target_os = "android")))]
17 libc_bitflags!(
18     pub flags WaitPidFlag: c_int {
19         WNOHANG,
20         WUNTRACED,
21     }
22 );
23 
24 #[cfg(any(target_os = "linux",
25           target_os = "android"))]
26 libc_bitflags!(
27     pub flags WaitPidFlag: c_int {
28         WNOHANG,
29         WUNTRACED,
30         WEXITED,
31         WCONTINUED,
32         WNOWAIT, // Don't reap, just poll status.
33         __WNOTHREAD, // Don't wait on children of other threads in this group
34         __WALL, // Wait on all children, regardless of type
35         __WCLONE,
36     }
37 );
38 
39 #[cfg(any(target_os = "linux",
40           target_os = "android"))]
41 const WSTOPPED: WaitPidFlag = WUNTRACED;
42 
43 /// Possible return values from `wait()` or `waitpid()`.
44 ///
45 /// Each status (other than `StillAlive`) describes a state transition
46 /// in a child process `Pid`, such as the process exiting or stopping,
47 /// plus additional data about the transition if any.
48 ///
49 /// Note that there are two Linux-specific enum variants, `PtraceEvent`
50 /// and `PtraceSyscall`. Portable code should avoid exhaustively
51 /// matching on `WaitStatus`.
52 #[derive(Eq, PartialEq, Clone, Copy, Debug)]
53 pub enum WaitStatus {
54     /// The process exited normally (as with `exit()` or returning from
55     /// `main`) with the given exit code. This case matches the C macro
56     /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
57     Exited(Pid, i8),
58     /// The process was killed by the given signal. The third field
59     /// indicates whether the signal generated a core dump. This case
60     /// matches the C macro `WIFSIGNALED(status)`; the last two fields
61     /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
62     Signaled(Pid, Signal, bool),
63     /// The process is alive, but was stopped by the given signal. This
64     /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
65     /// case matches the C macro `WIFSTOPPED(status)`; the second field
66     /// is `WSTOPSIG(status)`.
67     Stopped(Pid, Signal),
68     /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
69     /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
70     /// currently-defined events use `SIGTRAP` as the signal; the third
71     /// field is the `PTRACE_EVENT_*` value of the event.
72     ///
73     /// [`nix::sys::ptrace`]: ../ptrace/index.html
74     /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
75     #[cfg(any(target_os = "linux", target_os = "android"))]
76     PtraceEvent(Pid, Signal, c_int),
77     /// The traced process was stopped by execution of a system call,
78     /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
79     /// more information.
80     ///
81     /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
82     #[cfg(any(target_os = "linux", target_os = "android"))]
83     PtraceSyscall(Pid),
84     /// The process was previously stopped but has resumed execution
85     /// after receiving a `SIGCONT` signal. This is only reported if
86     /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
87     /// macro `WIFCONTINUED(status)`.
88     Continued(Pid),
89     /// There are currently no state changes to report in any awaited
90     /// child process. This is only returned if `WaitPidFlag::WNOHANG`
91     /// was used (otherwise `wait()` or `waitpid()` would block until
92     /// there was something to report).
93     StillAlive
94 }
95 
96 #[cfg(any(target_os = "linux",
97           target_os = "android"))]
98 mod status {
99     use sys::signal::Signal;
100     use libc::c_int;
101     use libc::SIGTRAP;
102 
exited(status: i32) -> bool103     pub fn exited(status: i32) -> bool {
104         (status & 0x7F) == 0
105     }
106 
exit_status(status: i32) -> i8107     pub fn exit_status(status: i32) -> i8 {
108         ((status & 0xFF00) >> 8) as i8
109     }
110 
signaled(status: i32) -> bool111     pub fn signaled(status: i32) -> bool {
112         ((((status & 0x7f) + 1) as i8) >> 1) > 0
113     }
114 
term_signal(status: i32) -> Signal115     pub fn term_signal(status: i32) -> Signal {
116         Signal::from_c_int(status & 0x7f).unwrap()
117     }
118 
dumped_core(status: i32) -> bool119     pub fn dumped_core(status: i32) -> bool {
120         (status & 0x80) != 0
121     }
122 
stopped(status: i32) -> bool123     pub fn stopped(status: i32) -> bool {
124         (status & 0xff) == 0x7f
125     }
126 
stop_signal(status: i32) -> Signal127     pub fn stop_signal(status: i32) -> Signal {
128         // Keep only 7 bits of the signal: the high bit
129         // is used to indicate syscall stops, below.
130         Signal::from_c_int((status & 0x7F00) >> 8).unwrap()
131     }
132 
syscall_stop(status: i32) -> bool133     pub fn syscall_stop(status: i32) -> bool {
134         // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
135         // of delivering SIGTRAP | 0x80 as the signal number for syscall
136         // stops. This allows easily distinguishing syscall stops from
137         // genuine SIGTRAP signals.
138         ((status & 0xFF00) >> 8) == SIGTRAP | 0x80
139     }
140 
stop_additional(status: i32) -> c_int141     pub fn stop_additional(status: i32) -> c_int {
142         (status >> 16) as c_int
143     }
144 
continued(status: i32) -> bool145     pub fn continued(status: i32) -> bool {
146         status == 0xFFFF
147     }
148 }
149 
150 #[cfg(any(target_os = "macos",
151           target_os = "ios"))]
152 mod status {
153     use sys::signal::{Signal,SIGCONT};
154 
155     const WCOREFLAG: i32 = 0x80;
156     const WSTOPPED: i32 = 0x7f;
157 
wstatus(status: i32) -> i32158     fn wstatus(status: i32) -> i32 {
159         status & 0x7F
160     }
161 
exit_status(status: i32) -> i8162     pub fn exit_status(status: i32) -> i8 {
163         ((status >> 8) & 0xFF) as i8
164     }
165 
stop_signal(status: i32) -> Signal166     pub fn stop_signal(status: i32) -> Signal {
167         Signal::from_c_int(status >> 8).unwrap()
168     }
169 
continued(status: i32) -> bool170     pub fn continued(status: i32) -> bool {
171         wstatus(status) == WSTOPPED && stop_signal(status) == SIGCONT
172     }
173 
stopped(status: i32) -> bool174     pub fn stopped(status: i32) -> bool {
175         wstatus(status) == WSTOPPED && stop_signal(status) != SIGCONT
176     }
177 
exited(status: i32) -> bool178     pub fn exited(status: i32) -> bool {
179         wstatus(status) == 0
180     }
181 
signaled(status: i32) -> bool182     pub fn signaled(status: i32) -> bool {
183         wstatus(status) != WSTOPPED && wstatus(status) != 0
184     }
185 
term_signal(status: i32) -> Signal186     pub fn term_signal(status: i32) -> Signal {
187         Signal::from_c_int(wstatus(status)).unwrap()
188     }
189 
dumped_core(status: i32) -> bool190     pub fn dumped_core(status: i32) -> bool {
191         (status & WCOREFLAG) != 0
192     }
193 }
194 
195 #[cfg(any(target_os = "freebsd",
196           target_os = "openbsd",
197           target_os = "dragonfly",
198           target_os = "netbsd"))]
199 mod status {
200     use sys::signal::Signal;
201 
202     const WCOREFLAG: i32 = 0x80;
203     const WSTOPPED: i32 = 0x7f;
204 
wstatus(status: i32) -> i32205     fn wstatus(status: i32) -> i32 {
206         status & 0x7F
207     }
208 
stopped(status: i32) -> bool209     pub fn stopped(status: i32) -> bool {
210         wstatus(status) == WSTOPPED
211     }
212 
stop_signal(status: i32) -> Signal213     pub fn stop_signal(status: i32) -> Signal {
214         Signal::from_c_int(status >> 8).unwrap()
215     }
216 
signaled(status: i32) -> bool217     pub fn signaled(status: i32) -> bool {
218         wstatus(status) != WSTOPPED && wstatus(status) != 0 && status != 0x13
219     }
220 
term_signal(status: i32) -> Signal221     pub fn term_signal(status: i32) -> Signal {
222         Signal::from_c_int(wstatus(status)).unwrap()
223     }
224 
exited(status: i32) -> bool225     pub fn exited(status: i32) -> bool {
226         wstatus(status) == 0
227     }
228 
exit_status(status: i32) -> i8229     pub fn exit_status(status: i32) -> i8 {
230         (status >> 8) as i8
231     }
232 
continued(status: i32) -> bool233     pub fn continued(status: i32) -> bool {
234         status == 0x13
235     }
236 
dumped_core(status: i32) -> bool237     pub fn dumped_core(status: i32) -> bool {
238         (status & WCOREFLAG) != 0
239     }
240 }
241 
decode(pid : Pid, status: i32) -> WaitStatus242 fn decode(pid : Pid, status: i32) -> WaitStatus {
243     if status::exited(status) {
244         WaitStatus::Exited(pid, status::exit_status(status))
245     } else if status::signaled(status) {
246         WaitStatus::Signaled(pid, status::term_signal(status), status::dumped_core(status))
247     } else if status::stopped(status) {
248         cfg_if! {
249             if #[cfg(any(target_os = "linux", target_os = "android"))] {
250                 fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
251                     let status_additional = status::stop_additional(status);
252                     if status::syscall_stop(status) {
253                         WaitStatus::PtraceSyscall(pid)
254                     } else if status_additional == 0 {
255                         WaitStatus::Stopped(pid, status::stop_signal(status))
256                     } else {
257                         WaitStatus::PtraceEvent(pid, status::stop_signal(status), status::stop_additional(status))
258                     }
259                 }
260             } else {
261                 fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
262                     WaitStatus::Stopped(pid, status::stop_signal(status))
263                 }
264             }
265         }
266         decode_stopped(pid, status)
267     } else {
268         assert!(status::continued(status));
269         WaitStatus::Continued(pid)
270     }
271 }
272 
waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus>273 pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
274     use self::WaitStatus::*;
275 
276     let mut status: i32 = 0;
277 
278     let option_bits = match options {
279         Some(bits) => bits.bits(),
280         None => 0
281     };
282 
283     let res = unsafe { ffi::waitpid(pid.into().unwrap_or(Pid::from_raw(-1)).into(), &mut status as *mut c_int, option_bits) };
284 
285     Ok(match try!(Errno::result(res)) {
286         0 => StillAlive,
287         res => decode(Pid::from_raw(res), status),
288     })
289 }
290 
wait() -> Result<WaitStatus>291 pub fn wait() -> Result<WaitStatus> {
292     waitpid(None, None)
293 }
294