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