diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/rustix/src/process | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/rustix/src/process')
| -rw-r--r-- | vendor/rustix/src/process/chdir.rs | 98 | ||||
| -rw-r--r-- | vendor/rustix/src/process/chroot.rs | 16 | ||||
| -rw-r--r-- | vendor/rustix/src/process/exit.rs | 36 | ||||
| -rw-r--r-- | vendor/rustix/src/process/fcntl_getlk.rs | 23 | ||||
| -rw-r--r-- | vendor/rustix/src/process/id.rs | 260 | ||||
| -rw-r--r-- | vendor/rustix/src/process/ioctl.rs | 73 | ||||
| -rw-r--r-- | vendor/rustix/src/process/kill.rs | 98 | ||||
| -rw-r--r-- | vendor/rustix/src/process/mod.rs | 114 | ||||
| -rw-r--r-- | vendor/rustix/src/process/pidfd.rs | 43 | ||||
| -rw-r--r-- | vendor/rustix/src/process/pidfd_getfd.rs | 56 | ||||
| -rw-r--r-- | vendor/rustix/src/process/pivot_root.rs | 18 | ||||
| -rw-r--r-- | vendor/rustix/src/process/prctl.rs | 1165 | ||||
| -rw-r--r-- | vendor/rustix/src/process/priority.rs | 132 | ||||
| -rw-r--r-- | vendor/rustix/src/process/procctl.rs | 547 | ||||
| -rw-r--r-- | vendor/rustix/src/process/rlimit.rs | 53 | ||||
| -rw-r--r-- | vendor/rustix/src/process/types.rs | 94 | ||||
| -rw-r--r-- | vendor/rustix/src/process/umask.rs | 21 | ||||
| -rw-r--r-- | vendor/rustix/src/process/wait.rs | 493 |
18 files changed, 3340 insertions, 0 deletions
diff --git a/vendor/rustix/src/process/chdir.rs b/vendor/rustix/src/process/chdir.rs new file mode 100644 index 00000000..8c373979 --- /dev/null +++ b/vendor/rustix/src/process/chdir.rs @@ -0,0 +1,98 @@ +#[cfg(not(target_os = "fuchsia"))] +use crate::backend::fd::AsFd; +#[cfg(feature = "fs")] +use crate::path; +#[cfg(any(feature = "fs", not(target_os = "fuchsia")))] +use crate::{backend, io}; +#[cfg(all(feature = "alloc", feature = "fs"))] +use { + crate::ffi::{CStr, CString}, + crate::path::SMALL_PATH_BUFFER_SIZE, + alloc::vec::Vec, +}; + +/// `chdir(path)`—Change the current working directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/chdir.html +/// [Linux]: https://man7.org/linux/man-pages/man2/chdir.2.html +#[inline] +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +pub fn chdir<P: path::Arg>(path: P) -> io::Result<()> { + path.into_with_c_str(backend::process::syscalls::chdir) +} + +/// `fchdir(fd)`—Change the current working directory. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchdir.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fchdir.2.html +#[cfg(not(target_os = "fuchsia"))] +#[inline] +pub fn fchdir<Fd: AsFd>(fd: Fd) -> io::Result<()> { + backend::process::syscalls::fchdir(fd.as_fd()) +} + +/// `getcwd`—Return the current working directory. +/// +/// If `reuse` already has available capacity, reuse it if possible. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getcwd.html +/// [Linux]: https://man7.org/linux/man-pages/man3/getcwd.3.html +#[cfg(all(feature = "alloc", feature = "fs"))] +#[cfg(not(target_os = "wasi"))] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[inline] +pub fn getcwd<B: Into<Vec<u8>>>(reuse: B) -> io::Result<CString> { + _getcwd(reuse.into()) +} + +#[cfg(all(feature = "alloc", feature = "fs"))] +#[allow(unsafe_code)] +fn _getcwd(mut buffer: Vec<u8>) -> io::Result<CString> { + buffer.clear(); + buffer.reserve(SMALL_PATH_BUFFER_SIZE); + + loop { + match backend::process::syscalls::getcwd(buffer.spare_capacity_mut()) { + Err(io::Errno::RANGE) => { + // Use `Vec` reallocation strategy to grow capacity + // exponentially. + buffer.reserve(buffer.capacity() + 1); + } + Ok(_) => { + // SAFETY: + // - “These functions return a null-terminated string” + // - [POSIX definition 3.375: String]: “A contiguous sequence + // of bytes terminated by and including the first null byte.” + // + // Thus, there will be a single NUL byte at the end of the + // string. + // + // [POSIX definition 3.375: String]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_375 + unsafe { + buffer.set_len( + CStr::from_ptr(buffer.as_ptr().cast()) + .to_bytes_with_nul() + .len(), + ); + + return Ok(CString::from_vec_with_nul_unchecked(buffer)); + } + } + Err(errno) => return Err(errno), + } + } +} diff --git a/vendor/rustix/src/process/chroot.rs b/vendor/rustix/src/process/chroot.rs new file mode 100644 index 00000000..7c2476db --- /dev/null +++ b/vendor/rustix/src/process/chroot.rs @@ -0,0 +1,16 @@ +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +use crate::{backend, io, path}; + +/// `chroot(path)`—Change the process root directory. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/chroot.2.html +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +#[inline] +pub fn chroot<P: path::Arg>(path: P) -> io::Result<()> { + path.into_with_c_str(backend::process::syscalls::chroot) +} diff --git a/vendor/rustix/src/process/exit.rs b/vendor/rustix/src/process/exit.rs new file mode 100644 index 00000000..e882da14 --- /dev/null +++ b/vendor/rustix/src/process/exit.rs @@ -0,0 +1,36 @@ +use crate::backend; + +/// `EXIT_SUCCESS` for use with [`exit`]. +/// +/// [`exit`]: std::process::exit +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html +/// [Linux]: https://man7.org/linux/man-pages/man3/exit.3.html +pub const EXIT_SUCCESS: i32 = backend::c::EXIT_SUCCESS; + +/// `EXIT_FAILURE` for use with [`exit`]. +/// +/// [`exit`]: std::process::exit +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdlib.h.html +/// [Linux]: https://man7.org/linux/man-pages/man3/exit.3.html +pub const EXIT_FAILURE: i32 = backend::c::EXIT_FAILURE; + +/// The exit status used by a process terminated with a [`Signal::ABORT`] +/// signal. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://tldp.org/LDP/abs/html/exitcodes.html +/// [`Signal::ABORT`]: crate::process::Signal::ABORT +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub const EXIT_SIGNALED_SIGABRT: i32 = backend::c::EXIT_SIGNALED_SIGABRT; diff --git a/vendor/rustix/src/process/fcntl_getlk.rs b/vendor/rustix/src/process/fcntl_getlk.rs new file mode 100644 index 00000000..0dbdb945 --- /dev/null +++ b/vendor/rustix/src/process/fcntl_getlk.rs @@ -0,0 +1,23 @@ +use super::Flock; +use crate::fd::AsFd; +use crate::{backend, io}; + +/// `fcntl(fd, F_GETLK)`—Get the first lock that blocks the lock description +/// pointed to by the argument `lock`. If no such lock is found, then `None` is +/// returned. +/// +/// If `lock.typ` is set to `FlockType::Unlocked`, the returned value/error is +/// not explicitly defined, as per POSIX, and will depend on the underlying +/// platform implementation. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html +/// [Linux]: https://man7.org/linux/man-pages/man2/fcntl.2.html +#[inline] +#[doc(alias = "F_GETLK")] +pub fn fcntl_getlk<Fd: AsFd>(fd: Fd, lock: &Flock) -> io::Result<Option<Flock>> { + backend::process::syscalls::fcntl_getlk(fd.as_fd(), lock) +} diff --git a/vendor/rustix/src/process/id.rs b/vendor/rustix/src/process/id.rs new file mode 100644 index 00000000..2e9a228f --- /dev/null +++ b/vendor/rustix/src/process/id.rs @@ -0,0 +1,260 @@ +//! Unix user, group, and process identifiers. +//! +//! # Safety +//! +//! The `Uid`, `Gid`, and `Pid` types can be constructed from raw integers, +//! which is marked unsafe because actual OS's assign special meaning to some +//! integer values. +#![allow(unsafe_code)] + +use crate::{backend, io}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +pub use crate::pid::{Pid, RawPid}; +pub use crate::ugid::{Gid, RawGid, RawUid, Uid}; + +/// `getuid()`—Returns the process' real user ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getuid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getuid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getuid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getuid +/// [NetBSD]: https://man.netbsd.org/getuid.2 +#[inline] +#[must_use] +pub fn getuid() -> Uid { + backend::ugid::syscalls::getuid() +} + +/// `geteuid()`—Returns the process' effective user ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/geteuid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=geteuid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/geteuid +/// [NetBSD]: https://man.netbsd.org/geteuid.2 +#[inline] +#[must_use] +pub fn geteuid() -> Uid { + backend::ugid::syscalls::geteuid() +} + +/// `getgid()`—Returns the process' real group ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getgid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getgid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getgid +/// [NetBSD]: https://man.netbsd.org/getgid.2 +#[inline] +#[must_use] +pub fn getgid() -> Gid { + backend::ugid::syscalls::getgid() +} + +/// `getegid()`—Returns the process' effective group ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getegid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getegid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getegid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getegid +/// [NetBSD]: https://man.netbsd.org/getegid.2 +#[inline] +#[must_use] +pub fn getegid() -> Gid { + backend::ugid::syscalls::getegid() +} + +/// `getpid()`—Returns the process' ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getpid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getpid +/// [NetBSD]: https://man.netbsd.org/getpid.2 +#[inline] +#[must_use] +pub fn getpid() -> Pid { + backend::pid::syscalls::getpid() +} + +/// `getppid()`—Returns the parent process' ID. +/// +/// This will return `None` if the current process has no parent (or no parent +/// accessible in the current PID namespace), such as if the current process is +/// the init process ([`Pid::INIT`]). +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getppid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getppid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getppid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getppid +/// [NetBSD]: https://man.netbsd.org/getppid.2 +#[inline] +#[must_use] +pub fn getppid() -> Option<Pid> { + backend::process::syscalls::getppid() +} + +/// `getpgid(pid)`—Returns the process group ID of the given process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpgid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getpgid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getpgid +/// [NetBSD]: https://man.netbsd.org/getpgid.2 +#[inline] +pub fn getpgid(pid: Option<Pid>) -> io::Result<Pid> { + backend::process::syscalls::getpgid(pid) +} + +/// `setpgid(pid, pgid)`—Sets the process group ID of the given process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpgid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setpgid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=setpgid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/setpgid +/// [NetBSD]: https://man.netbsd.org/setpgid.2 +#[inline] +pub fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> { + backend::process::syscalls::setpgid(pid, pgid) +} + +/// `getpgrp()`—Returns the process' group ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgrp.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpgrp.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getpgrp&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getpgrp +/// [NetBSD]: https://man.netbsd.org/getpgrp.2 +#[inline] +#[must_use] +pub fn getpgrp() -> Pid { + backend::process::syscalls::getpgrp() +} + +/// `getsid(pid)`—Get the session ID of the given process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getsid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getsid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/getsid +/// [NetBSD]: https://man.netbsd.org/getsid.2 +#[cfg(not(target_os = "redox"))] +#[inline] +pub fn getsid(pid: Option<Pid>) -> io::Result<Pid> { + backend::process::syscalls::getsid(pid) +} + +/// `setsid()`—Create a new session. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [illumos] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setsid.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setsid.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=setsid&sektion=2 +/// [illumos]: https://www.illumos.org/man/2/setsid +/// [NetBSD]: https://man.netbsd.org/setsid.2 +#[inline] +pub fn setsid() -> io::Result<Pid> { + backend::process::syscalls::setsid() +} + +/// `getgroups()`—Return a list of the current user's groups. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [FreeBSD] +/// - [NetBSD] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgroups.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getgroups.2.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=getgroups&sektion=2 +/// [NetBSD]: https://man.netbsd.org/getgroups.2 +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub fn getgroups() -> io::Result<Vec<Gid>> { + // This code would benefit from having a better way to read into + // uninitialized memory, but that requires `unsafe`. + let mut buffer = Vec::with_capacity(0); + let ngroups = backend::process::syscalls::getgroups(&mut buffer)?; + buffer.resize(ngroups, Gid::ROOT); + backend::process::syscalls::getgroups(&mut buffer)?; + Ok(buffer) +} diff --git a/vendor/rustix/src/process/ioctl.rs b/vendor/rustix/src/process/ioctl.rs new file mode 100644 index 00000000..5362504f --- /dev/null +++ b/vendor/rustix/src/process/ioctl.rs @@ -0,0 +1,73 @@ +//! Process-oriented `ioctl`s. +//! +//! # Safety +//! +//! This module invokes `ioctl`s. + +#![allow(unsafe_code)] + +use crate::{backend, io, ioctl}; +use backend::c; +use backend::fd::AsFd; + +/// `ioctl(fd, TIOCSCTTY, 0)`—Sets the controlling terminal for the process. +/// +/// # References +/// - [Linux] +/// - [FreeBSD] +/// - [NetBSD] +/// - [OpenBSD] +/// +/// [Linux]: https://man7.org/linux/man-pages/man4/tty_ioctl.4.html +/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=tty&sektion=4 +/// [NetBSD]: https://man.netbsd.org/tty.4 +/// [OpenBSD]: https://man.openbsd.org/tty.4 +#[cfg(not(any( + windows, + target_os = "aix", + target_os = "horizon", + target_os = "redox", + target_os = "wasi" +)))] +#[inline] +#[doc(alias = "TIOCSCTTY")] +pub fn ioctl_tiocsctty<Fd: AsFd>(fd: Fd) -> io::Result<()> { + unsafe { ioctl::ioctl(fd, Tiocsctty) } +} + +#[cfg(not(any( + windows, + target_os = "aix", + target_os = "horizon", + target_os = "redox", + target_os = "wasi" +)))] +struct Tiocsctty; + +#[cfg(not(any( + windows, + target_os = "aix", + target_os = "horizon", + target_os = "redox", + target_os = "wasi" +)))] +unsafe impl ioctl::Ioctl for Tiocsctty { + type Output = (); + + const IS_MUTATING: bool = false; + + fn opcode(&self) -> ioctl::Opcode { + c::TIOCSCTTY as ioctl::Opcode + } + + fn as_ptr(&mut self) -> *mut c::c_void { + crate::utils::as_ptr(&0_u32) as *mut c::c_void + } + + unsafe fn output_from_ptr( + _: ioctl::IoctlOutput, + _: *mut c::c_void, + ) -> io::Result<Self::Output> { + Ok(()) + } +} diff --git a/vendor/rustix/src/process/kill.rs b/vendor/rustix/src/process/kill.rs new file mode 100644 index 00000000..8299b8ee --- /dev/null +++ b/vendor/rustix/src/process/kill.rs @@ -0,0 +1,98 @@ +use crate::process::Pid; +use crate::{backend, io}; + +pub use crate::signal::Signal; + +/// `kill(pid, sig)`—Sends a signal to a process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> { + backend::process::syscalls::kill_process(pid, sig) +} + +/// `kill(-pid, sig)`—Sends a signal to all processes in a process group. +/// +/// If `pid` is [`Pid::INIT`], this sends a signal to all processes the current +/// process has permission to send signals to, except process `Pid::INIT`, +/// possibly other system-specific processes, and on some systems, the current +/// process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> { + backend::process::syscalls::kill_process_group(pid, sig) +} + +/// `kill(0, sig)`—Sends a signal to all processes in the current process +/// group. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn kill_current_process_group(sig: Signal) -> io::Result<()> { + backend::process::syscalls::kill_current_process_group(sig) +} + +/// `kill(pid, 0)`—Check validity of pid and permissions to send signals to +/// the process, without actually sending any signals. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn test_kill_process(pid: Pid) -> io::Result<()> { + backend::process::syscalls::test_kill_process(pid) +} + +/// `kill(-pid, 0)`—Check validity of pid and permissions to send signals to +/// all processes in the process group, without actually sending any signals. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn test_kill_process_group(pid: Pid) -> io::Result<()> { + backend::process::syscalls::test_kill_process_group(pid) +} + +/// `kill(0, 0)`—Check validity of pid and permissions to send signals to the +/// all processes in the current process group, without actually sending any +/// signals. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html +/// [Linux]: https://man7.org/linux/man-pages/man2/kill.2.html +#[inline] +#[doc(alias = "kill")] +pub fn test_kill_current_process_group() -> io::Result<()> { + backend::process::syscalls::test_kill_current_process_group() +} diff --git a/vendor/rustix/src/process/mod.rs b/vendor/rustix/src/process/mod.rs new file mode 100644 index 00000000..7509b060 --- /dev/null +++ b/vendor/rustix/src/process/mod.rs @@ -0,0 +1,114 @@ +//! Process-associated operations. + +#[cfg(not(target_os = "wasi"))] +mod chdir; +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +mod chroot; +mod exit; +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +mod fcntl_getlk; +#[cfg(not(target_os = "wasi"))] // WASI doesn't have get[gpu]id. +mod id; +#[cfg(not(any(target_os = "aix", target_os = "espidf", target_os = "vita")))] +mod ioctl; +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +mod kill; +#[cfg(target_os = "linux")] +mod pidfd; +#[cfg(target_os = "linux")] +mod pidfd_getfd; +#[cfg(target_os = "linux")] +mod pivot_root; +#[cfg(linux_kernel)] +mod prctl; +#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +// WASI doesn't have [gs]etpriority. +mod priority; +#[cfg(freebsdlike)] +mod procctl; +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +mod rlimit; +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +mod types; +#[cfg(not(target_os = "wasi"))] // WASI doesn't have umask. +mod umask; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +mod wait; + +#[cfg(not(target_os = "wasi"))] +pub use chdir::*; +#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] +pub use chroot::*; +pub use exit::*; +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub use fcntl_getlk::*; +#[cfg(not(target_os = "wasi"))] +pub use id::*; +#[cfg(not(any(target_os = "aix", target_os = "espidf", target_os = "vita")))] +pub use ioctl::*; +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +pub use kill::*; +#[cfg(target_os = "linux")] +pub use pidfd::*; +#[cfg(target_os = "linux")] +pub use pidfd_getfd::*; +#[cfg(target_os = "linux")] +pub use pivot_root::*; +#[cfg(linux_kernel)] +pub use prctl::*; +#[cfg(not(any(target_os = "fuchsia", target_os = "vita", target_os = "wasi")))] +pub use priority::*; +#[cfg(freebsdlike)] +pub use procctl::*; +#[cfg(not(any( + target_os = "espidf", + target_os = "fuchsia", + target_os = "horizon", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub use rlimit::*; +#[cfg(not(any( + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "redox", + target_os = "vita", + target_os = "wasi" +)))] +pub use types::*; +#[cfg(not(target_os = "wasi"))] +pub use umask::*; +#[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))] +pub use wait::*; diff --git a/vendor/rustix/src/process/pidfd.rs b/vendor/rustix/src/process/pidfd.rs new file mode 100644 index 00000000..f812a9f6 --- /dev/null +++ b/vendor/rustix/src/process/pidfd.rs @@ -0,0 +1,43 @@ +use crate::fd::OwnedFd; +use crate::process::{Pid, Signal}; +use crate::{backend, ffi, io}; +use backend::fd::AsFd; + +bitflags::bitflags! { + /// `PIDFD_*` flags for use with [`pidfd_open`]. + /// + /// [`pidfd_open`]: crate::process::pidfd_open + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct PidfdFlags: ffi::c_uint { + /// `PIDFD_NONBLOCK` + const NONBLOCK = backend::c::PIDFD_NONBLOCK; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `syscall(SYS_pidfd_open, pid, flags)`—Creates a file descriptor for a +/// process. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html +#[inline] +pub fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> { + backend::process::syscalls::pidfd_open(pid, flags) +} + +/// `syscall(SYS_pidfd_send_signal, pidfd, sig, NULL, 0)`—Send a signal to a +/// process specified by a file descriptor. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html +#[inline] +pub fn pidfd_send_signal<Fd: AsFd>(pidfd: Fd, sig: Signal) -> io::Result<()> { + backend::process::syscalls::pidfd_send_signal(pidfd.as_fd(), sig) +} diff --git a/vendor/rustix/src/process/pidfd_getfd.rs b/vendor/rustix/src/process/pidfd_getfd.rs new file mode 100644 index 00000000..1be215e0 --- /dev/null +++ b/vendor/rustix/src/process/pidfd_getfd.rs @@ -0,0 +1,56 @@ +//! The [`pidfd_getfd`] function and supporting types. + +#![allow(unsafe_code)] +use crate::fd::OwnedFd; +use crate::{backend, ffi, io}; +use backend::fd::{AsFd, RawFd}; + +/// Raw file descriptor in another process. +/// +/// A distinct type alias is used here to inform the user that normal file +/// descriptors from the calling process should not be used. The provided file +/// descriptor is used by the kernel as the index into the file descriptor +/// table of an entirely different process. +pub type ForeignRawFd = RawFd; + +bitflags::bitflags! { + /// All flags are reserved for future use. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct PidfdGetfdFlags: ffi::c_uint { + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// `syscall(SYS_pidfd_getfd, pidfd, flags)`—Obtain a duplicate of another +/// process' file descriptor. +/// +/// # References +/// - [Linux] +/// +/// # Warning +/// +/// This function is generally safe for the calling process, but it can impact +/// the target process in unexpected ways. If you want to ensure that Rust I/O +/// safety assumptions continue to hold in the target process, then the target +/// process must have communicated the file description number to the calling +/// process from a value of a type that implements `AsRawFd`, and the target +/// process must not drop that value until after the calling process has +/// returned from `pidfd_getfd`. +/// +/// When `pidfd_getfd` is used to debug the target, or the target is not a Rust +/// application, or `pidfd_getfd` is used in any other way, then extra care +/// should be taken to avoid unexpected behaviour or crashes. +/// +/// For further details, see the references above. +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pidfd_getfd.2.html +#[inline] +pub fn pidfd_getfd<Fd: AsFd>( + pidfd: Fd, + targetfd: ForeignRawFd, + flags: PidfdGetfdFlags, +) -> io::Result<OwnedFd> { + backend::process::syscalls::pidfd_getfd(pidfd.as_fd(), targetfd, flags) +} diff --git a/vendor/rustix/src/process/pivot_root.rs b/vendor/rustix/src/process/pivot_root.rs new file mode 100644 index 00000000..91672774 --- /dev/null +++ b/vendor/rustix/src/process/pivot_root.rs @@ -0,0 +1,18 @@ +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +use crate::{backend, io, path}; + +/// `pivot_root(new_root, put_old)`—Change the root mount. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/pivot_root.2.html +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +#[inline] +pub fn pivot_root<P: path::Arg, Q: path::Arg>(new_root: P, put_old: Q) -> io::Result<()> { + new_root.into_with_c_str(|new_root| { + put_old.into_with_c_str(|put_old| backend::process::syscalls::pivot_root(new_root, put_old)) + }) +} diff --git a/vendor/rustix/src/process/prctl.rs b/vendor/rustix/src/process/prctl.rs new file mode 100644 index 00000000..8b08409c --- /dev/null +++ b/vendor/rustix/src/process/prctl.rs @@ -0,0 +1,1165 @@ +//! Bindings for the Linux `prctl` system call. +//! +//! There are similarities (but also differences) with FreeBSD's `procctl` +//! system call, whose interface is located in the `procctl.rs` file. + +#![allow(unsafe_code)] + +use core::mem::size_of; +use core::num::NonZeroI32; +use core::ptr::{null, null_mut, NonNull}; + +use bitflags::bitflags; + +use crate::backend::prctl::syscalls; +use crate::fd::{AsRawFd as _, BorrowedFd, RawFd}; +use crate::ffi::{c_int, c_uint, c_void, CStr}; +use crate::io; +use crate::prctl::*; +use crate::process::{Pid, RawPid}; +use crate::signal::Signal; +use crate::utils::{as_mut_ptr, as_ptr}; + +// +// PR_GET_PDEATHSIG/PR_SET_PDEATHSIG +// + +const PR_GET_PDEATHSIG: c_int = 2; + +/// Get the current value of the parent process death signal. +/// +/// # References +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] +/// +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +#[doc(alias = "PR_GET_PDEATHSIG")] +pub fn parent_process_death_signal() -> io::Result<Option<Signal>> { + let raw = unsafe { prctl_get_at_arg2_optional::<c_int>(PR_GET_PDEATHSIG)? }; + if let Some(non_zero) = NonZeroI32::new(raw) { + // SAFETY: The only way to get a libc-reserved signal number in + // here would be to do something equivalent to + // `set_parent_process_death_signal`, but that would have required + // using a `Signal` with a libc-reserved value. + Ok(Some(unsafe { + Signal::from_raw_nonzero_unchecked(non_zero) + })) + } else { + Ok(None) + } +} + +const PR_SET_PDEATHSIG: c_int = 1; + +/// Set the parent-death signal of the calling process. +/// +/// # References +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] +/// +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +#[doc(alias = "PR_SET_PDEATHSIG")] +pub fn set_parent_process_death_signal(signal: Option<Signal>) -> io::Result<()> { + let signal = signal.map_or(0_usize, |signal| signal.as_raw() as usize); + unsafe { prctl_2args(PR_SET_PDEATHSIG, signal as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_DUMPABLE/PR_SET_DUMPABLE +// + +const PR_GET_DUMPABLE: c_int = 3; + +const SUID_DUMP_DISABLE: i32 = 0; +const SUID_DUMP_USER: i32 = 1; +const SUID_DUMP_ROOT: i32 = 2; + +/// `SUID_DUMP_*` values for use with [`dumpable_behavior`] and +/// [`set_dumpable_behavior`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum DumpableBehavior { + /// Not dumpable. + #[doc(alias = "SUID_DUMP_DISABLE")] + NotDumpable = SUID_DUMP_DISABLE, + /// Dumpable. + #[doc(alias = "SUID_DUMP_USER")] + Dumpable = SUID_DUMP_USER, + /// Dumpable but only readable by root. + #[doc(alias = "SUID_DUMP_ROOT")] + DumpableReadableOnlyByRoot = SUID_DUMP_ROOT, +} + +impl TryFrom<i32> for DumpableBehavior { + type Error = io::Errno; + + fn try_from(value: i32) -> Result<Self, Self::Error> { + match value { + SUID_DUMP_DISABLE => Ok(Self::NotDumpable), + SUID_DUMP_USER => Ok(Self::Dumpable), + SUID_DUMP_ROOT => Ok(Self::DumpableReadableOnlyByRoot), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get the current state of the calling process' `dumpable` attribute. +/// +/// # References +/// - [`prctl(PR_GET_DUMPABLE,…)`] +/// +/// [`prctl(PR_GET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_DUMPABLE")] +pub fn dumpable_behavior() -> io::Result<DumpableBehavior> { + unsafe { prctl_1arg(PR_GET_DUMPABLE) }.and_then(TryInto::try_into) +} + +const PR_SET_DUMPABLE: c_int = 4; + +/// Set the state of the `dumpable` attribute. +/// +/// This attribute determines whether the process can be traced and whether +/// core dumps are produced for the calling process upon delivery of a signal +/// whose default behavior is to produce a core dump. +/// +/// A similar function with the same name is available on FreeBSD (as part of +/// the `procctl` interface), but it has an extra argument which allows to +/// select a process other then the current process. +/// +/// # References +/// - [`prctl(PR_SET_DUMPABLE,…)`] +/// +/// [`prctl(PR_SET_DUMPABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_DUMPABLE")] +pub fn set_dumpable_behavior(config: DumpableBehavior) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_DUMPABLE, config as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_UNALIGN/PR_SET_UNALIGN +// + +const PR_GET_UNALIGN: c_int = 5; + +bitflags! { + /// `PR_UNALIGN_*` flags for use with [`unaligned_access_control`] and + /// [`set_unaligned_access_control`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct UnalignedAccessControl: u32 { + /// Silently fix up unaligned user accesses. + #[doc(alias = "NOPRINT")] + #[doc(alias = "PR_UNALIGN_NOPRINT")] + const NO_PRINT = 1; + /// Generate a [`Signal::Bus`] signal on unaligned user access. + #[doc(alias = "PR_UNALIGN_SIGBUS")] + const SIGBUS = 2; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// Get unaligned access control bits. +/// +/// # References +/// - [`prctl(PR_GET_UNALIGN,…)`] +/// +/// [`prctl(PR_GET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_UNALIGN")] +pub fn unaligned_access_control() -> io::Result<UnalignedAccessControl> { + let r = unsafe { prctl_get_at_arg2_optional::<c_uint>(PR_GET_UNALIGN)? }; + UnalignedAccessControl::from_bits(r).ok_or(io::Errno::RANGE) +} + +const PR_SET_UNALIGN: c_int = 6; + +/// Set unaligned access control bits. +/// +/// # References +/// - [`prctl(PR_SET_UNALIGN,…)`] +/// +/// [`prctl(PR_SET_UNALIGN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_UNALIGN")] +pub fn set_unaligned_access_control(config: UnalignedAccessControl) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_UNALIGN, config.bits() as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_FPEMU/PR_SET_FPEMU +// + +const PR_GET_FPEMU: c_int = 9; + +bitflags! { + /// `PR_FPEMU_*` flags for use with [`floating_point_emulation_control`] + /// and [`set_floating_point_emulation_control`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FloatingPointEmulationControl: u32 { + /// Silently emulate floating point operations accesses. + #[doc(alias = "PR_UNALIGN_NOPRINT")] + const NO_PRINT = 1; + /// Don't emulate floating point operations, send a [`Signal::Fpe`] + /// signal instead. + #[doc(alias = "PR_UNALIGN_SIGFPE")] + const SIGFPE = 2; + } +} + +/// Get floating point emulation control bits. +/// +/// # References +/// - [`prctl(PR_GET_FPEMU,…)`] +/// +/// [`prctl(PR_GET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_FPEMU")] +pub fn floating_point_emulation_control() -> io::Result<FloatingPointEmulationControl> { + let r = unsafe { prctl_get_at_arg2_optional::<c_uint>(PR_GET_FPEMU)? }; + FloatingPointEmulationControl::from_bits(r).ok_or(io::Errno::RANGE) +} + +const PR_SET_FPEMU: c_int = 10; + +/// Set floating point emulation control bits. +/// +/// # References +/// - [`prctl(PR_SET_FPEMU,…)`] +/// +/// [`prctl(PR_SET_FPEMU,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_FPEMU")] +pub fn set_floating_point_emulation_control( + config: FloatingPointEmulationControl, +) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_FPEMU, config.bits() as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_FPEXC/PR_SET_FPEXC +// + +const PR_GET_FPEXC: c_int = 11; + +bitflags! { + /// Zero means floating point exceptions are disabled. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct FloatingPointExceptionMode: u32 { + /// Async non-recoverable exception mode. + const NONRECOV = 1; + /// Async recoverable exception mode. + const ASYNC = 2; + /// Precise exception mode. + const PRECISE = 3; + + /// Use FPEXC for floating point exception enables. + const SW_ENABLE = 0x80; + /// Floating point divide by zero. + const DIV = 0x01_0000; + /// Floating point overflow. + const OVF = 0x02_0000; + /// Floating point underflow. + const UND = 0x04_0000; + /// Floating point inexact result. + const RES = 0x08_0000; + /// Floating point invalid operation. + const INV = 0x10_0000; + } +} + +/// Get floating point exception mode. +/// +/// # References +/// - [`prctl(PR_GET_FPEXC,…)`] +/// +/// [`prctl(PR_GET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_FPEXEC")] +pub fn floating_point_exception_mode() -> io::Result<Option<FloatingPointExceptionMode>> { + unsafe { prctl_get_at_arg2_optional::<c_uint>(PR_GET_FPEXC) } + .map(FloatingPointExceptionMode::from_bits) +} + +const PR_SET_FPEXC: c_int = 12; + +/// Set floating point exception mode. +/// +/// # References +/// - [`prctl(PR_SET_FPEXC,…)`] +/// +/// [`prctl(PR_SET_FPEXC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_FPEXEC")] +pub fn set_floating_point_exception_mode( + config: Option<FloatingPointExceptionMode>, +) -> io::Result<()> { + let config = config.as_ref().map_or(0, FloatingPointExceptionMode::bits); + unsafe { prctl_2args(PR_SET_FPEXC, config as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_TIMING/PR_SET_TIMING +// + +const PR_GET_TIMING: c_int = 13; + +const PR_TIMING_STATISTICAL: i32 = 0; +const PR_TIMING_TIMESTAMP: i32 = 1; + +/// `PR_TIMING_*` values for use with [`timing_method`] and +/// [`set_timing_method`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum TimingMethod { + /// Normal, traditional, statistical process timing. + Statistical = PR_TIMING_STATISTICAL, + /// Accurate timestamp based process timing. + TimeStamp = PR_TIMING_TIMESTAMP, +} + +impl TryFrom<i32> for TimingMethod { + type Error = io::Errno; + + fn try_from(value: i32) -> Result<Self, Self::Error> { + match value { + PR_TIMING_STATISTICAL => Ok(Self::Statistical), + PR_TIMING_TIMESTAMP => Ok(Self::TimeStamp), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get which process timing method is currently in use. +/// +/// # References +/// - [`prctl(PR_GET_TIMING,…)`] +/// +/// [`prctl(PR_GET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_TIMING")] +pub fn timing_method() -> io::Result<TimingMethod> { + unsafe { prctl_1arg(PR_GET_TIMING) }.and_then(TryInto::try_into) +} + +const PR_SET_TIMING: c_int = 14; + +/// Set whether to use (normal, traditional) statistical process timing or +/// accurate timestamp-based process timing. +/// +/// # References +/// - [`prctl(PR_SET_TIMING,…)`] +/// +/// [`prctl(PR_SET_TIMING,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_TIMING")] +pub fn set_timing_method(method: TimingMethod) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_TIMING, method as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_ENDIAN/PR_SET_ENDIAN +// + +const PR_GET_ENDIAN: c_int = 19; + +const PR_ENDIAN_BIG: u32 = 0; +const PR_ENDIAN_LITTLE: u32 = 1; +const PR_ENDIAN_PPC_LITTLE: u32 = 2; + +/// `PR_ENDIAN_*` values for use with [`endian_mode`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum EndianMode { + /// Big endian mode. + Big = PR_ENDIAN_BIG, + /// True little endian mode. + Little = PR_ENDIAN_LITTLE, + /// `PowerPC` pseudo little endian. + PowerPCLittle = PR_ENDIAN_PPC_LITTLE, +} + +impl TryFrom<u32> for EndianMode { + type Error = io::Errno; + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + PR_ENDIAN_BIG => Ok(Self::Big), + PR_ENDIAN_LITTLE => Ok(Self::Little), + PR_ENDIAN_PPC_LITTLE => Ok(Self::PowerPCLittle), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get the endianness of the calling process. +/// +/// # References +/// - [`prctl(PR_GET_ENDIAN,…)`] +/// +/// [`prctl(PR_GET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_ENDIAN")] +pub fn endian_mode() -> io::Result<EndianMode> { + unsafe { prctl_get_at_arg2::<c_uint, _>(PR_GET_ENDIAN) } +} + +const PR_SET_ENDIAN: c_int = 20; + +/// Set the endianness of the calling process. +/// +/// # References +/// - [`prctl(PR_SET_ENDIAN,…)`] +/// +/// # Safety +/// +/// Please ensure the conditions necessary to safely call this function, as +/// detailed in the references above. +/// +/// [`prctl(PR_SET_ENDIAN,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_ENDIAN")] +pub unsafe fn set_endian_mode(mode: EndianMode) -> io::Result<()> { + prctl_2args(PR_SET_ENDIAN, mode as usize as *mut _).map(|_r| ()) +} + +// +// PR_GET_TSC/PR_SET_TSC +// + +const PR_GET_TSC: c_int = 25; + +const PR_TSC_ENABLE: u32 = 1; +const PR_TSC_SIGSEGV: u32 = 2; + +/// `PR_TSC_*` values for use with [`time_stamp_counter_readability`] and +/// [`set_time_stamp_counter_readability`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum TimeStampCounterReadability { + /// Allow the use of the timestamp counter. + Readable = PR_TSC_ENABLE, + /// Throw a [`Signal::SEGV`] signal instead of reading the TSC. + RaiseSIGSEGV = PR_TSC_SIGSEGV, +} + +impl TryFrom<u32> for TimeStampCounterReadability { + type Error = io::Errno; + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + PR_TSC_ENABLE => Ok(Self::Readable), + PR_TSC_SIGSEGV => Ok(Self::RaiseSIGSEGV), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get the state of the flag determining if the timestamp counter can be read. +/// +/// # References +/// - [`prctl(PR_GET_TSC,…)`] +/// +/// [`prctl(PR_GET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_TSC")] +pub fn time_stamp_counter_readability() -> io::Result<TimeStampCounterReadability> { + unsafe { prctl_get_at_arg2::<c_uint, _>(PR_GET_TSC) } +} + +const PR_SET_TSC: c_int = 26; + +/// Set the state of the flag determining if the timestamp counter can be read +/// by the process. +/// +/// # References +/// - [`prctl(PR_SET_TSC,…)`] +/// +/// [`prctl(PR_SET_TSC,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_TSC")] +pub fn set_time_stamp_counter_readability( + readability: TimeStampCounterReadability, +) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_TSC, readability as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_TASK_PERF_EVENTS_DISABLE/PR_TASK_PERF_EVENTS_ENABLE +// + +const PR_TASK_PERF_EVENTS_DISABLE: c_int = 31; +const PR_TASK_PERF_EVENTS_ENABLE: c_int = 32; + +/// Enable or disable all performance counters attached to the calling process. +/// +/// # References +/// - [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`] +/// - [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`] +/// +/// [`prctl(PR_TASK_PERF_EVENTS_ENABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [`prctl(PR_TASK_PERF_EVENTS_DISABLE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_TASK_PERF_EVENTS_ENABLE")] +#[doc(alias = "PR_TASK_PERF_EVENTS_DISABLE")] +pub fn configure_performance_counters(enable: bool) -> io::Result<()> { + let option = if enable { + PR_TASK_PERF_EVENTS_ENABLE + } else { + PR_TASK_PERF_EVENTS_DISABLE + }; + + unsafe { prctl_1arg(option) }.map(|_r| ()) +} + +// +// PR_MCE_KILL_GET/PR_MCE_KILL +// + +const PR_MCE_KILL_GET: c_int = 34; + +const PR_MCE_KILL_LATE: u32 = 0; +const PR_MCE_KILL_EARLY: u32 = 1; +const PR_MCE_KILL_DEFAULT: u32 = 2; + +/// `PR_MCE_KILL_*` values for use with +/// [`machine_check_memory_corruption_kill_policy`] and +/// [`set_machine_check_memory_corruption_kill_policy`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum MachineCheckMemoryCorruptionKillPolicy { + /// Late kill policy. + #[doc(alias = "PR_MCE_KILL_LATE")] + Late = PR_MCE_KILL_LATE, + /// Early kill policy. + #[doc(alias = "PR_MCE_KILL_EARLY")] + Early = PR_MCE_KILL_EARLY, + /// System-wide default policy. + #[doc(alias = "PR_MCE_KILL_DEFAULT")] + Default = PR_MCE_KILL_DEFAULT, +} + +impl TryFrom<u32> for MachineCheckMemoryCorruptionKillPolicy { + type Error = io::Errno; + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + PR_MCE_KILL_LATE => Ok(Self::Late), + PR_MCE_KILL_EARLY => Ok(Self::Early), + PR_MCE_KILL_DEFAULT => Ok(Self::Default), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get the current per-process machine check kill policy. +/// +/// # References +/// - [`prctl(PR_MCE_KILL_GET,…)`] +/// +/// [`prctl(PR_MCE_KILL_GET,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_MCE_KILL_GET")] +pub fn machine_check_memory_corruption_kill_policy( +) -> io::Result<MachineCheckMemoryCorruptionKillPolicy> { + let r = unsafe { prctl_1arg(PR_MCE_KILL_GET)? } as c_uint; + MachineCheckMemoryCorruptionKillPolicy::try_from(r) +} + +const PR_MCE_KILL: c_int = 33; + +const PR_MCE_KILL_CLEAR: usize = 0; +const PR_MCE_KILL_SET: usize = 1; + +/// Set the machine check memory corruption kill policy for the calling thread. +/// +/// # References +/// - [`prctl(PR_MCE_KILL,…)`] +/// +/// [`prctl(PR_MCE_KILL,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_MCE_KILL")] +pub fn set_machine_check_memory_corruption_kill_policy( + policy: Option<MachineCheckMemoryCorruptionKillPolicy>, +) -> io::Result<()> { + let (sub_operation, policy) = if let Some(policy) = policy { + (PR_MCE_KILL_SET, policy as usize as *mut _) + } else { + (PR_MCE_KILL_CLEAR, null_mut()) + }; + + unsafe { prctl_3args(PR_MCE_KILL, sub_operation as *mut _, policy) }.map(|_r| ()) +} + +// +// PR_SET_MM +// + +const PR_SET_MM: c_int = 35; + +const PR_SET_MM_START_CODE: u32 = 1; +const PR_SET_MM_END_CODE: u32 = 2; +const PR_SET_MM_START_DATA: u32 = 3; +const PR_SET_MM_END_DATA: u32 = 4; +const PR_SET_MM_START_STACK: u32 = 5; +const PR_SET_MM_START_BRK: u32 = 6; +const PR_SET_MM_BRK: u32 = 7; +const PR_SET_MM_ARG_START: u32 = 8; +const PR_SET_MM_ARG_END: u32 = 9; +const PR_SET_MM_ENV_START: u32 = 10; +const PR_SET_MM_ENV_END: u32 = 11; +const PR_SET_MM_AUXV: usize = 12; +const PR_SET_MM_EXE_FILE: usize = 13; +const PR_SET_MM_MAP: usize = 14; +const PR_SET_MM_MAP_SIZE: usize = 15; + +/// `PR_SET_MM_*` values for use with [`set_virtual_memory_map_address`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum VirtualMemoryMapAddress { + /// Set the address above which the program text can run. + CodeStart = PR_SET_MM_START_CODE, + /// Set the address below which the program text can run. + CodeEnd = PR_SET_MM_END_CODE, + /// Set the address above which initialized and uninitialized (bss) data + /// are placed. + DataStart = PR_SET_MM_START_DATA, + /// Set the address below which initialized and uninitialized (bss) data + /// are placed. + DataEnd = PR_SET_MM_END_DATA, + /// Set the start address of the stack. + StackStart = PR_SET_MM_START_STACK, + /// Set the address above which the program heap can be expanded with `brk` + /// call. + BrkStart = PR_SET_MM_START_BRK, + /// Set the current `brk` value. + BrkCurrent = PR_SET_MM_BRK, + /// Set the address above which the program command line is placed. + ArgStart = PR_SET_MM_ARG_START, + /// Set the address below which the program command line is placed. + ArgEnd = PR_SET_MM_ARG_END, + /// Set the address above which the program environment is placed. + EnvironmentStart = PR_SET_MM_ENV_START, + /// Set the address below which the program environment is placed. + EnvironmentEnd = PR_SET_MM_ENV_END, +} + +/// Modify certain kernel memory map descriptor addresses of the calling +/// process. +/// +/// # References +/// - [`prctl(PR_SET_MM,…)`] +/// +/// # Safety +/// +/// Please ensure the conditions necessary to safely call this function, as +/// detailed in the references above. +/// +/// [`prctl(PR_SET_MM,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_MM")] +pub unsafe fn set_virtual_memory_map_address( + option: VirtualMemoryMapAddress, + address: Option<NonNull<c_void>>, +) -> io::Result<()> { + let address = address.map_or_else(null_mut, NonNull::as_ptr); + prctl_3args(PR_SET_MM, option as usize as *mut _, address).map(|_r| ()) +} + +/// Supersede the `/proc/pid/exe` symbolic link with a new one pointing to a +/// new executable file. +/// +/// # References +/// - [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`] +/// +/// [`prctl(PR_SET_MM,PR_SET_MM_EXE_FILE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_MM")] +#[doc(alias = "PR_SET_MM_EXE_FILE")] +pub fn set_executable_file(fd: BorrowedFd<'_>) -> io::Result<()> { + let fd = usize::try_from(fd.as_raw_fd()).map_err(|_r| io::Errno::RANGE)?; + unsafe { prctl_3args(PR_SET_MM, PR_SET_MM_EXE_FILE as *mut _, fd as *mut _) }.map(|_r| ()) +} + +/// Set a new auxiliary vector. +/// +/// # References +/// - [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`] +/// +/// # Safety +/// +/// Please ensure the conditions necessary to safely call this function, as +/// detailed in the references above. +/// +/// [`prctl(PR_SET_MM,PR_SET_MM_AUXV,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_MM")] +#[doc(alias = "PR_SET_MM_AUXV")] +pub unsafe fn set_auxiliary_vector(auxv: &[*const c_void]) -> io::Result<()> { + syscalls::prctl( + PR_SET_MM, + PR_SET_MM_AUXV as *mut _, + auxv.as_ptr() as *mut _, + auxv.len() as *mut _, + null_mut(), + ) + .map(|_r| ()) +} + +/// Get the size of the [`PrctlMmMap`] the kernel expects. +/// +/// # References +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`] +/// +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP_SIZE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_MM")] +#[doc(alias = "PR_SET_MM_MAP_SIZE")] +pub fn virtual_memory_map_config_struct_size() -> io::Result<usize> { + let mut value: c_uint = 0; + let value_ptr = as_mut_ptr(&mut value); + unsafe { prctl_3args(PR_SET_MM, PR_SET_MM_MAP_SIZE as *mut _, value_ptr.cast())? }; + Ok(value as usize) +} + +/// This structure provides new memory descriptor map which mostly modifies +/// `/proc/pid/stat[m]` output for a task. +/// This mostly done in a sake of checkpoint/restore functionality. +#[repr(C)] +#[derive(Debug, Clone)] +pub struct PrctlMmMap { + /// Code section start address. + pub start_code: u64, + /// Code section end address. + pub end_code: u64, + /// Data section start address. + pub start_data: u64, + /// Data section end address. + pub end_data: u64, + /// `brk` start address. + pub start_brk: u64, + /// `brk` current address. + pub brk: u64, + /// Stack start address. + pub start_stack: u64, + /// Program command line start address. + pub arg_start: u64, + /// Program command line end address. + pub arg_end: u64, + /// Program environment start address. + pub env_start: u64, + /// Program environment end address. + pub env_end: u64, + /// Auxiliary vector start address. + pub auxv: *mut u64, + /// Auxiliary vector size. + pub auxv_size: u32, + /// File descriptor of executable file that was used to create this + /// process. + pub exe_fd: RawFd, +} + +/// Provides one-shot access to all the addresses by passing in a +/// [`PrctlMmMap`]. +/// +/// # References +/// - [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`] +/// +/// # Safety +/// +/// Please ensure the conditions necessary to safely call this function, as +/// detailed in the references above. +/// +/// [`prctl(PR_SET_MM,PR_SET_MM_MAP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_MM")] +#[doc(alias = "PR_SET_MM_MAP")] +pub unsafe fn configure_virtual_memory_map(config: &PrctlMmMap) -> io::Result<()> { + syscalls::prctl( + PR_SET_MM, + PR_SET_MM_MAP as *mut _, + as_ptr(config) as *mut _, + size_of::<PrctlMmMap>() as *mut _, + null_mut(), + ) + .map(|_r| ()) +} + +// +// PR_SET_PTRACER +// + +const PR_SET_PTRACER: c_int = 0x59_61_6d_61; + +const PR_SET_PTRACER_ANY: usize = usize::MAX; + +/// Process ptracer. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PTracer { + /// None. + None, + /// Disable `ptrace` restrictions for the calling process. + Any, + /// Specific process. + ProcessID(Pid), +} + +/// Declare that the ptracer process can `ptrace` the calling process as if it +/// were a direct process ancestor. +/// +/// # References +/// - [`prctl(PR_SET_PTRACER,…)`] +/// +/// [`prctl(PR_SET_PTRACER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_PTRACER")] +pub fn set_ptracer(tracer: PTracer) -> io::Result<()> { + let pid = match tracer { + PTracer::None => null_mut(), + PTracer::Any => PR_SET_PTRACER_ANY as *mut _, + PTracer::ProcessID(pid) => pid.as_raw_nonzero().get() as usize as *mut _, + }; + + unsafe { prctl_2args(PR_SET_PTRACER, pid) }.map(|_r| ()) +} + +// +// PR_GET_CHILD_SUBREAPER/PR_SET_CHILD_SUBREAPER +// + +const PR_GET_CHILD_SUBREAPER: c_int = 37; + +/// Get the `child subreaper` setting of the calling process. +/// +/// # References +/// - [`prctl(PR_GET_CHILD_SUBREAPER,…)`] +/// +/// [`prctl(PR_GET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_CHILD_SUBREAPER")] +pub fn child_subreaper() -> io::Result<Option<Pid>> { + unsafe { + let r = prctl_get_at_arg2_optional::<c_uint>(PR_GET_CHILD_SUBREAPER)?; + Ok(Pid::from_raw(r as RawPid)) + } +} + +const PR_SET_CHILD_SUBREAPER: c_int = 36; + +/// Set the `child subreaper` attribute of the calling process. +/// +/// # References +/// - [`prctl(PR_SET_CHILD_SUBREAPER,…)`] +/// +/// [`prctl(PR_SET_CHILD_SUBREAPER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_CHILD_SUBREAPER")] +pub fn set_child_subreaper(pid: Option<Pid>) -> io::Result<()> { + let pid = pid.map_or(0_usize, |pid| pid.as_raw_nonzero().get() as usize); + unsafe { prctl_2args(PR_SET_CHILD_SUBREAPER, pid as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_FP_MODE/PR_SET_FP_MODE +// + +const PR_GET_FP_MODE: c_int = 46; + +const PR_FP_MODE_FR: u32 = 1_u32 << 0; +const PR_FP_MODE_FRE: u32 = 1_u32 << 1; + +/// `PR_FP_MODE_*` values for use with [`floating_point_mode`] and +/// [`set_floating_point_mode`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum FloatingPointMode { + /// 64-bit floating point registers. + FloatingPointRegisters = PR_FP_MODE_FR, + /// Enable emulation of 32-bit floating-point mode. + FloatingPointEmulation = PR_FP_MODE_FRE, +} + +impl TryFrom<u32> for FloatingPointMode { + type Error = io::Errno; + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + PR_FP_MODE_FR => Ok(Self::FloatingPointRegisters), + PR_FP_MODE_FRE => Ok(Self::FloatingPointEmulation), + _ => Err(io::Errno::RANGE), + } + } +} + +/// Get the current floating point mode. +/// +/// # References +/// - [`prctl(PR_GET_FP_MODE,…)`] +/// +/// [`prctl(PR_GET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_FP_MODE")] +pub fn floating_point_mode() -> io::Result<FloatingPointMode> { + let r = unsafe { prctl_1arg(PR_GET_FP_MODE)? } as c_uint; + FloatingPointMode::try_from(r) +} + +const PR_SET_FP_MODE: c_int = 45; + +/// Allow control of the floating point mode from user space. +/// +/// # References +/// - [`prctl(PR_SET_FP_MODE,…)`] +/// +/// [`prctl(PR_SET_FP_MODE,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_FP_MODE")] +pub fn set_floating_point_mode(mode: FloatingPointMode) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_FP_MODE, mode as usize as *mut _) }.map(|_r| ()) +} + +// +// PR_GET_SPECULATION_CTRL/PR_SET_SPECULATION_CTRL +// + +const PR_GET_SPECULATION_CTRL: c_int = 52; + +const PR_SPEC_STORE_BYPASS: u32 = 0; +const PR_SPEC_INDIRECT_BRANCH: u32 = 1; +const PR_SPEC_L1D_FLUSH: u32 = 2; + +/// `PR_SPEC_*` values for use with [`speculative_feature_state`] and +/// [`control_speculative_feature`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum SpeculationFeature { + /// Set the state of the speculative store bypass misfeature. + SpeculativeStoreBypass = PR_SPEC_STORE_BYPASS, + /// Set the state of the indirect branch speculation misfeature. + IndirectBranchSpeculation = PR_SPEC_INDIRECT_BRANCH, + /// Flush L1D Cache on context switch out of the task. + FlushL1DCacheOnContextSwitchOutOfTask = PR_SPEC_L1D_FLUSH, +} + +impl TryFrom<u32> for SpeculationFeature { + type Error = io::Errno; + + fn try_from(value: u32) -> Result<Self, Self::Error> { + match value { + PR_SPEC_STORE_BYPASS => Ok(Self::SpeculativeStoreBypass), + PR_SPEC_INDIRECT_BRANCH => Ok(Self::IndirectBranchSpeculation), + PR_SPEC_L1D_FLUSH => Ok(Self::FlushL1DCacheOnContextSwitchOutOfTask), + _ => Err(io::Errno::RANGE), + } + } +} + +bitflags! { + /// `PR_SPEC_*` flags for use with [`control_speculative_feature`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SpeculationFeatureControl: u32 { + /// The speculation feature is enabled, mitigation is disabled. + const ENABLE = 1_u32 << 1; + /// The speculation feature is disabled, mitigation is enabled. + const DISABLE = 1_u32 << 2; + /// The speculation feature is disabled, mitigation is enabled, and it + /// cannot be undone. + const FORCE_DISABLE = 1_u32 << 3; + /// The speculation feature is disabled, mitigation is enabled, and the + /// state will be cleared on `execve`. + const DISABLE_NOEXEC = 1_u32 << 4; + } +} + +bitflags! { + /// Zero means the processors are not vulnerable. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SpeculationFeatureState: u32 { + /// Mitigation can be controlled per thread by + /// [`control_speculative_feature`]. + const PRCTL = 1_u32 << 0; + /// The speculation feature is enabled, mitigation is disabled. + const ENABLE = 1_u32 << 1; + /// The speculation feature is disabled, mitigation is enabled. + const DISABLE = 1_u32 << 2; + /// The speculation feature is disabled, mitigation is enabled, and it + /// cannot be undone. + const FORCE_DISABLE = 1_u32 << 3; + /// The speculation feature is disabled, mitigation is enabled, and the + /// state will be cleared on `execve`. + const DISABLE_NOEXEC = 1_u32 << 4; + } +} + +/// Get the state of the speculation misfeature. +/// +/// # References +/// - [`prctl(PR_GET_SPECULATION_CTRL,…)`] +/// +/// [`prctl(PR_GET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.13/userspace-api/spec_ctrl.html +#[inline] +#[doc(alias = "PR_GET_SPECULATION_CTRL")] +pub fn speculative_feature_state( + feature: SpeculationFeature, +) -> io::Result<Option<SpeculationFeatureState>> { + let r = unsafe { prctl_2args(PR_GET_SPECULATION_CTRL, feature as usize as *mut _)? } as c_uint; + Ok(SpeculationFeatureState::from_bits(r)) +} + +const PR_SET_SPECULATION_CTRL: c_int = 53; + +/// Sets the state of the speculation misfeature. +/// +/// # References +/// - [`prctl(PR_SET_SPECULATION_CTRL,…)`] +/// +/// [`prctl(PR_SET_SPECULATION_CTRL,…)`]: https://www.kernel.org/doc/html/v6.13/userspace-api/spec_ctrl.html +#[inline] +#[doc(alias = "PR_SET_SPECULATION_CTRL")] +pub fn control_speculative_feature( + feature: SpeculationFeature, + config: SpeculationFeatureControl, +) -> io::Result<()> { + let feature = feature as usize as *mut _; + let config = config.bits() as usize as *mut _; + unsafe { prctl_3args(PR_SET_SPECULATION_CTRL, feature, config) }.map(|_r| ()) +} + +// +// PR_GET_IO_FLUSHER/PR_SET_IO_FLUSHER +// + +const PR_GET_IO_FLUSHER: c_int = 58; + +/// Get the `IO_FLUSHER` state of the caller. +/// +/// # References +/// - [`prctl(PR_GET_IO_FLUSHER,…)`] +/// +/// [`prctl(PR_GET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_GET_IO_FLUSHER")] +pub fn is_io_flusher() -> io::Result<bool> { + unsafe { prctl_1arg(PR_GET_IO_FLUSHER) }.map(|r| r != 0) +} + +const PR_SET_IO_FLUSHER: c_int = 57; + +/// Put the process in the `IO_FLUSHER` state, allowing it to make progress +/// when allocating memory. +/// +/// # References +/// - [`prctl(PR_SET_IO_FLUSHER,…)`] +/// +/// [`prctl(PR_SET_IO_FLUSHER,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +#[inline] +#[doc(alias = "PR_SET_IO_FLUSHER")] +pub fn configure_io_flusher_behavior(enable: bool) -> io::Result<()> { + unsafe { prctl_2args(PR_SET_IO_FLUSHER, usize::from(enable) as *mut _) }.map(|_r| ()) +} + +// +// PR_PAC_GET_ENABLED_KEYS/PR_PAC_SET_ENABLED_KEYS +// + +const PR_PAC_GET_ENABLED_KEYS: c_int = 61; + +/// Get enabled pointer authentication keys. +/// +/// # References +/// - [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`] +/// +/// [`prctl(PR_PAC_GET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.13/arch/arm64/pointer-authentication.html +#[inline] +#[doc(alias = "PR_PAC_GET_ENABLED_KEYS")] +pub fn enabled_pointer_authentication_keys() -> io::Result<PointerAuthenticationKeys> { + let r = unsafe { prctl_1arg(PR_PAC_GET_ENABLED_KEYS)? } as c_uint; + PointerAuthenticationKeys::from_bits(r).ok_or(io::Errno::RANGE) +} + +const PR_PAC_SET_ENABLED_KEYS: c_int = 60; + +/// Set enabled pointer authentication keys. +/// +/// # References +/// - [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`] +/// +/// # Safety +/// +/// Please ensure the conditions necessary to safely call this function, as +/// detailed in the references above. +/// +/// [`prctl(PR_PAC_SET_ENABLED_KEYS,…)`]: https://www.kernel.org/doc/html/v6.13/arch/arm64/pointer-authentication.html +#[inline] +#[doc(alias = "PR_PAC_SET_ENABLED_KEYS")] +pub unsafe fn configure_pointer_authentication_keys< + Config: Iterator<Item = (PointerAuthenticationKeys, bool)>, +>( + config: Config, +) -> io::Result<()> { + let mut affected_keys: u32 = 0; + let mut enabled_keys: u32 = 0; + + for (key, enable) in config { + let key = key.bits(); + affected_keys |= key; + + if enable { + enabled_keys |= key; + } else { + enabled_keys &= !key; + } + } + + if affected_keys == 0 { + return Ok(()); // Nothing to do. + } + + prctl_3args( + PR_PAC_SET_ENABLED_KEYS, + affected_keys as usize as *mut _, + enabled_keys as usize as *mut _, + ) + .map(|_r| ()) +} + +// +// PR_SET_VMA +// + +const PR_SET_VMA: c_int = 0x53_56_4d_41; + +const PR_SET_VMA_ANON_NAME: usize = 0; + +/// Set the name for a virtual memory region. +/// +/// # References +/// - [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`] +/// +/// [`prctl(PR_SET_VMA,PR_SET_VMA_ANON_NAME,…)`]: https://lwn.net/Articles/867818/ +#[inline] +#[doc(alias = "PR_SET_VMA")] +#[doc(alias = "PR_SET_VMA_ANON_NAME")] +pub fn set_virtual_memory_region_name(region: &[u8], name: Option<&CStr>) -> io::Result<()> { + unsafe { + syscalls::prctl( + PR_SET_VMA, + PR_SET_VMA_ANON_NAME as *mut _, + region.as_ptr() as *mut _, + region.len() as *mut _, + name.map_or_else(null, CStr::as_ptr) as *mut _, + ) + .map(|_r| ()) + } +} diff --git a/vendor/rustix/src/process/priority.rs b/vendor/rustix/src/process/priority.rs new file mode 100644 index 00000000..0051b068 --- /dev/null +++ b/vendor/rustix/src/process/priority.rs @@ -0,0 +1,132 @@ +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +use crate::process::{Pid, Uid}; +use crate::{backend, io}; + +/// `nice(inc)`—Adjust the scheduling priority of the current process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/nice.html +/// [Linux]: https://man7.org/linux/man-pages/man2/nice.2.html +#[inline] +pub fn nice(inc: i32) -> io::Result<i32> { + backend::process::syscalls::nice(inc) +} + +/// `getpriority(PRIO_USER, uid)`—Get the scheduling priority of the given +/// user. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "getpriority")] +pub fn getpriority_user(uid: Uid) -> io::Result<i32> { + backend::process::syscalls::getpriority_user(uid) +} + +/// `getpriority(PRIO_PGRP, gid)`—Get the scheduling priority of the given +/// process group. +/// +/// A `pgid` of `None` means the process group of the calling process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "getpriority")] +pub fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> { + backend::process::syscalls::getpriority_pgrp(pgid) +} + +/// `getpriority(PRIO_PROCESS, pid)`—Get the scheduling priority of the given +/// process. +/// +/// A `pid` of `None` means the calling process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "getpriority")] +pub fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> { + backend::process::syscalls::getpriority_process(pid) +} + +/// `setpriority(PRIO_USER, uid)`—Get the scheduling priority of the given +/// user. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "setpriority")] +pub fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> { + backend::process::syscalls::setpriority_user(uid, priority) +} + +/// `setpriority(PRIO_PGRP, pgid)`—Get the scheduling priority of the given +/// process group. +/// +/// A `pgid` of `None` means the process group of the calling process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "setpriority")] +pub fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> { + backend::process::syscalls::setpriority_pgrp(pgid, priority) +} + +/// `setpriority(PRIO_PROCESS, pid)`—Get the scheduling priority of the given +/// process. +/// +/// A `pid` of `None` means the calling process. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// - [Apple] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpriority.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setpriority.2.html +/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setpriority.2.html +#[cfg(not(any(target_os = "espidf", target_os = "horizon")))] +#[inline] +#[doc(alias = "setpriority")] +pub fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> { + backend::process::syscalls::setpriority_process(pid, priority) +} diff --git a/vendor/rustix/src/process/procctl.rs b/vendor/rustix/src/process/procctl.rs new file mode 100644 index 00000000..24d55302 --- /dev/null +++ b/vendor/rustix/src/process/procctl.rs @@ -0,0 +1,547 @@ +//! Bindings for the FreeBSD `procctl` system call. +//! +//! There are similarities (but also differences) with Linux's `prctl` system +//! call, whose interface is located in the `prctl.rs` file. + +#![allow(unsafe_code)] + +#[cfg(feature = "alloc")] +use alloc::{vec, vec::Vec}; +use core::mem::MaybeUninit; +use core::num::NonZeroI32; +use core::ptr; + +use bitflags::bitflags; + +use crate::backend::process::syscalls; +use crate::backend::process::types::RawId; +use crate::ffi::{c_int, c_uint, c_void}; +use crate::io; +use crate::process::{Pid, RawPid}; +use crate::signal::Signal; +use crate::utils::{as_mut_ptr, as_ptr}; + +// +// Helper functions. +// + +/// Subset of `idtype_t` C enum, with only the values allowed by `procctl`. +#[repr(i32)] +pub enum IdType { + /// Process id. + Pid = 0, + /// Process group id. + Pgid = 2, +} + +/// A process selector for use with the `procctl` interface. +/// +/// `None` represents the current process. `Some((IdType::Pid, pid))` +/// represents the process with pid `pid`. `Some((IdType::Pgid, pgid))` +/// represents the control processes belonging to the process group with id +/// `pgid`. +pub type ProcSelector = Option<(IdType, Pid)>; +fn proc_selector_to_raw(selector: ProcSelector) -> (IdType, RawPid) { + match selector { + Some((idtype, id)) => (idtype, id.as_raw_nonzero().get()), + None => (IdType::Pid, 0), + } +} + +#[inline] +pub(crate) unsafe fn procctl( + option: c_int, + process: ProcSelector, + data: *mut c_void, +) -> io::Result<()> { + let (idtype, id) = proc_selector_to_raw(process); + syscalls::procctl(idtype as c_uint, id as RawId, option, data) +} + +#[inline] +pub(crate) unsafe fn procctl_set<P>( + option: c_int, + process: ProcSelector, + data: &P, +) -> io::Result<()> { + procctl(option, process, (as_ptr(data) as *mut P).cast()) +} + +#[inline] +pub(crate) unsafe fn procctl_get_optional<P>( + option: c_int, + process: ProcSelector, +) -> io::Result<P> { + let mut value: MaybeUninit<P> = MaybeUninit::uninit(); + procctl(option, process, value.as_mut_ptr().cast())?; + Ok(value.assume_init()) +} + +// +// PROC_PDEATHSIG_STATUS/PROC_PDEATHSIG_CTL +// + +const PROC_PDEATHSIG_STATUS: c_int = 12; + +/// Get the current value of the parent process death signal. +/// +/// # References +/// - [Linux: `prctl(PR_GET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`] +/// +/// [Linux: `prctl(PR_GET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn parent_process_death_signal() -> io::Result<Option<Signal>> { + let raw = unsafe { procctl_get_optional::<c_int>(PROC_PDEATHSIG_STATUS, None) }?; + if let Some(non_zero) = NonZeroI32::new(raw) { + // SAFETY: The only way to get a libc-reserved signal number in + // here would be to do something equivalent to + // `set_parent_process_death_signal`, but that would have required + // using a `Signal` with a libc-reserved value. + Ok(Some(unsafe { + Signal::from_raw_nonzero_unchecked(non_zero) + })) + } else { + Ok(None) + } +} + +const PROC_PDEATHSIG_CTL: c_int = 11; + +/// Set the parent-death signal of the calling process. +/// +/// # References +/// - [Linux: `prctl(PR_SET_PDEATHSIG,…)`] +/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`] +/// +/// [Linux: `prctl(PR_SET_PDEATHSIG,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn set_parent_process_death_signal(signal: Option<Signal>) -> io::Result<()> { + let signal = signal.map_or(0, |signal| signal.as_raw()); + unsafe { procctl_set::<c_int>(PROC_PDEATHSIG_CTL, None, &signal) } +} + +// +// PROC_TRACE_CTL +// + +const PROC_TRACE_CTL: c_int = 7; + +const PROC_TRACE_CTL_ENABLE: i32 = 1; +const PROC_TRACE_CTL_DISABLE: i32 = 2; +const PROC_TRACE_CTL_DISABLE_EXEC: i32 = 3; + +/// `PROC_TRACE_CTL_*` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum DumpableBehavior { + /// Not dumpable. + NotDumpable = PROC_TRACE_CTL_DISABLE, + /// Dumpable. + Dumpable = PROC_TRACE_CTL_ENABLE, + /// Not dumpable, and this behaviour is preserved across `execve` calls. + NotDumpableExecPreserved = PROC_TRACE_CTL_DISABLE_EXEC, +} + +/// Set the state of the `dumpable` attribute for the process indicated by +/// `idtype` and `id`. +/// +/// This determines whether the process can be traced and whether core dumps +/// are produced for the process upon delivery of a signal whose default +/// behavior is to produce a core dump. +/// +/// This is similar to `set_dumpable_behavior` on Linux, with the exception +/// that on FreeBSD there is an extra argument `process`. When `process` is set +/// to `None`, the operation is performed for the current process, like on +/// Linux. +/// +/// # References +/// - [FreeBSD `procctl(PROC_TRACE_CTL,…)`] +/// +/// [FreeBSD `procctl(PROC_TRACE_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> { + unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) } +} + +// +// PROC_TRACE_STATUS +// + +const PROC_TRACE_STATUS: c_int = 8; + +/// Tracing status as returned by [`trace_status`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TracingStatus { + /// Tracing is disabled for the process. + NotTraceble, + /// Tracing is not disabled for the process, but not debugger/tracer is + /// attached. + Tracable, + /// The process is being traced by the process whose pid is stored in the + /// first component of this variant. + BeingTraced(Pid), +} + +/// Get the tracing status of the process indicated by `idtype` and `id`. +/// +/// # References +/// - [FreeBSD `procctl(PROC_TRACE_STATUS,…)`] +/// +/// [FreeBSD `procctl(PROC_TRACE_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn trace_status(process: ProcSelector) -> io::Result<TracingStatus> { + let val = unsafe { procctl_get_optional::<c_int>(PROC_TRACE_STATUS, process) }?; + match val { + -1 => Ok(TracingStatus::NotTraceble), + 0 => Ok(TracingStatus::Tracable), + pid => { + let pid = Pid::from_raw(pid as RawPid).ok_or(io::Errno::RANGE)?; + Ok(TracingStatus::BeingTraced(pid)) + } + } +} + +// +// PROC_REAP_* +// + +const PROC_REAP_ACQUIRE: c_int = 2; +const PROC_REAP_RELEASE: c_int = 3; + +/// Acquire or release the reaper status of the calling process. +/// +/// # References +/// - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`] +/// +/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn set_reaper_status(reaper: bool) -> io::Result<()> { + unsafe { + procctl( + if reaper { + PROC_REAP_ACQUIRE + } else { + PROC_REAP_RELEASE + }, + None, + ptr::null_mut(), + ) + } +} + +const PROC_REAP_STATUS: c_int = 4; + +bitflags! { + /// `REAPER_STATUS_*` + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReaperStatusFlags: c_uint { + /// The process has acquired reaper status. + const OWNED = 1; + /// The process is the root of the reaper tree ([`Pid::INIT`]). + const REALINIT = 2; + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[repr(C)] +struct procctl_reaper_status { + rs_flags: c_uint, + rs_children: c_uint, + rs_descendants: c_uint, + rs_reaper: RawPid, + rs_pid: RawPid, + rs_pad0: [c_uint; 15], +} + +/// Reaper status as returned by [`get_reaper_status`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ReaperStatus { + /// The flags. + pub flags: ReaperStatusFlags, + /// The number of children of the reaper among the descendants. + pub children: usize, + /// The total number of descendants of the reaper(s), not counting + /// descendants of the reaper in the subtree. + pub descendants: usize, + /// The pid of the reaper for the specified process id. + pub reaper: Pid, + /// The pid of one reaper child if there are any descendants. + pub pid: Option<Pid>, +} + +/// Get information about the reaper of the specified process (or the process +/// itself if it is a reaper). +/// +/// # References +/// - [FreeBSD: `procctl(PROC_REAP_STATUS,…)`] +/// +/// [FreeBSD: `procctl(PROC_REAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn get_reaper_status(process: ProcSelector) -> io::Result<ReaperStatus> { + let raw = unsafe { procctl_get_optional::<procctl_reaper_status>(PROC_REAP_STATUS, process) }?; + Ok(ReaperStatus { + flags: ReaperStatusFlags::from_bits_retain(raw.rs_flags), + children: raw.rs_children as _, + descendants: raw.rs_descendants as _, + reaper: Pid::from_raw(raw.rs_reaper).ok_or(io::Errno::RANGE)?, + pid: if raw.rs_pid == -1 { + None + } else { + Some(Pid::from_raw(raw.rs_pid).ok_or(io::Errno::RANGE)?) + }, + }) +} + +#[cfg(feature = "alloc")] +const PROC_REAP_GETPIDS: c_int = 5; + +bitflags! { + /// `REAPER_PIDINFO_*` + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct PidInfoFlags: c_uint { + /// This structure was filled by the kernel. + const VALID = 1; + /// The pid field identifies a direct child of the reaper. + const CHILD = 2; + /// The reported process is itself a reaper. Descendants of a + /// subordinate reaper are not reported. + const REAPER = 4; + /// The reported process is in the zombie state. + const ZOMBIE = 8; + /// The reported process is stopped by + /// [`Signal::Stop`]/[`Signal::Tstp`]. + const STOPPED = 16; + /// The reported process is in the process of exiting. + const EXITING = 32; + } +} + +#[repr(C)] +#[derive(Default, Clone)] +struct procctl_reaper_pidinfo { + pi_pid: RawPid, + pi_subtree: RawPid, + pi_flags: c_uint, + pi_pad0: [c_uint; 15], +} + +#[repr(C)] +struct procctl_reaper_pids { + rp_count: c_uint, + rp_pad0: [c_uint; 15], + rp_pids: *mut procctl_reaper_pidinfo, +} + +/// A child process of a reaper. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct PidInfo { + /// The flags of the process. + pub flags: PidInfoFlags, + /// The pid of the process. + pub pid: Pid, + /// The pid of the child of the reaper which is the (grand-…)parent of the + /// process. + pub subtree: Pid, +} + +/// Get the list of descendants of the specified reaper process. +/// +/// # References +/// - [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`] +/// +/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +pub fn get_reaper_pids(process: ProcSelector) -> io::Result<Vec<PidInfo>> { + // Sadly no better way to guarantee that we get all the results than to + // allocate ≈8MB of memory… + const PID_MAX: usize = 99999; + let mut pids: Vec<procctl_reaper_pidinfo> = vec![Default::default(); PID_MAX]; + let mut pinfo = procctl_reaper_pids { + rp_count: PID_MAX as _, + rp_pad0: [0; 15], + rp_pids: pids.as_mut_slice().as_mut_ptr(), + }; + unsafe { procctl(PROC_REAP_GETPIDS, process, as_mut_ptr(&mut pinfo).cast())? }; + let mut result = Vec::new(); + for raw in pids.into_iter() { + let flags = PidInfoFlags::from_bits_retain(raw.pi_flags); + if !flags.contains(PidInfoFlags::VALID) { + break; + } + result.push(PidInfo { + flags, + subtree: Pid::from_raw(raw.pi_subtree).ok_or(io::Errno::RANGE)?, + pid: Pid::from_raw(raw.pi_pid).ok_or(io::Errno::RANGE)?, + }); + } + Ok(result) +} + +const PROC_REAP_KILL: c_int = 6; + +bitflags! { + /// `REAPER_KILL_*` + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + struct KillFlags: c_uint { + const CHILDREN = 1; + const SUBTREE = 2; + } +} + +#[repr(C)] +struct procctl_reaper_kill { + rk_sig: c_int, + rk_flags: c_uint, + rk_subtree: RawPid, + rk_killed: c_uint, + rk_fpid: RawPid, + rk_pad0: [c_uint; 15], +} + +/// Reaper status as returned by [`get_reaper_status`]. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct KillResult { + /// The number of processes that were signalled. + pub killed: usize, + /// The pid of the first process that wasn't successfully signalled. + pub first_failed: Option<Pid>, +} + +/// Deliver a signal to some subset of the descendants of the reaper. +/// +/// # References +/// - [FreeBSD: `procctl(PROC_REAP_KILL,…)`] +/// +/// [FreeBSD: `procctl(PROC_REAP_KILL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +pub fn reaper_kill( + process: ProcSelector, + signal: Signal, + direct_children: bool, + subtree: Option<Pid>, +) -> io::Result<KillResult> { + let mut flags = KillFlags::empty(); + flags.set(KillFlags::CHILDREN, direct_children); + flags.set(KillFlags::SUBTREE, subtree.is_some()); + let mut req = procctl_reaper_kill { + rk_sig: signal.as_raw(), + rk_flags: flags.bits(), + rk_subtree: subtree.map(|p| p.as_raw_nonzero().into()).unwrap_or(0), + rk_killed: 0, + rk_fpid: 0, + rk_pad0: [0; 15], + }; + unsafe { procctl(PROC_REAP_KILL, process, as_mut_ptr(&mut req).cast())? }; + Ok(KillResult { + killed: req.rk_killed as _, + first_failed: Pid::from_raw(req.rk_fpid), + }) +} + +// +// PROC_TRAPCAP_STATUS/PROC_TRAPCAP_CTL +// + +const PROC_TRAPCAP_CTL: c_int = 9; + +const PROC_TRAPCAP_CTL_ENABLE: i32 = 1; +const PROC_TRAPCAP_CTL_DISABLE: i32 = 2; + +/// `PROC_TRAPCAP_CTL_*` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum TrapCapBehavior { + /// Disable the [`Signal::Trap`] signal delivery on capability mode access + /// violations. + Disable = PROC_TRAPCAP_CTL_DISABLE, + /// Enable the [`Signal::Trap`] signal delivery on capability mode access + /// violations. + Enable = PROC_TRAPCAP_CTL_ENABLE, +} + +/// Set the current value of the capability mode violation trapping behavior. +/// +/// If this behavior is enabled, the kernel would deliver a [`Signal::Trap`] +/// signal on any return from a system call that would result in a +/// [`io::Errno::NOTCAPABLE`] or [`io::Errno::CAPMODE`] error. +/// +/// This behavior is inherited by the children of the process and is kept +/// across `execve` calls. +/// +/// # References +/// - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`] +/// +/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn set_trap_cap_behavior(process: ProcSelector, config: TrapCapBehavior) -> io::Result<()> { + let config = config as c_int; + unsafe { procctl_set::<c_int>(PROC_TRAPCAP_CTL, process, &config) } +} + +const PROC_TRAPCAP_STATUS: c_int = 10; + +/// Get the current value of the capability mode violation trapping behavior. +/// +/// # References +/// - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`] +/// +/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn trap_cap_behavior(process: ProcSelector) -> io::Result<TrapCapBehavior> { + let val = unsafe { procctl_get_optional::<c_int>(PROC_TRAPCAP_STATUS, process) }?; + match val { + PROC_TRAPCAP_CTL_DISABLE => Ok(TrapCapBehavior::Disable), + PROC_TRAPCAP_CTL_ENABLE => Ok(TrapCapBehavior::Enable), + _ => Err(io::Errno::RANGE), + } +} + +// +// PROC_NO_NEW_PRIVS_STATUS/PROC_NO_NEW_PRIVS_CTL +// + +const PROC_NO_NEW_PRIVS_CTL: c_int = 19; + +const PROC_NO_NEW_PRIVS_ENABLE: c_int = 1; + +/// Enable the `no_new_privs` mode that ignores SUID and SGID bits on `execve` +/// in the specified process and its future descendants. +/// +/// This is similar to `set_no_new_privs` on Linux, with the exception that on +/// FreeBSD there is no argument `no_new_privs` argument as it's only possible +/// to enable this mode and there's no going back. +/// +/// # References +/// - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`] +/// +/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn set_no_new_privs(process: ProcSelector) -> io::Result<()> { + unsafe { procctl_set::<c_int>(PROC_NO_NEW_PRIVS_CTL, process, &PROC_NO_NEW_PRIVS_ENABLE) } +} + +const PROC_NO_NEW_PRIVS_STATUS: c_int = 20; + +/// Check the `no_new_privs` mode of the specified process. +/// +/// # References +/// - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`] +/// - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`] +/// +/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html +/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,…)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 +#[inline] +pub fn no_new_privs(process: ProcSelector) -> io::Result<bool> { + unsafe { procctl_get_optional::<c_int>(PROC_NO_NEW_PRIVS_STATUS, process) } + .map(|x| x == PROC_NO_NEW_PRIVS_ENABLE) +} diff --git a/vendor/rustix/src/process/rlimit.rs b/vendor/rustix/src/process/rlimit.rs new file mode 100644 index 00000000..124ca51a --- /dev/null +++ b/vendor/rustix/src/process/rlimit.rs @@ -0,0 +1,53 @@ +#[cfg(linux_kernel)] +use crate::process::Pid; +use crate::{backend, io}; + +pub use backend::process::types::Resource; + +/// `struct rlimit`—Current and maximum values used in [`getrlimit`], +/// [`setrlimit`], and [`prlimit`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rlimit { + /// Current effective, “soft”, limit. + pub current: Option<u64>, + /// Maximum, “hard”, value that `current` may be dynamically increased to. + pub maximum: Option<u64>, +} + +/// `getrlimit(resource)`—Get a process resource limit value. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getrlimit.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getrlimit.2.html +#[inline] +pub fn getrlimit(resource: Resource) -> Rlimit { + backend::process::syscalls::getrlimit(resource) +} + +/// `setrlimit(resource, new)`—Set a process resource limit value. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/setrlimit.html +/// [Linux]: https://man7.org/linux/man-pages/man2/setrlimit.2.html +#[inline] +pub fn setrlimit(resource: Resource, new: Rlimit) -> io::Result<()> { + backend::process::syscalls::setrlimit(resource, new) +} + +/// `prlimit(pid, resource, new)`—Get and set a process resource limit value. +/// +/// # References +/// - [Linux] +/// +/// [Linux]: https://man7.org/linux/man-pages/man2/prlimit.2.html +#[cfg(linux_kernel)] +#[inline] +pub fn prlimit(pid: Option<Pid>, resource: Resource, new: Rlimit) -> io::Result<Rlimit> { + backend::process::syscalls::prlimit(pid, resource, new) +} diff --git a/vendor/rustix/src/process/types.rs b/vendor/rustix/src/process/types.rs new file mode 100644 index 00000000..0adb47ec --- /dev/null +++ b/vendor/rustix/src/process/types.rs @@ -0,0 +1,94 @@ +//! Types for use with [`rustix::process`] functions. +//! +//! [`rustix::process`]: crate::process + +#![allow(unsafe_code)] + +use crate::backend::c; +use crate::pid::Pid; +use core::mem::transmute; + +/// File lock data structure used in [`fcntl_getlk`]. +/// +/// [`fcntl_getlk`]: crate::process::fcntl_getlk() +#[cfg(not(target_os = "horizon"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Flock { + /// Starting offset for lock + pub start: u64, + /// Number of bytes to lock + pub length: u64, + /// PID of process blocking our lock. If set to `None`, it refers to the + /// current process + pub pid: Option<Pid>, + /// Type of lock + pub typ: FlockType, + /// Offset type of lock + pub offset_type: FlockOffsetType, +} + +#[cfg(not(target_os = "horizon"))] +impl Flock { + pub(crate) const unsafe fn from_raw_unchecked(raw_fl: c::flock) -> Self { + Self { + start: raw_fl.l_start as _, + length: raw_fl.l_len as _, + pid: Pid::from_raw(raw_fl.l_pid), + typ: transmute::<i16, FlockType>(raw_fl.l_type), + offset_type: transmute::<i16, FlockOffsetType>(raw_fl.l_whence), + } + } + + pub(crate) fn as_raw(&self) -> c::flock { + let mut f: c::flock = unsafe { core::mem::zeroed() }; + f.l_start = self.start as _; + f.l_len = self.length as _; + f.l_pid = Pid::as_raw(self.pid); + f.l_type = self.typ as _; + f.l_whence = self.offset_type as _; + f + } +} + +#[cfg(not(target_os = "horizon"))] +impl From<FlockType> for Flock { + fn from(value: FlockType) -> Self { + Self { + start: 0, + length: 0, + pid: None, + typ: value, + offset_type: FlockOffsetType::Set, + } + } +} + +/// `F_*LCK` constants for use with [`fcntl_getlk`]. +/// +/// [`fcntl_getlk`]: crate::process::fcntl_getlk() +#[cfg(not(target_os = "horizon"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(i16)] +pub enum FlockType { + /// `F_RDLCK` + ReadLock = c::F_RDLCK as _, + /// `F_WRLCK` + WriteLock = c::F_WRLCK as _, + /// `F_UNLCK` + Unlocked = c::F_UNLCK as _, +} + +/// `F_SEEK*` constants for use with [`fcntl_getlk`]. +/// +/// [`fcntl_getlk`]: crate::process::fcntl_getlk() +#[cfg(not(target_os = "horizon"))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(i16)] +pub enum FlockOffsetType { + /// `F_SEEK_SET` + Set = c::SEEK_SET as _, + /// `F_SEEK_CUR` + Current = c::SEEK_CUR as _, + /// `F_SEEK_END` + End = c::SEEK_END as _, +} diff --git a/vendor/rustix/src/process/umask.rs b/vendor/rustix/src/process/umask.rs new file mode 100644 index 00000000..16bd550a --- /dev/null +++ b/vendor/rustix/src/process/umask.rs @@ -0,0 +1,21 @@ +//! Umask support. + +#[cfg(feature = "fs")] +use crate::backend; +#[cfg(feature = "fs")] +use crate::fs::Mode; + +/// `umask(mask)`—Set the process file creation mask. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/umask.html +/// [Linux]: https://man7.org/linux/man-pages/man2/umask.2.html +#[cfg(feature = "fs")] +#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] +#[inline] +pub fn umask(mask: Mode) -> Mode { + backend::process::syscalls::umask(mask) +} diff --git a/vendor/rustix/src/process/wait.rs b/vendor/rustix/src/process/wait.rs new file mode 100644 index 00000000..0e004f3d --- /dev/null +++ b/vendor/rustix/src/process/wait.rs @@ -0,0 +1,493 @@ +//! Wait for processes to change state. +//! +//! # Safety +//! +//! This code needs to implement `Send` and `Sync` for `WaitIdStatus` because +//! the linux-raw-sys bindings generate a type that doesn't do so +//! automatically. +#![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! { + /// Options for modifying the behavior of [`wait`]/[`waitpid`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct WaitOptions: u32 { + /// Return immediately if no child has exited. + const NOHANG = bitcast!(backend::process::wait::WNOHANG); + /// Return if a child has stopped (but not traced via [`ptrace`]). + /// + /// [`ptrace`]: https://man7.org/linux/man-pages/man2/ptrace.2.html + #[cfg(not(target_os = "horizon"))] + const UNTRACED = bitcast!(backend::process::wait::WUNTRACED); + /// Return if a stopped child has been resumed by delivery of + /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont + #[cfg(not(target_os = "horizon"))] + const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +#[cfg(not(any( + target_os = "horizon", + target_os = "openbsd", + target_os = "redox", + target_os = "wasi" +)))] +bitflags! { + /// Options for modifying the behavior of [`waitid`]. + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct WaitIdOptions: u32 { + /// Return immediately if no child has exited. + const NOHANG = bitcast!(backend::process::wait::WNOHANG); + /// Return if a stopped child has been resumed by delivery of + /// [`Signal::Cont`]. + /// + /// [`Signal::Cont`]: crate::process::Signal::Cont + const CONTINUED = bitcast!(backend::process::wait::WCONTINUED); + /// Wait for processed that have exited. + #[cfg(not(target_os = "cygwin"))] + const EXITED = bitcast!(backend::process::wait::WEXITED); + /// Keep processed in a waitable state. + #[cfg(not(target_os = "cygwin"))] + const NOWAIT = bitcast!(backend::process::wait::WNOWAIT); + /// Wait for processes that have been stopped. + #[cfg(not(target_os = "cygwin"))] + const STOPPED = bitcast!(backend::process::wait::WSTOPPED); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +/// The status of a child process after calling [`wait`]/[`waitpid`]. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct WaitStatus(i32); + +impl WaitStatus { + /// Creates a `WaitStatus` out of an integer. + #[inline] + pub(crate) fn new(status: i32) -> Self { + Self(status) + } + + /// Converts a `WaitStatus` into its raw representation as an integer. + #[inline] + pub const fn as_raw(self) -> i32 { + self.0 + } + + /// Returns whether the process is currently stopped. + #[inline] + #[doc(alias = "WIFSTOPPED")] + pub fn stopped(self) -> bool { + backend::process::wait::WIFSTOPPED(self.0) + } + + /// Returns whether the process has exited normally. + #[inline] + #[doc(alias = "WIFEXITED")] + pub fn exited(self) -> bool { + backend::process::wait::WIFEXITED(self.0) + } + + /// Returns whether the process was terminated by a signal. + #[inline] + #[doc(alias = "WIFSIGNALED")] + pub fn signaled(self) -> bool { + backend::process::wait::WIFSIGNALED(self.0) + } + + /// Returns whether the process has continued from a job control stop. + #[inline] + #[doc(alias = "WIFCONTINUED")] + pub fn continued(self) -> bool { + backend::process::wait::WIFCONTINUED(self.0) + } + + /// Returns the number of the signal that stopped the process, if the + /// process was stopped by a signal. + #[inline] + #[doc(alias = "WSTOPSIG")] + pub fn stopping_signal(self) -> Option<i32> { + if self.stopped() { + Some(backend::process::wait::WSTOPSIG(self.0)) + } else { + None + } + } + + /// Returns the exit status number returned by the process, if it exited + /// normally. + #[inline] + #[doc(alias = "WEXITSTATUS")] + pub fn exit_status(self) -> Option<i32> { + if self.exited() { + Some(backend::process::wait::WEXITSTATUS(self.0)) + } else { + None + } + } + + /// Returns the number of the signal that terminated the process, if the + /// process was terminated by a signal. + #[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() + } +} + +/// The status of a process after calling [`waitid`]. +#[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)] +// SAFETY: `siginfo_t` does contain some raw pointers, such as the `si_ptr` +// and the `si_addr` fields, however it's up to users to use those correctly. +unsafe impl Send for WaitIdStatus {} + +#[cfg(linux_raw)] +// SAFETY: Same as with `Send`. +unsafe impl Sync for WaitIdStatus {} + +#[cfg(not(any( + target_os = "horizon", + target_os = "openbsd", + target_os = "redox", + target_os = "wasi" +)))] +impl WaitIdStatus { + /// Returns whether the process is currently stopped. + #[inline] + pub fn stopped(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_STOPPED) + } + + /// Returns whether the process is currently trapped. + #[inline] + pub fn trapped(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_TRAPPED) + } + + /// Returns whether the process has exited normally. + #[inline] + pub fn exited(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_EXITED) + } + + /// Returns whether the process was terminated by a signal and did not + /// create a core file. + #[inline] + pub fn killed(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_KILLED) + } + + /// Returns whether the process was terminated by a signal and did create a + /// core file. + #[inline] + pub fn dumped(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_DUMPED) + } + + /// Returns whether the process has continued from a job control stop. + #[inline] + pub fn continued(&self) -> bool { + self.raw_code() == bitcast!(backend::c::CLD_CONTINUED) + } + + /// Returns the number of the signal that stopped the process, if the + /// process was stopped by a signal. + #[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 + } + } + + /// Returns the number of the signal that trapped the process, if the + /// process was trapped by a signal. + #[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 + } + } + + /// Returns the exit status number returned by the process, if it exited + /// normally. + #[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 + } + } + + /// Returns the number of the signal that terminated the process, if the + /// process was terminated by a signal. + #[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 + } + } + + /// Return the raw `si_signo` value returned from `waitid`. + #[cfg(linux_raw)] + pub fn raw_signo(&self) -> crate::ffi::c_int { + self.0.si_signo() + } + + /// Return the raw `si_signo` value returned from `waitid`. + #[cfg(not(linux_raw))] + pub fn raw_signo(&self) -> crate::ffi::c_int { + self.0.si_signo + } + + /// Return the raw `si_errno` value returned from `waitid`. + #[cfg(linux_raw)] + pub fn raw_errno(&self) -> crate::ffi::c_int { + self.0.si_errno() + } + + /// Return the raw `si_errno` value returned from `waitid`. + #[cfg(not(linux_raw))] + pub fn raw_errno(&self) -> crate::ffi::c_int { + self.0.si_errno + } + + /// Return the raw `si_code` value returned from `waitid`. + #[cfg(linux_raw)] + pub fn raw_code(&self) -> crate::ffi::c_int { + self.0.si_code() + } + + /// Return the raw `si_code` value returned from `waitid`. + #[cfg(not(linux_raw))] + pub fn raw_code(&self) -> crate::ffi::c_int { + self.0.si_code + } + + // This is disabled on NetBSD because the libc crate's `si_status()` + // implementation doesn't appear to match what's in NetBSD's headers and we + // don't get a meaningful value returned. + // TODO: Report this upstream. + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "netbsd")))] + #[allow(unsafe_code)] + fn si_status(&self) -> crate::ffi::c_int { + // SAFETY: POSIX [specifies] that the `siginfo_t` returned by a + // `waitid` call always has a valid `si_status` value. + // + // [specifies]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html + 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() + } +} + +/// The identifier to wait on in a call to [`waitid`]. +#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi")))] +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum WaitId<'a> { + /// Wait on all processes. + #[doc(alias = "P_ALL")] + All, + + /// Wait for a specific process ID. + #[doc(alias = "P_PID")] + Pid(Pid), + + /// Wait for a specific process group ID, or the calling process' group ID. + #[doc(alias = "P_PGID")] + Pgid(Option<Pid>), + + /// Wait for a specific process file descriptor. + #[cfg(target_os = "linux")] + #[doc(alias = "P_PIDFD")] + PidFd(BorrowedFd<'a>), + + /// Eat the lifetime for non-Linux platforms. + #[doc(hidden)] + #[cfg(not(target_os = "linux"))] + __EatLifetime(core::marker::PhantomData<&'a ()>), +} + +/// `waitpid(pid, waitopts)`—Wait for a specific process to change state. +/// +/// If the pid is `None`, the call will wait for any child process whose +/// process group id matches that of the calling process. Otherwise, the call +/// will wait for the child process with the given pid. +/// +/// On Success, returns the status of the selected process. +/// +/// If `NOHANG` was specified in the options, and the selected child process +/// didn't change state, returns `None`. +/// +/// To wait for a given process group (the `< -1` case of `waitpid`), use +/// [`waitpgid`] or [`waitid`]. To wait for any process (the `-1` case of +/// `waitpid`), use [`wait`]. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html +/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html +#[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) +} + +/// `waitpid(-pgid, waitopts)`—Wait for a process in a specific process group +/// to change state. +/// +/// The call will wait for any child process with the given pgid. +/// +/// On Success, returns the status of the selected process. +/// +/// If `NOHANG` was specified in the options, and no selected child process +/// changed state, returns `None`. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html +/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { + backend::process::syscalls::waitpgid(pgid, waitopts) +} + +/// `wait(waitopts)`—Wait for any of the children of calling process to +/// change state. +/// +/// On success, returns the pid of the child process whose state changed, and +/// the status of said process. +/// +/// If `NOHANG` was specified in the options, and the selected child process +/// didn't change state, returns `None`. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html +/// [Linux]: https://man7.org/linux/man-pages/man2/waitpid.2.html +#[cfg(not(target_os = "wasi"))] +#[inline] +pub fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { + backend::process::syscalls::wait(waitopts) +} + +/// `waitid(_, _, _, opts)`—Wait for the specified child process to change +/// state. +#[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) +} |
