Coverage Report

Created: 2024-12-17 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/rustix-0.38.42/src/process/wait.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::process::Pid;
2
use crate::{backend, io};
3
use bitflags::bitflags;
4
5
#[cfg(target_os = "linux")]
6
use crate::fd::BorrowedFd;
7
8
#[cfg(linux_raw)]
9
use crate::backend::process::wait::SiginfoExt;
10
11
bitflags! {
12
    /// Options for modifying the behavior of [`wait`]/[`waitpid`].
13
    #[repr(transparent)]
14
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
15
    pub struct WaitOptions: u32 {
16
        /// Return immediately if no child has exited.
17
        const NOHANG = bitcast!(backend::process::wait::WNOHANG);
18
        /// Return if a child has stopped (but not traced via [`ptrace`]).
19
        ///
20
        /// [`ptrace`]: https://man7.org/linux/man-pages/man2/ptrace.2.html
21
        const UNTRACED = bitcast!(backend::process::wait::WUNTRACED);
22
        /// Return if a stopped child has been resumed by delivery of
23
        /// [`Signal::Cont`].
24
        ///
25
        /// [`Signal::Cont`]: crate::process::Signal::Cont
26
        const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
27
28
        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
29
        const _ = !0;
30
    }
31
}
32
33
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
34
bitflags! {
35
    /// Options for modifying the behavior of [`waitid`].
36
    #[repr(transparent)]
37
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
38
    pub struct WaitidOptions: u32 {
39
        /// Return immediately if no child has exited.
40
        const NOHANG = bitcast!(backend::process::wait::WNOHANG);
41
        /// Return if a stopped child has been resumed by delivery of
42
        /// [`Signal::Cont`].
43
        ///
44
        /// [`Signal::Cont`]: crate::process::Signal::Cont
45
        const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
46
        /// Wait for processed that have exited.
47
        const EXITED = bitcast!(backend::process::wait::WEXITED);
48
        /// Keep processed in a waitable state.
49
        const NOWAIT = bitcast!(backend::process::wait::WNOWAIT);
50
        /// Wait for processes that have been stopped.
51
        const STOPPED = bitcast!(backend::process::wait::WSTOPPED);
52
53
        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
54
        const _ = !0;
55
    }
56
}
57
58
/// The status of a child process after calling [`wait`]/[`waitpid`].
59
#[derive(Debug, Clone, Copy)]
60
#[repr(transparent)]
61
pub struct WaitStatus(u32);
62
63
impl WaitStatus {
64
    /// Creates a `WaitStatus` out of an integer.
65
    #[inline]
66
0
    pub(crate) fn new(status: u32) -> Self {
67
0
        Self(status)
68
0
    }
69
70
    /// Converts a `WaitStatus` into its raw representation as an integer.
71
    #[inline]
72
0
    pub const fn as_raw(self) -> u32 {
73
0
        self.0
74
0
    }
75
76
    /// Returns whether the process is currently stopped.
77
    #[inline]
78
0
    pub fn stopped(self) -> bool {
79
0
        backend::process::wait::WIFSTOPPED(self.0 as _)
80
0
    }
81
82
    /// Returns whether the process has exited normally.
83
    #[inline]
84
0
    pub fn exited(self) -> bool {
85
0
        backend::process::wait::WIFEXITED(self.0 as _)
86
0
    }
87
88
    /// Returns whether the process was terminated by a signal.
89
    #[inline]
90
0
    pub fn signaled(self) -> bool {
91
0
        backend::process::wait::WIFSIGNALED(self.0 as _)
92
0
    }
93
94
    /// Returns whether the process has continued from a job control stop.
95
    #[inline]
96
0
    pub fn continued(self) -> bool {
97
0
        backend::process::wait::WIFCONTINUED(self.0 as _)
98
0
    }
99
100
    /// Returns the number of the signal that stopped the process, if the
101
    /// process was stopped by a signal.
102
    #[inline]
103
0
    pub fn stopping_signal(self) -> Option<u32> {
104
0
        if self.stopped() {
105
0
            Some(backend::process::wait::WSTOPSIG(self.0 as _) as _)
106
        } else {
107
0
            None
108
        }
109
0
    }
110
111
    /// Returns the exit status number returned by the process, if it exited
112
    /// normally.
113
    #[inline]
114
0
    pub fn exit_status(self) -> Option<u32> {
115
0
        if self.exited() {
116
0
            Some(backend::process::wait::WEXITSTATUS(self.0 as _) as _)
117
        } else {
118
0
            None
119
        }
120
0
    }
121
122
    /// Returns the number of the signal that terminated the process, if the
123
    /// process was terminated by a signal.
124
    #[inline]
125
0
    pub fn terminating_signal(self) -> Option<u32> {
126
0
        if self.signaled() {
127
0
            Some(backend::process::wait::WTERMSIG(self.0 as _) as _)
128
        } else {
129
0
            None
130
        }
131
0
    }
132
}
133
134
/// The status of a process after calling [`waitid`].
135
#[derive(Clone, Copy)]
136
#[repr(transparent)]
137
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
138
pub struct WaitidStatus(pub(crate) backend::c::siginfo_t);
139
140
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
141
impl WaitidStatus {
142
    /// Returns whether the process is currently stopped.
143
    #[inline]
144
0
    pub fn stopped(&self) -> bool {
145
0
        self.si_code() == backend::c::CLD_STOPPED
146
0
    }
147
148
    /// Returns whether the process is currently trapped.
149
    #[inline]
150
0
    pub fn trapped(&self) -> bool {
151
0
        self.si_code() == backend::c::CLD_TRAPPED
152
0
    }
153
154
    /// Returns whether the process has exited normally.
155
    #[inline]
156
0
    pub fn exited(&self) -> bool {
157
0
        self.si_code() == backend::c::CLD_EXITED
158
0
    }
159
160
    /// Returns whether the process was terminated by a signal and did not
161
    /// create a core file.
162
    #[inline]
163
0
    pub fn killed(&self) -> bool {
164
0
        self.si_code() == backend::c::CLD_KILLED
165
0
    }
166
167
    /// Returns whether the process was terminated by a signal and did create a
168
    /// core file.
169
    #[inline]
170
0
    pub fn dumped(&self) -> bool {
171
0
        self.si_code() == backend::c::CLD_DUMPED
172
0
    }
173
174
    /// Returns whether the process has continued from a job control stop.
175
    #[inline]
176
0
    pub fn continued(&self) -> bool {
177
0
        self.si_code() == backend::c::CLD_CONTINUED
178
0
    }
179
180
    /// Returns the number of the signal that stopped the process, if the
181
    /// process was stopped by a signal.
182
    #[inline]
183
    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
184
0
    pub fn stopping_signal(&self) -> Option<u32> {
185
0
        if self.stopped() {
186
0
            Some(self.si_status() as _)
187
        } else {
188
0
            None
189
        }
190
0
    }
191
192
    /// Returns the number of the signal that trapped the process, if the
193
    /// process was trapped by a signal.
194
    #[inline]
195
    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
196
0
    pub fn trapping_signal(&self) -> Option<u32> {
197
0
        if self.trapped() {
198
0
            Some(self.si_status() as _)
199
        } else {
200
0
            None
201
        }
202
0
    }
203
204
    /// Returns the exit status number returned by the process, if it exited
205
    /// normally.
206
    #[inline]
207
    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
208
0
    pub fn exit_status(&self) -> Option<u32> {
209
0
        if self.exited() {
210
0
            Some(self.si_status() as _)
211
        } else {
212
0
            None
213
        }
214
0
    }
215
216
    /// Returns the number of the signal that terminated the process, if the
217
    /// process was terminated by a signal.
218
    #[inline]
219
    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
220
0
    pub fn terminating_signal(&self) -> Option<u32> {
221
0
        if self.killed() || self.dumped() {
222
0
            Some(self.si_status() as _)
223
        } else {
224
0
            None
225
        }
226
0
    }
227
228
    /// Returns a reference to the raw platform-specific `siginfo_t` struct.
229
    #[inline]
230
0
    pub const fn as_raw(&self) -> &backend::c::siginfo_t {
231
0
        &self.0
232
0
    }
233
234
    #[cfg(linux_raw)]
235
0
    fn si_code(&self) -> u32 {
236
0
        self.0.si_code() as u32 // CLD_ consts are unsigned
237
0
    }
238
239
    #[cfg(not(linux_raw))]
240
    fn si_code(&self) -> backend::c::c_int {
241
        self.0.si_code
242
    }
243
244
    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
245
    #[allow(unsafe_code)]
246
0
    fn si_status(&self) -> backend::c::c_int {
247
0
        // SAFETY: POSIX [specifies] that the `siginfo_t` returned by a
248
0
        // `waitid` call always has a valid `si_status` value.
249
0
        //
250
0
        // [specifies]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html
251
0
        unsafe { self.0.si_status() }
252
0
    }
253
}
254
255
/// The identifier to wait on in a call to [`waitid`].
256
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
257
#[derive(Debug, Clone)]
258
#[non_exhaustive]
259
pub enum WaitId<'a> {
260
    /// Wait on all processes.
