summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys/wait.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/nix/src/sys/wait.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/src/sys/wait.rs')
-rw-r--r--third_party/rust/nix/src/sys/wait.rs393
1 files changed, 393 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/wait.rs b/third_party/rust/nix/src/sys/wait.rs
new file mode 100644
index 0000000000..f7a63ffcd2
--- /dev/null
+++ b/third_party/rust/nix/src/sys/wait.rs
@@ -0,0 +1,393 @@
+//! Wait for a process to change status
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::convert::TryFrom;
+#[cfg(any(
+ target_os = "android",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+use std::os::unix::io::{AsRawFd, BorrowedFd};
+
+libc_bitflags!(
+ /// Controls the behavior of [`waitpid`].
+ pub struct WaitPidFlag: c_int {
+ /// Do not block when there are no processes wishing to report status.
+ WNOHANG;
+ /// Report the status of selected processes which are stopped due to a
+ /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
+ /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
+ /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
+ /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
+ WUNTRACED;
+ /// Report the status of selected processes which have terminated.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WEXITED;
+ /// Report the status of selected processes that have continued from a
+ /// job control stop by receiving a
+ /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
+ WCONTINUED;
+ /// An alias for WUNTRACED.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WSTOPPED;
+ /// Don't reap, just poll status.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WNOWAIT;
+ /// Don't wait on children of other threads in this group
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WNOTHREAD;
+ /// Wait on all children, regardless of type
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WALL;
+ /// Wait for "clone" children only.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WCLONE;
+ }
+);
+
+/// Possible return values from `wait()` or `waitpid()`.
+///
+/// Each status (other than `StillAlive`) describes a state transition
+/// in a child process `Pid`, such as the process exiting or stopping,
+/// plus additional data about the transition if any.
+///
+/// Note that there are two Linux-specific enum variants, `PtraceEvent`
+/// and `PtraceSyscall`. Portable code should avoid exhaustively
+/// matching on `WaitStatus`.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum WaitStatus {
+ /// The process exited normally (as with `exit()` or returning from
+ /// `main`) with the given exit code. This case matches the C macro
+ /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
+ Exited(Pid, i32),
+ /// The process was killed by the given signal. The third field
+ /// indicates whether the signal generated a core dump. This case
+ /// matches the C macro `WIFSIGNALED(status)`; the last two fields
+ /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
+ Signaled(Pid, Signal, bool),
+ /// The process is alive, but was stopped by the given signal. This
+ /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
+ /// case matches the C macro `WIFSTOPPED(status)`; the second field
+ /// is `WSTOPSIG(status)`.
+ Stopped(Pid, Signal),
+ /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
+ /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
+ /// currently-defined events use `SIGTRAP` as the signal; the third
+ /// field is the `PTRACE_EVENT_*` value of the event.
+ ///
+ /// [`nix::sys::ptrace`]: ../ptrace/index.html
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PtraceEvent(Pid, Signal, c_int),
+ /// The traced process was stopped by execution of a system call,
+ /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
+ /// more information.
+ ///
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PtraceSyscall(Pid),
+ /// The process was previously stopped but has resumed execution
+ /// after receiving a `SIGCONT` signal. This is only reported if
+ /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
+ /// macro `WIFCONTINUED(status)`.
+ Continued(Pid),
+ /// There are currently no state changes to report in any awaited
+ /// child process. This is only returned if `WaitPidFlag::WNOHANG`
+ /// was used (otherwise `wait()` or `waitpid()` would block until
+ /// there was something to report).
+ StillAlive,
+}
+
+impl WaitStatus {
+ /// Extracts the PID from the WaitStatus unless it equals StillAlive.
+ pub fn pid(&self) -> Option<Pid> {
+ use self::WaitStatus::*;
+ match *self {
+ Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => {
+ Some(p)
+ }
+ StillAlive => None,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
+ }
+ }
+}
+
+fn exited(status: i32) -> bool {
+ libc::WIFEXITED(status)
+}
+
+fn exit_status(status: i32) -> i32 {
+ libc::WEXITSTATUS(status)
+}
+
+fn signaled(status: i32) -> bool {
+ libc::WIFSIGNALED(status)
+}
+
+fn term_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WTERMSIG(status))
+}
+
+fn dumped_core(status: i32) -> bool {
+ libc::WCOREDUMP(status)
+}
+
+fn stopped(status: i32) -> bool {
+ libc::WIFSTOPPED(status)
+}
+
+fn stop_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WSTOPSIG(status))
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn syscall_stop(status: i32) -> bool {
+ // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
+ // of delivering SIGTRAP | 0x80 as the signal number for syscall
+ // stops. This allows easily distinguishing syscall stops from
+ // genuine SIGTRAP signals.
+ libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn stop_additional(status: i32) -> c_int {
+ (status >> 16) as c_int
+}
+
+fn continued(status: i32) -> bool {
+ libc::WIFCONTINUED(status)
+}
+
+impl WaitStatus {
+ /// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus`
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Error` corresponding to `EINVAL` for invalid status values.
+ ///
+ /// # Examples
+ ///
+ /// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`:
+ ///
+ /// ```
+ /// use nix::sys::wait::WaitStatus;
+ /// use nix::sys::signal::Signal;
+ /// let pid = nix::unistd::Pid::from_raw(1);
+ /// let status = WaitStatus::from_raw(pid, 0x0002);
+ /// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
+ /// ```
+ pub fn from_raw(pid: Pid, status: i32) -> Result<WaitStatus> {
+ Ok(if exited(status) {
+ WaitStatus::Exited(pid, exit_status(status))
+ } else if signaled(status) {
+ WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status))
+ } else if stopped(status) {
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
+ let status_additional = stop_additional(status);
+ Ok(if syscall_stop(status) {
+ WaitStatus::PtraceSyscall(pid)
+ } else if status_additional == 0 {
+ WaitStatus::Stopped(pid, stop_signal(status)?)
+ } else {
+ WaitStatus::PtraceEvent(pid, stop_signal(status)?,
+ stop_additional(status))
+ })
+ }
+ } else {
+ fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
+ Ok(WaitStatus::Stopped(pid, stop_signal(status)?))
+ }
+ }
+ }
+ return decode_stopped(pid, status);
+ } else {
+ assert!(continued(status));
+ WaitStatus::Continued(pid)
+ })
+ }
+
+ /// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Error` corresponding to `EINVAL` for invalid values.
+ ///
+ /// # Safety
+ ///
+ /// siginfo_t is actually a union, not all fields may be initialized.
+ /// The functions si_pid() and si_status() must be valid to call on
+ /// the passed siginfo_t.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ ))]
+ unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
+ let si_pid = siginfo.si_pid();
+ if si_pid == 0 {
+ return Ok(WaitStatus::StillAlive);
+ }
+
+ assert_eq!(siginfo.si_signo, libc::SIGCHLD);
+
+ let pid = Pid::from_raw(si_pid);
+ let si_status = siginfo.si_status();
+
+ let status = match siginfo.si_code {
+ libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
+ libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
+ pid,
+ Signal::try_from(si_status)?,
+ siginfo.si_code == libc::CLD_DUMPED,
+ ),
+ libc::CLD_STOPPED => {
+ WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
+ }
+ libc::CLD_CONTINUED => WaitStatus::Continued(pid),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::CLD_TRAPPED => {
+ if si_status == libc::SIGTRAP | 0x80 {
+ WaitStatus::PtraceSyscall(pid)
+ } else {
+ WaitStatus::PtraceEvent(
+ pid,
+ Signal::try_from(si_status & 0xff)?,
+ (si_status >> 8) as c_int,
+ )
+ }
+ }
+ _ => return Err(Errno::EINVAL),
+ };
+
+ Ok(status)
+ }
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
+pub fn waitpid<P: Into<Option<Pid>>>(
+ pid: P,
+ options: Option<WaitPidFlag>,
+) -> Result<WaitStatus> {
+ use self::WaitStatus::*;
+
+ let mut status: i32 = 0;
+
+ let option_bits = match options {
+ Some(bits) => bits.bits(),
+ None => 0,
+ };
+
+ let res = unsafe {
+ libc::waitpid(
+ pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
+ &mut status as *mut c_int,
+ option_bits,
+ )
+ };
+
+ match Errno::result(res)? {
+ 0 => Ok(StillAlive),
+ res => WaitStatus::from_raw(Pid::from_raw(res), status),
+ }
+}
+
+/// Wait for any child process to change status or a signal is received.
+///
+/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
+pub fn wait() -> Result<WaitStatus> {
+ waitpid(None, None)
+}
+
+/// The ID argument for `waitid`
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[derive(Debug)]
+pub enum Id<'fd> {
+ /// Wait for any child
+ All,
+ /// Wait for the child whose process ID matches the given PID
+ Pid(Pid),
+ /// Wait for the child whose process group ID matches the given PID
+ ///
+ /// If the PID is zero, the caller's process group is used since Linux 5.4.
+ PGid(Pid),
+ /// Wait for the child referred to by the given PID file descriptor
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ PIDFd(BorrowedFd<'fd>),
+ /// A helper variant to resolve the unused parameter (`'fd`) problem on platforms
+ /// other than Linux and Android.
+ #[doc(hidden)]
+ _Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>),
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
+ let (idtype, idval) = match id {
+ Id::All => (libc::P_ALL, 0),
+ Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
+ Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t),
+ Id::_Unreachable(_) => unreachable!("This variant could never be constructed"),
+ };
+
+ let siginfo = unsafe {
+ // Memory is zeroed rather than uninitialized, as not all platforms
+ // initialize the memory in the StillAlive case
+ let mut siginfo: libc::siginfo_t = std::mem::zeroed();
+ Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
+ siginfo
+ };
+
+ unsafe { WaitStatus::from_siginfo(&siginfo) }
+}