• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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