| //! linux_raw syscalls supporting `rustix::process`. |
| //! |
| //! # Safety |
| //! |
| //! See the `rustix::backend` module documentation for details. |
| #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] |
| |
| use super::types::RawCpuSet; |
| use crate::backend::c; |
| #[cfg(all(feature = "alloc", feature = "fs"))] |
| use crate::backend::conv::slice_mut; |
| use crate::backend::conv::{ |
| by_mut, by_ref, c_int, c_uint, negative_pid, pass_usize, raw_fd, ret, ret_c_int, |
| ret_c_int_infallible, ret_c_uint, ret_infallible, ret_owned_fd, ret_usize, size_of, |
| slice_just_addr, zero, |
| }; |
| use crate::fd::{AsRawFd, BorrowedFd, OwnedFd, RawFd}; |
| #[cfg(feature = "fs")] |
| use crate::ffi::CStr; |
| use crate::io; |
| use crate::pid::RawPid; |
| use crate::process::{ |
| Cpuid, MembarrierCommand, MembarrierQuery, Pid, PidfdFlags, PidfdGetfdFlags, Resource, Rlimit, |
| Uid, WaitId, WaitOptions, WaitStatus, WaitidOptions, WaitidStatus, |
| }; |
| use crate::signal::Signal; |
| use crate::utils::as_mut_ptr; |
| use core::mem::MaybeUninit; |
| use core::ptr::{null, null_mut}; |
| use linux_raw_sys::general::{ |
| membarrier_cmd, membarrier_cmd_flag, rlimit, rlimit64, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, |
| RLIM64_INFINITY, RLIM_INFINITY, |
| }; |
| #[cfg(feature = "fs")] |
| use {crate::backend::conv::ret_c_uint_infallible, crate::fs::Mode}; |
| #[cfg(feature = "alloc")] |
| use {crate::backend::conv::slice_just_addr_mut, crate::process::Gid}; |
| |
| #[cfg(feature = "fs")] |
| #[inline] |
| pub(crate) fn chdir(filename: &CStr) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_chdir, filename)) } |
| } |
| |
| #[inline] |
| pub(crate) fn fchdir(fd: BorrowedFd<'_>) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_fchdir, fd)) } |
| } |
| |
| #[cfg(feature = "fs")] |
| #[inline] |
| pub(crate) fn chroot(filename: &CStr) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_chroot, filename)) } |
| } |
| |
| #[cfg(all(feature = "alloc", feature = "fs"))] |
| #[inline] |
| pub(crate) fn getcwd(buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> { |
| let (buf_addr_mut, buf_len) = slice_mut(buf); |
| unsafe { ret_usize(syscall!(__NR_getcwd, buf_addr_mut, buf_len)) } |
| } |
| |
| #[inline] |
| pub(crate) fn membarrier_query() -> MembarrierQuery { |
| unsafe { |
| match ret_c_uint(syscall!( |
| __NR_membarrier, |
| c_int(membarrier_cmd::MEMBARRIER_CMD_QUERY as _), |
| c_uint(0) |
| )) { |
| Ok(query) => MembarrierQuery::from_bits_retain(query), |
| Err(_) => MembarrierQuery::empty(), |
| } |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> { |
| unsafe { ret(syscall!(__NR_membarrier, cmd, c_uint(0))) } |
| } |
| |
| #[inline] |
| pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> { |
| unsafe { |
| ret(syscall!( |
| __NR_membarrier, |
| cmd, |
| c_uint(membarrier_cmd_flag::MEMBARRIER_CMD_FLAG_CPU as _), |
| cpu |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getppid() -> Option<Pid> { |
| unsafe { |
| let ppid = ret_c_int_infallible(syscall_readonly!(__NR_getppid)); |
| Pid::from_raw(ppid) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> { |
| unsafe { |
| let pgid = ret_c_int(syscall_readonly!(__NR_getpgid, c_int(Pid::as_raw(pid))))?; |
| debug_assert!(pgid > 0); |
| Ok(Pid::from_raw_unchecked(pgid)) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_setpgid, |
| c_int(Pid::as_raw(pid)), |
| c_int(Pid::as_raw(pgid)) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getpgrp() -> Pid { |
| // Use the `getpgrp` syscall if available. |
| #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] |
| unsafe { |
| let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgrp)); |
| debug_assert!(pgid > 0); |
| Pid::from_raw_unchecked(pgid) |
| } |
| |
| // Otherwise use `getpgrp` and pass it zero. |
| #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] |
| unsafe { |
| let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgid, c_uint(0))); |
| debug_assert!(pgid > 0); |
| Pid::from_raw_unchecked(pgid) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> { |
| unsafe { |
| // The raw linux syscall returns the size (in bytes) of the `cpumask_t` |
| // data type that is used internally by the kernel to represent the CPU |
| // set bit mask. |
| let size = ret_usize(syscall!( |
| __NR_sched_getaffinity, |
| c_int(Pid::as_raw(pid)), |
| size_of::<RawCpuSet, _>(), |
| by_mut(&mut cpuset.bits) |
| ))?; |
| let bytes = as_mut_ptr(cpuset).cast::<u8>(); |
| let rest = bytes.wrapping_add(size); |
| // Zero every byte in the cpuset not set by the kernel. |
| rest.write_bytes(0, core::mem::size_of::<RawCpuSet>() - size); |
| Ok(()) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_sched_setaffinity, |
| c_int(Pid::as_raw(pid)), |
| size_of::<RawCpuSet, _>(), |
| slice_just_addr(&cpuset.bits) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn sched_yield() { |
| unsafe { |
| // See the documentation for [`crate::process::sched_yield`] for why |
| // errors are ignored. |
| syscall_readonly!(__NR_sched_yield).decode_void(); |
| } |
| } |
| |
| #[cfg(feature = "fs")] |
| #[inline] |
| pub(crate) fn umask(mode: Mode) -> Mode { |
| unsafe { Mode::from_bits_retain(ret_c_uint_infallible(syscall_readonly!(__NR_umask, mode))) } |
| } |
| |
| #[inline] |
| pub(crate) fn nice(inc: i32) -> io::Result<i32> { |
| let priority = (if inc > -40 && inc < 40 { |
| inc + getpriority_process(None)? |
| } else { |
| inc |
| }) |
| .clamp(-20, 19); |
| setpriority_process(None, priority)?; |
| Ok(priority) |
| } |
| |
| #[inline] |
| pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> { |
| unsafe { |
| Ok(20 |
| - ret_c_int(syscall_readonly!( |
| __NR_getpriority, |
| c_uint(PRIO_USER), |
| c_uint(uid.as_raw()) |
| ))?) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> { |
| unsafe { |
| Ok(20 |
| - ret_c_int(syscall_readonly!( |
| __NR_getpriority, |
| c_uint(PRIO_PGRP), |
| c_int(Pid::as_raw(pgid)) |
| ))?) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> { |
| unsafe { |
| Ok(20 |
| - ret_c_int(syscall_readonly!( |
| __NR_getpriority, |
| c_uint(PRIO_PROCESS), |
| c_int(Pid::as_raw(pid)) |
| ))?) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_setpriority, |
| c_uint(PRIO_USER), |
| c_uint(uid.as_raw()), |
| c_int(priority) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_setpriority, |
| c_uint(PRIO_PGRP), |
| c_int(Pid::as_raw(pgid)), |
| c_int(priority) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_setpriority, |
| c_uint(PRIO_PROCESS), |
| c_int(Pid::as_raw(pid)), |
| c_int(priority) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getrlimit(limit: Resource) -> Rlimit { |
| let mut result = MaybeUninit::<rlimit64>::uninit(); |
| unsafe { |
| match ret(syscall!( |
| __NR_prlimit64, |
| c_uint(0), |
| limit, |
| null::<c::c_void>(), |
| &mut result |
| )) { |
| Ok(()) => rlimit_from_linux(result.assume_init()), |
| Err(err) => { |
| debug_assert_eq!(err, io::Errno::NOSYS); |
| getrlimit_old(limit) |
| } |
| } |
| } |
| } |
| |
| /// The old 32-bit-only `getrlimit` syscall, for when we lack the new |
| /// `prlimit64`. |
| unsafe fn getrlimit_old(limit: Resource) -> Rlimit { |
| let mut result = MaybeUninit::<rlimit>::uninit(); |
| |
| // On these platforms, `__NR_getrlimit` is called `__NR_ugetrlimit`. |
| #[cfg(any( |
| target_arch = "arm", |
| target_arch = "powerpc", |
| target_arch = "powerpc64", |
| target_arch = "x86", |
| ))] |
| { |
| ret_infallible(syscall!(__NR_ugetrlimit, limit, &mut result)); |
| } |
| |
| // On these platforms, it's just `__NR_getrlimit`. |
| #[cfg(not(any( |
| target_arch = "arm", |
| target_arch = "powerpc", |
| target_arch = "powerpc64", |
| target_arch = "x86", |
| )))] |
| { |
| ret_infallible(syscall!(__NR_getrlimit, limit, &mut result)); |
| } |
| |
| rlimit_from_linux_old(result.assume_init()) |
| } |
| |
| #[inline] |
| pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> { |
| unsafe { |
| let lim = rlimit_to_linux(new.clone()); |
| match ret(syscall_readonly!( |
| __NR_prlimit64, |
| c_uint(0), |
| limit, |
| by_ref(&lim), |
| null_mut::<c::c_void>() |
| )) { |
| Ok(()) => Ok(()), |
| Err(io::Errno::NOSYS) => setrlimit_old(limit, new), |
| Err(err) => Err(err), |
| } |
| } |
| } |
| |
| /// The old 32-bit-only `setrlimit` syscall, for when we lack the new |
| /// `prlimit64`. |
| unsafe fn setrlimit_old(limit: Resource, new: Rlimit) -> io::Result<()> { |
| let lim = rlimit_to_linux_old(new)?; |
| ret(syscall_readonly!(__NR_setrlimit, limit, by_ref(&lim))) |
| } |
| |
| #[inline] |
| pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> { |
| let lim = rlimit_to_linux(new); |
| let mut result = MaybeUninit::<rlimit64>::uninit(); |
| unsafe { |
| match ret(syscall!( |
| __NR_prlimit64, |
| c_int(Pid::as_raw(pid)), |
| limit, |
| by_ref(&lim), |
| &mut result |
| )) { |
| Ok(()) => Ok(rlimit_from_linux(result.assume_init())), |
| Err(err) => Err(err), |
| } |
| } |
| } |
| |
| /// Convert a Rust [`Rlimit`] to a C `rlimit64`. |
| #[inline] |
| fn rlimit_from_linux(lim: rlimit64) -> Rlimit { |
| let current = if lim.rlim_cur as u64 == RLIM64_INFINITY as u64 { |
| None |
| } else { |
| Some(lim.rlim_cur) |
| }; |
| let maximum = if lim.rlim_max as u64 == RLIM64_INFINITY as u64 { |
| None |
| } else { |
| Some(lim.rlim_max) |
| }; |
| Rlimit { current, maximum } |
| } |
| |
| /// Convert a C `rlimit64` to a Rust `Rlimit`. |
| #[inline] |
| fn rlimit_to_linux(lim: Rlimit) -> rlimit64 { |
| let rlim_cur = match lim.current { |
| Some(r) => r, |
| None => RLIM64_INFINITY as _, |
| }; |
| let rlim_max = match lim.maximum { |
| Some(r) => r, |
| None => RLIM64_INFINITY as _, |
| }; |
| rlimit64 { rlim_cur, rlim_max } |
| } |
| |
| /// Like `rlimit_from_linux` but uses Linux's old 32-bit `rlimit`. |
| #[allow(clippy::useless_conversion)] |
| fn rlimit_from_linux_old(lim: rlimit) -> Rlimit { |
| let current = if lim.rlim_cur as u32 == RLIM_INFINITY as u32 { |
| None |
| } else { |
| Some(lim.rlim_cur.into()) |
| }; |
| let maximum = if lim.rlim_max as u32 == RLIM_INFINITY as u32 { |
| None |
| } else { |
| Some(lim.rlim_max.into()) |
| }; |
| Rlimit { current, maximum } |
| } |
| |
| /// Like `rlimit_to_linux` but uses Linux's old 32-bit `rlimit`. |
| #[allow(clippy::useless_conversion)] |
| fn rlimit_to_linux_old(lim: Rlimit) -> io::Result<rlimit> { |
| let rlim_cur = match lim.current { |
| Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?, |
| None => RLIM_INFINITY as _, |
| }; |
| let rlim_max = match lim.maximum { |
| Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?, |
| None => RLIM_INFINITY as _, |
| }; |
| Ok(rlimit { rlim_cur, rlim_max }) |
| } |
| |
| #[inline] |
| pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { |
| _waitpid(!0, waitopts) |
| } |
| |
| #[inline] |
| pub(crate) fn waitpid( |
| pid: Option<Pid>, |
| waitopts: WaitOptions, |
| ) -> io::Result<Option<(Pid, WaitStatus)>> { |
| _waitpid(Pid::as_raw(pid), waitopts) |
| } |
| |
| #[inline] |
| pub(crate) fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> { |
| _waitpid(-pgid.as_raw_nonzero().get(), waitopts) |
| } |
| |
| #[inline] |
| pub(crate) fn _waitpid( |
| pid: RawPid, |
| waitopts: WaitOptions, |
| ) -> io::Result<Option<(Pid, WaitStatus)>> { |
| unsafe { |
| let mut status = MaybeUninit::<u32>::uninit(); |
| let pid = ret_c_int(syscall!( |
| __NR_wait4, |
| c_int(pid as _), |
| &mut status, |
| c_int(waitopts.bits() as _), |
| zero() |
| ))?; |
| Ok(Pid::from_raw(pid).map(|pid| (pid, WaitStatus::new(status.assume_init())))) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn waitid(id: WaitId<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { |
| // Get the id to wait on. |
| match id { |
| WaitId::All => _waitid_all(options), |
| WaitId::Pid(pid) => _waitid_pid(pid, options), |
| WaitId::Pgid(pid) => _waitid_pgid(pid, options), |
| WaitId::PidFd(fd) => _waitid_pidfd(fd, options), |
| } |
| } |
| |
| #[inline] |
| fn _waitid_all(options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { |
| // `waitid` can return successfully without initializing the struct (no |
| // children found when using `WNOHANG`) |
| let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); |
| unsafe { |
| ret(syscall!( |
| __NR_waitid, |
| c_uint(c::P_ALL), |
| c_uint(0), |
| by_mut(&mut status), |
| c_int(options.bits() as _), |
| zero() |
| ))? |
| }; |
| |
| Ok(unsafe { cvt_waitid_status(status) }) |
| } |
| |
| #[inline] |
| fn _waitid_pid(pid: Pid, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { |
| // `waitid` can return successfully without initializing the struct (no |
| // children found when using `WNOHANG`) |
| let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); |
| unsafe { |
| ret(syscall!( |
| __NR_waitid, |
| c_uint(c::P_PID), |
| c_int(Pid::as_raw(Some(pid))), |
| by_mut(&mut status), |
| c_int(options.bits() as _), |
| zero() |
| ))? |
| }; |
| |
| Ok(unsafe { cvt_waitid_status(status) }) |
| } |
| |
| #[inline] |
| fn _waitid_pgid(pgid: Option<Pid>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { |
| // `waitid` can return successfully without initializing the struct (no |
| // children found when using `WNOHANG`) |
| let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); |
| unsafe { |
| ret(syscall!( |
| __NR_waitid, |
| c_uint(c::P_PGID), |
| c_int(Pid::as_raw(pgid)), |
| by_mut(&mut status), |
| c_int(options.bits() as _), |
| zero() |
| ))? |
| }; |
| |
| Ok(unsafe { cvt_waitid_status(status) }) |
| } |
| |
| #[inline] |
| fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> { |
| // `waitid` can return successfully without initializing the struct (no |
| // children found when using `WNOHANG`) |
| let mut status = MaybeUninit::<c::siginfo_t>::zeroed(); |
| unsafe { |
| ret(syscall!( |
| __NR_waitid, |
| c_uint(c::P_PIDFD), |
| c_uint(fd.as_raw_fd() as _), |
| by_mut(&mut status), |
| c_int(options.bits() as _), |
| zero() |
| ))? |
| }; |
| |
| Ok(unsafe { cvt_waitid_status(status) }) |
| } |
| |
| /// Convert a `siginfo_t` to a `WaitidStatus`. |
| /// |
| /// # Safety |
| /// |
| /// The caller must ensure that `status` is initialized and that `waitid` |
| /// returned successfully. |
| #[inline] |
| #[rustfmt::skip] |
| unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitidStatus> { |
| let status = status.assume_init(); |
| if status.__bindgen_anon_1.__bindgen_anon_1._sifields._sigchld._pid == 0 { |
| None |
| } else { |
| Some(WaitidStatus(status)) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn getsid(pid: Option<Pid>) -> io::Result<Pid> { |
| unsafe { |
| let pid = ret_c_int(syscall_readonly!(__NR_getsid, c_int(Pid::as_raw(pid))))?; |
| Ok(Pid::from_raw_unchecked(pid)) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn setsid() -> io::Result<Pid> { |
| unsafe { |
| let pid = ret_c_int(syscall_readonly!(__NR_setsid))?; |
| Ok(Pid::from_raw_unchecked(pid)) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_kill, pid, sig)) } |
| } |
| |
| #[inline] |
| pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_kill, negative_pid(pid), sig)) } |
| } |
| |
| #[inline] |
| pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), sig)) } |
| } |
| |
| #[inline] |
| pub(crate) fn test_kill_process(pid: Pid) -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_kill, pid, pass_usize(0))) } |
| } |
| |
| #[inline] |
| pub(crate) fn test_kill_process_group(pid: Pid) -> io::Result<()> { |
| unsafe { |
| ret(syscall_readonly!( |
| __NR_kill, |
| negative_pid(pid), |
| pass_usize(0) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn test_kill_current_process_group() -> io::Result<()> { |
| unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), pass_usize(0))) } |
| } |
| |
| #[inline] |
| pub(crate) fn pidfd_getfd( |
| pidfd: BorrowedFd<'_>, |
| targetfd: RawFd, |
| flags: PidfdGetfdFlags, |
| ) -> io::Result<OwnedFd> { |
| unsafe { |
| ret_owned_fd(syscall_readonly!( |
| __NR_pidfd_getfd, |
| pidfd, |
| raw_fd(targetfd), |
| c_int(flags.bits() as _) |
| )) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> { |
| unsafe { ret_owned_fd(syscall_readonly!(__NR_pidfd_open, pid, flags)) } |
| } |
| |
| #[cfg(feature = "alloc")] |
| #[inline] |
| pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<usize> { |
| let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?; |
| |
| unsafe { |
| ret_usize(syscall!( |
| __NR_getgroups, |
| c_int(len), |
| slice_just_addr_mut(buf) |
| )) |
| } |
| } |