#![allow(unsafe_code)]
use crate::process::Pid;
use crate::{backend, io};
use bitflags::bitflags;
use core::fmt;
#[cfg(target_os = "linux")]
use crate::fd::BorrowedFd;
#[cfg(linux_raw)]
use crate::backend::process::wait::SiginfoExt as _;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WaitOptions: u32 {
const NOHANG = bitcast!(backend::process::wait::WNOHANG);
#[cfg(not(target_os = "horizon"))]
const UNTRACED = bitcast!(backend::process::wait::WUNTRACED);
#[cfg(not(target_os = "horizon"))]
const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
const _ = !0;
}
}
#[cfg(not(any(
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi"
)))]
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct WaitIdOptions: u32 {
const NOHANG = bitcast!(backend::process::wait::WNOHANG);
const CONTINUED = bitcast!(backend::process::wait::WCONTINUED);
#[cfg(not(target_os = "cygwin"))]
const EXITED = bitcast!(backend::process::wait::WEXITED);
#[cfg(not(target_os = "cygwin"))]
const NOWAIT = bitcast!(backend::process::wait::WNOWAIT);
#[cfg(not(target_os = "cygwin"))]
const STOPPED = bitcast!(backend::process::wait::WSTOPPED);
const _ = !0;
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct WaitStatus(i32);
impl WaitStatus {
#[inline]
pub(crate) fn new(status: i32) -> Self {
Self(status)
}
#[inline]
pub const fn as_raw(self) -> i32 {
self.0
}
#[inline]
#[doc(alias = "WIFSTOPPED")]
pub fn stopped(self) -> bool {
backend::process::wait::WIFSTOPPED(self.0)
}
#[inline]
#[doc(alias = "WIFEXITED")]
pub fn exited(self) -> bool {
backend::process::wait::WIFEXITED(self.0)
}
#[inline]
#[doc(alias = "WIFSIGNALED")]
pub fn signaled(self) -> bool {
backend::process::wait::WIFSIGNALED(self.0)
}
#[inline]
#[doc(alias = "WIFCONTINUED")]
pub fn continued(self) -> bool {
backend::process::wait::WIFCONTINUED(self.0)
}
#[inline]
#[doc(alias = "WSTOPSIG")]
pub fn stopping_signal(self) -> Option<i32> {
if self.stopped() {
Some(backend::process::wait::WSTOPSIG(self.0))
} else {
None
}
}
#[inline]
#[doc(alias = "WEXITSTATUS")]
pub fn exit_status(self) -> Option<i32> {
if self.exited() {
Some(backend::process::wait::WEXITSTATUS(self.0))
} else {
None
}
}
#[inline]
#[doc(alias = "WTERMSIG")]
pub fn terminating_signal(self) -> Option<i32> {
if self.signaled() {
Some(backend::process::wait::WTERMSIG(self.0))
} else {
None
}
}
}
impl fmt::Debug for WaitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("WaitStatus");
s.field("stopped", &self.stopped());
s.field("exited", &self.exited());
s.field("signaled", &self.signaled());
s.field("continued", &self.continued());
if let Some(stopping_signal) = self.stopping_signal() {
s.field("stopping_signal", &stopping_signal);
}
if let Some(exit_status) = self.exit_status() {
s.field("exit_status", &exit_status);
}
if let Some(terminating_signal) = self.terminating_signal() {
s.field("terminating_signal", &terminating_signal);
}
s.finish()
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
#[cfg(not(any(
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi"
)))]
pub struct WaitIdStatus(pub(crate) backend::c::siginfo_t);
#[cfg(linux_raw)]
unsafe impl Send for WaitIdStatus {}
#[cfg(linux_raw)]
unsafe impl Sync for WaitIdStatus {}
#[cfg(not(any(
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi"
)))]
impl WaitIdStatus {
#[inline]
pub fn stopped(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_STOPPED)
}
#[inline]
pub fn trapped(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_TRAPPED)
}
#[inline]
pub fn exited(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_EXITED)
}
#[inline]
pub fn killed(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_KILLED)
}
#[inline]
pub fn dumped(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_DUMPED)
}
#[inline]
pub fn continued(&self) -> bool {
self.raw_code() == bitcast!(backend::c::CLD_CONTINUED)
}
#[inline]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
pub fn stopping_signal(&self) -> Option<i32> {
if self.stopped() {
Some(self.si_status())
} else {
None
}
}
#[inline]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
pub fn trapping_signal(&self) -> Option<i32> {
if self.trapped() {
Some(self.si_status())
} else {
None
}
}
#[inline]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
pub fn exit_status(&self) -> Option<i32> {
if self.exited() {
Some(self.si_status())
} else {
None
}
}
#[inline]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
pub fn terminating_signal(&self) -> Option<i32> {
if self.killed() || self.dumped() {
Some(self.si_status())
} else {
None
}
}
#[cfg(linux_raw)]
pub fn raw_signo(&self) -> crate::ffi::c_int {
self.0.si_signo()
}
#[cfg(not(linux_raw))]
pub fn raw_signo(&self) -> crate::ffi::c_int {
self.0.si_signo
}
#[cfg(linux_raw)]
pub fn raw_errno(&self) -> crate::ffi::c_int {
self.0.si_errno()
}
#[cfg(not(linux_raw))]
pub fn raw_errno(&self) -> crate::ffi::c_int {
self.0.si_errno
}
#[cfg(linux_raw)]
pub fn raw_code(&self) -> crate::ffi::c_int {
self.0.si_code()
}
#[cfg(not(linux_raw))]
pub fn raw_code(&self) -> crate::ffi::c_int {
self.0.si_code
}
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
#[allow(unsafe_code)]
fn si_status(&self) -> crate::ffi::c_int {
unsafe { self.0.si_status() }
}
}
#[cfg(not(any(
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi"
)))]
impl fmt::Debug for WaitIdStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("WaitIdStatus");
s.field("stopped", &self.stopped());
s.field("exited", &self.exited());
s.field("killed", &self.killed());
s.field("trapped", &self.trapped());
s.field("dumped", &self.dumped());
s.field("continued", &self.continued());
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
if let Some(stopping_signal) = self.stopping_signal() {
s.field("stopping_signal", &stopping_signal);
}
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
if let Some(trapping_signal) = self.trapping_signal() {
s.field("trapping_signal", &trapping_signal);
}
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
if let Some(exit_status) = self.exit_status() {
s.field("exit_status", &exit_status);
}
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))]
if let Some(terminating_signal) = self.terminating_signal() {
s.field("terminating_signal", &terminating_signal);
}
s.finish()
}
}
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum WaitId<'a> {
#[doc(alias = "P_ALL")]
All,
#[doc(alias = "P_PID")]
Pid(Pid),
#[doc(alias = "P_PGID")]
Pgid(Option<Pid>),
#[cfg(target_os = "linux")]
#[doc(alias = "P_PIDFD")]
PidFd(BorrowedFd<'a>),
#[doc(hidden)]
#[cfg(not(target_os = "linux"))]
__EatLifetime(core::marker::PhantomData<&'a ()>),
}
#[doc(alias = "wait4")]
#[cfg(not(target_os = "wasi"))]
#[inline]
pub fn waitpid(pid: Option<Pid>, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
backend::process::syscalls::waitpid(pid, waitopts)
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
backend::process::syscalls::waitpgid(pgid, waitopts)
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
backend::process::syscalls::wait(waitopts)
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "horizon",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
#[inline]
pub fn waitid<'a, Id: Into<WaitId<'a>>>(
id: Id,
options: WaitIdOptions,
) -> io::Result<Option<WaitIdStatus>> {
backend::process::syscalls::waitid(id.into(), options)
}