261
    #[doc(alias = "P_ALL")]
262
    All,
263
264
    /// Wait for a specific process ID.
265
    #[doc(alias = "P_PID")]
266
    Pid(Pid),
267
268
    /// Wait for a specific process group ID, or the calling process' group ID.
269
    #[doc(alias = "P_PGID")]
270
    Pgid(Option<Pid>),
271
272
    /// Wait for a specific process file descriptor.
273
    #[cfg(target_os = "linux")]
274
    #[doc(alias = "P_PIDFD")]
275
    PidFd(BorrowedFd<'a>),
276
277
    /// Eat the lifetime for non-Linux platforms.
278
    #[doc(hidden)]
279
    #[cfg(not(target_os = "linux"))]
280
    __EatLifetime(core::marker::PhantomData<&'a ()>),
281
}
282
283
/// `waitpid(pid, waitopts)`—Wait for a specific process to change state.
284
///
285
/// If the pid is `None`, the call will wait for any child process whose
286
/// process group id matches that of the calling process.
287
///
288
/// Otherwise, the call will wait for the child process with the given pid.
289
///
290
/// On Success, returns the status of the selected process.
291
///
292
/// If `NOHANG` was specified in the options, and the selected child process
293
/// didn't change state, returns `None`.
294
///
295
/// # Bugs
296
///
297
/// This function does not currently support waiting for given process group
298
/// (the < 0 case of `waitpid`); to do that, currently the [`waitpgid`] or
299
/// [`waitid`] function must be used.
300
///
301
/// This function does not currently support waiting for any process (the
302
/// `-1` case of `waitpid`); to do that, currently the [`wait`] function must
303
/// be used.
304
///
305
/// # References
306
///  - [POSIX]
307
///  - [Linux]
308
///
309
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html
310
/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html
311
#[cfg(not(target_os = "wasi"))]
312
#[inline]
313
0
pub fn waitpid(pid: Option<Pid>, waitopts: WaitOptions) -> io::Result<Option<WaitStatus>> {
314
0
    Ok(backend::process::syscalls::waitpid(pid, waitopts)?.map(|(_, status)| status))
315
0
}
316
317
/// `waitpid(-pgid, waitopts)`—Wait for a process in a specific process group
318
/// to change state.
319
///
320
/// The call will wait for any child process with the given pgid.
321
///
322
/// On Success, returns the status of the selected process.
323
///
324
/// If `NOHANG` was specified in the options, and no selected child process
325
/// changed state, returns `None`.
326
///
327
/// # References
328
///  - [POSIX]
329
///  - [Linux]
330
///
331
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html
332
/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html
333
#[cfg(not(target_os = "wasi"))]
334
#[inline]
335
0
pub fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<WaitStatus>> {
336
0
    Ok(backend::process::syscalls::waitpgid(pgid, waitopts)?.map(|(_, status)| status))
337
0
}
338
339
/// `wait(waitopts)`—Wait for any of the children of calling process to
340
/// change state.
341
///
342
/// On success, returns the pid of the child process whose state changed, and
343
/// the status of said process.
344
///
345
/// If `NOHANG` was specified in the options, and the selected child process
346
/// didn't change state, returns `None`.
347
///
348
/// # References
349
///  - [POSIX]
350
///  - [Linux]
351
///
352
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html
353
/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html
354
#[cfg(not(target_os = "wasi"))]
355
#[inline]
356
0
pub fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
357
0
    backend::process::syscalls::wait(waitopts)
358
0
}
359
360
/// `waitid(_, _, _, opts)`—Wait for the specified child process to change
361
/// state.
362
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
363
#[inline]
364
0
pub fn waitid<'a>(
365
0
    id: impl Into<WaitId<'a>>,
366
0
    options: WaitidOptions,
367
0
) -> io::Result<Option<WaitidStatus>> {
368
0
    backend::process::syscalls::waitid(id.into(), options)
369
0
}