1#![allow(unsafe_code)]
9use crate::process::Pid;
10use crate::{backend, io};
11use bitflags::bitflags;
12use core::fmt;
13
14#[cfg(target_os = "linux")]
15use crate::fd::BorrowedFd;
16
17#[cfg(linux_raw)]
18use crate::backend::process::wait::SiginfoExt as _;
19
20bitflags! {
21 #[repr(transparent)]
23 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
24 pub struct WaitOptions: u32 {
25 const NOHANG = bitcast!(backend::process::wait::WNOHANG);
27 #[cfg(not(target_os = "horizon"))]
31 const UNTRACED = bitcast!(backend::process::wait::WUNTRACED);
32 #[cfg(not(target_os = "horizon"))]
37 const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
38
39 const _ = !0;
41 }
42}
43
44#[cfg(not(any(
45 target_os = "horizon",
46 target_os = "openbsd",
47 target_os = "redox",
48 target_os = "wasi"
49)))]
50bitflags! {
51 #[repr(transparent)]
53 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
54 pub struct WaitIdOptions: u32 {
55 const NOHANG = bitcast!(backend::process::wait::WNOHANG);
57 const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
62 #[cfg(not(target_os = "cygwin"))]
64 const EXITED = bitcast!(backend::process::wait::WEXITED);
65 #[cfg(not(target_os = "cygwin"))]
67 const NOWAIT = bitcast!(backend::process::wait::WNOWAIT);
68 #[cfg(not(target_os = "cygwin"))]
70 const STOPPED = bitcast!(backend::process::wait::WSTOPPED);
71
72 const _ = !0;
74 }
75}
76
77#[derive(Clone, Copy)]
79#[repr(transparent)]
80pub struct WaitStatus(i32);
81
82impl WaitStatus {
83 #[inline]
85 pub(crate) fn new(status: i32) -> Self {
86 Self(status)
87 }
88
89 #[inline]
91 pub const fn as_raw(self) -> i32 {
92 self.0
93 }
94
95 #[inline]
97 #[doc(alias = "WIFSTOPPED")]
98 pub fn stopped(self) -> bool {
99 backend::process::wait::WIFSTOPPED(self.0)
100 }
101
102 #[inline]
104 #[doc(alias = "WIFEXITED")]
105 pub fn exited(self) -> bool {
106 backend::process::wait::WIFEXITED(self.0)
107 }
108
109 #[inline]
111 #[doc(alias = "WIFSIGNALED")]
112 pub fn signaled(self) -> bool {
113 backend::process::wait::WIFSIGNALED(self.0)
114 }
115
116 #[inline]
118 #[doc(alias = "WIFCONTINUED")]
119 pub fn continued(self) -> bool {
120 backend::process::wait::WIFCONTINUED(self.0)
121 }
122
123 #[inline]
126 #[doc(alias = "WSTOPSIG")]
127 pub fn stopping_signal(self) -> Option<i32> {
128 if self.stopped() {
129 Some(backend::process::wait::WSTOPSIG(self.0))
130 } else {
131 None
132 }
133 }
134
135 #[inline]
138 #[doc(alias = "WEXITSTATUS")]
139 pub fn exit_status(self) -> Option<i32> {
140 if self.exited() {
141 Some(backend::process::wait::WEXITSTATUS(self.0))
142 } else {
143 None
144 }
145 }
146
147 #[inline]
150 #[doc(alias = "WTERMSIG")]
151 pub fn terminating_signal(self) -> Option<i32> {
152 if self.signaled() {
153 Some(backend::process::wait::WTERMSIG(self.0))
154 } else {
155 None
156 }
157 }
158}
159
160impl fmt::Debug for WaitStatus {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 let mut s = f.debug_struct("WaitStatus");
163 s.field("stopped", &self.stopped());
164 s.field("exited", &self.exited());
165 s.field("signaled", &self.signaled());
166 s.field("continued", &self.continued());
167 if let Some(stopping_signal) = self.stopping_signal() {
168 s.field("stopping_signal", &stopping_signal);
169 }
170 if let Some(exit_status) = self.exit_status() {
171 s.field("exit_status", &exit_status);
172 }
173 if let Some(terminating_signal) = self.terminating_signal() {
174 s.field("terminating_signal", &terminating_signal);
175 }
176 s.finish()
177 }
178}
179
180#[derive(Clone, Copy)]
182#[repr(transparent)]
183#[cfg(not(any(
184 target_os = "horizon",
185 target_os = "openbsd",
186 target_os = "redox",
187 target_os = "wasi"
188)))]
189pub struct WaitIdStatus(pub(crate) backend::c::siginfo_t);
190
191#[cfg(linux_raw)]
192unsafe impl Send for WaitIdStatus {}
195
196#[cfg(linux_raw)]
197unsafe impl Sync for WaitIdStatus {}
199
200#[cfg(not(any(
201 target_os = "horizon",
202 target_os = "openbsd",
203 target_os = "redox",
204 target_os = "wasi"
205)))]
206impl WaitIdStatus {
207 #[inline]
209 pub fn stopped(&self) -> bool {
210 self.raw_code() == bitcast!(backend::c::CLD_STOPPED)
211 }
212
213 #[inline]
215 pub fn trapped(&self) -> bool {
216 self.raw_code() == bitcast!(backend::c::CLD_TRAPPED)
217 }
218
219 #[inline]
221 pub fn exited(&self) -> bool {
222 self.raw_code() == bitcast!(backend::c::CLD_EXITED)
223 }
224
225 #[inline]
228 pub fn killed(&self) -> bool {
229 self.raw_code() == bitcast!(backend::c::CLD_KILLED)
230 }
231
232 #[inline]
235 pub fn dumped(&self) -> bool {
236 self.raw_code() == bitcast!(backend::c::CLD_DUMPED)
237 }
238
239 #[inline]
241 pub fn continued(&self) -> bool {
242 self.raw_code() == bitcast!(backend::c::CLD_CONTINUED)
243 }
244
245 #[inline]
248 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
249 pub fn stopping_signal(&self) -> Option<i32> {
250 if self.stopped() {
251 Some(self.si_status())
252 } else {
253 None
254 }
255 }
256
257 #[inline]
260 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
261 pub fn trapping_signal(&self) -> Option<i32> {
262 if self.trapped() {
263 Some(self.si_status())
264 } else {
265 None
266 }
267 }
268
269 #[inline]
272 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
273 pub fn exit_status(&self) -> Option<i32> {
274 if self.exited() {
275 Some(self.si_status())
276 } else {
277 None
278 }
279 }
280
281 #[inline]
284 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
285 pub fn terminating_signal(&self) -> Option<i32> {
286 if self.killed() || self.dumped() {
287 Some(self.si_status())
288 } else {
289 None
290 }
291 }
292
293 #[cfg(linux_raw)]
295 pub fn raw_signo(&self) -> crate::ffi::c_int {
296 self.0.si_signo()
297 }
298
299 #[cfg(not(linux_raw))]
301 pub fn raw_signo(&self) -> crate::ffi::c_int {
302 self.0.si_signo
303 }
304
305 #[cfg(linux_raw)]
307 pub fn raw_errno(&self) -> crate::ffi::c_int {
308 self.0.si_errno()
309 }
310
311 #[cfg(not(linux_raw))]
313 pub fn raw_errno(&self) -> crate::ffi::c_int {
314 self.0.si_errno
315 }
316
317 #[cfg(linux_raw)]
319 pub fn raw_code(&self) -> crate::ffi::c_int {
320 self.0.si_code()
321 }
322
323 #[cfg(not(linux_raw))]
325 pub fn raw_code(&self) -> crate::ffi::c_int {
326 self.0.si_code
327 }
328
329 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
334 #[allow(unsafe_code)]
335 fn si_status(&self) -> crate::ffi::c_int {
336 unsafe { self.0.si_status() }
341 }
342}
343
344#[cfg(not(any(
345 target_os = "horizon",
346 target_os = "openbsd",
347 target_os = "redox",
348 target_os = "wasi"
349)))]
350impl fmt::Debug for WaitIdStatus {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 let mut s = f.debug_struct("WaitIdStatus");
353 s.field("stopped", &self.stopped());
354 s.field("exited", &self.exited());
355 s.field("killed", &self.killed());
356 s.field("trapped", &self.trapped());
357 s.field("dumped", &self.dumped());
358 s.field("continued", &self.continued());
359 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
360 if let Some(stopping_signal) = self.stopping_signal() {
361 s.field("stopping_signal", &stopping_signal);
362 }
363 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
364 if let Some(trapping_signal) = self.trapping_signal() {
365 s.field("trapping_signal", &trapping_signal);
366 }
367 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
368 if let Some(exit_status) = self.exit_status() {
369 s.field("exit_status", &exit_status);
370 }
371 #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
372 if let Some(terminating_signal) = self.terminating_signal() {
373 s.field("terminating_signal", &terminating_signal);
374 }
375 s.finish()
376 }
377}
378
379#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
381#[derive(Debug, Clone)]
382#[non_exhaustive]
383pub enum WaitId<'a> {
384 #[doc(alias = "P_ALL")]
386 All,
387
388 #[doc(alias = "P_PID")]
390 Pid(Pid),
391
392 #[doc(alias = "P_PGID")]
394 Pgid(Option<Pid>),
395
396 #[cfg(target_os = "linux")]
398 #[doc(alias = "P_PIDFD")]
399 PidFd(BorrowedFd<'a>),
400
401 #[doc(hidden)]
403 #[cfg(not(target_os = "linux"))]
404 __EatLifetime(core::marker::PhantomData<&'a ()>),
405}
406
407#[doc(alias = "wait4")]
429#[cfg(not(target_os = "wasi"))]
430#[inline]
431pub fn waitpid(pid: Option<Pid>, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
432 backend::process::syscalls::waitpid(pid, waitopts)
433}
434
435#[cfg(not(target_os = "wasi"))]
452#[inline]
453pub fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
454 backend::process::syscalls::waitpgid(pgid, waitopts)
455}
456
457#[cfg(not(target_os = "wasi"))]
473#[inline]
474pub fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
475 backend::process::syscalls::wait(waitopts)
476}
477
478#[cfg(not(any(
481 target_os = "cygwin",
482 target_os = "horizon",
483 target_os = "openbsd",
484 target_os = "redox",
485 target_os = "wasi",
486)))]
487#[inline]
488pub fn waitid<'a, Id: Into<WaitId<'a>>>(
489 id: Id,
490 options: WaitIdOptions,
491) -> io::Result<Option<WaitIdStatus>> {
492 backend::process::syscalls::waitid(id.into(), options)
493}