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