diff --git a/examples/process.rs b/examples/process.rs index 49c6b8d9e..77f8601f3 100644 --- a/examples/process.rs +++ b/examples/process.rs @@ -10,6 +10,13 @@ fn main() -> io::Result<()> { println!("Pid: {}", getpid().as_raw_nonzero()); println!("Parent Pid: {}", Pid::as_raw(getppid())); + println!("Group Pid: {}", getpgrp().as_raw_nonzero()); + if let Some(ppid) = getppid() { + println!( + "Parent Group Pid: {}", + getpgid(Some(ppid)).unwrap().as_raw_nonzero() + ); + } println!("Uid: {}", getuid().as_raw()); println!("Gid: {}", getgid().as_raw()); #[cfg(any( diff --git a/src/backend/libc/process/syscalls.rs b/src/backend/libc/process/syscalls.rs index 1b100e26b..c15ce3438 100644 --- a/src/backend/libc/process/syscalls.rs +++ b/src/backend/libc/process/syscalls.rs @@ -3,6 +3,8 @@ use super::super::c; #[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))] use super::super::conv::borrowed_fd; +#[cfg(not(target_os = "wasi"))] +use super::super::conv::ret_pid_t; use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr}; #[cfg(any(target_os = "android", target_os = "linux"))] use super::super::conv::{syscall_ret, syscall_ret_u32}; @@ -148,9 +150,20 @@ pub(crate) fn getppid() -> Option { #[cfg(not(target_os = "wasi"))] #[inline] #[must_use] -pub(crate) fn getpgid(pid: Option) -> Pid { +pub(crate) fn getpgid(pid: Option) -> io::Result { + unsafe { + let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?; + debug_assert_ne!(pgid, 0); + Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid))) + } +} + +#[cfg(not(target_os = "wasi"))] +#[inline] +#[must_use] +pub(crate) fn getpgrp() -> Pid { unsafe { - let pgid = c::getpgid(Pid::as_raw(pid) as _); + let pgid = c::getpgrp(); debug_assert_ne!(pgid, 0); Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid)) } diff --git a/src/backend/linux_raw/process/syscalls.rs b/src/backend/linux_raw/process/syscalls.rs index d5bf60564..6ac9f1712 100644 --- a/src/backend/linux_raw/process/syscalls.rs +++ b/src/backend/linux_raw/process/syscalls.rs @@ -21,6 +21,7 @@ use crate::process::{ }; use core::convert::TryInto; use core::mem::MaybeUninit; +use core::num::NonZeroU32; use core::ptr::{null, null_mut}; use linux_raw_sys::general::{ __kernel_gid_t, __kernel_pid_t, __kernel_uid_t, membarrier_cmd, membarrier_cmd_flag, rlimit, @@ -100,11 +101,31 @@ pub(crate) fn getppid() -> Option { } #[inline] -pub(crate) fn getpgid(pid: Option) -> Pid { +pub(crate) fn getpgid(pid: Option) -> io::Result { unsafe { let pgid: i32 = - ret_usize_infallible(syscall_readonly!(__NR_getpgid, c_uint(Pid::as_raw(pid)))) - as __kernel_pid_t; + ret_usize(syscall_readonly!(__NR_getpgid, c_uint(Pid::as_raw(pid))))? as __kernel_pid_t; + Ok(Pid::from_raw_nonzero(NonZeroU32::new_unchecked( + pgid as u32, + ))) + } +} + +#[inline] +pub(crate) fn getpgrp() -> Pid { + // Use the `getpgrp` syscall if available. + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + let pgid: i32 = ret_usize_infallible(syscall_readonly!(__NR_getpgrp)) as __kernel_pid_t; + debug_assert!(pgid > 0); + Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid as u32)) + } + + // Otherwise use `getpgrp` and pass it zero. + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + unsafe { + let pgid: i32 = + ret_usize_infallible(syscall_readonly!(__NR_getpgid, c_uint(0))) as __kernel_pid_t; debug_assert!(pgid > 0); Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid as u32)) } diff --git a/src/process/id.rs b/src/process/id.rs index 9e061bf9c..6677b9790 100644 --- a/src/process/id.rs +++ b/src/process/id.rs @@ -277,10 +277,24 @@ pub fn getppid() -> Option { /// [Linux]: https://man7.org/linux/man-pages/man2/getpgid.2.html #[inline] #[must_use] -pub fn getpgid(pid: Option) -> Pid { +pub fn getpgid(pid: Option) -> io::Result { backend::process::syscalls::getpgid(pid) } +/// `getpgrp()`—Returns the process' group ID. +/// +/// # References +/// - [POSIX] +/// - [Linux] +/// +/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html +/// [Linux]: https://man7.org/linux/man-pages/man2/getpgrp.2.html +#[inline] +#[must_use] +pub fn getpgrp() -> Pid { + backend::process::syscalls::getpgrp() +} + /// `setsid()`—Create a new session. /// /// # References diff --git a/src/process/mod.rs b/src/process/mod.rs index 6d7a61623..2c1671762 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -39,7 +39,7 @@ pub use exit::{EXIT_FAILURE, EXIT_SUCCESS}; pub use id::Cpuid; #[cfg(not(target_os = "wasi"))] pub use id::{ - getegid, geteuid, getgid, getpgid, getpid, getppid, getuid, setsid, Gid, Pid, RawGid, + getegid, geteuid, getgid, getpgid, getpgrp, getpid, getppid, getuid, setsid, Gid, Pid, RawGid, RawNonZeroPid, RawPid, RawUid, Uid, }; #[cfg(not(target_os = "wasi"))] diff --git a/tests/process/id.rs b/tests/process/id.rs index 33c2fda53..0394c4be5 100644 --- a/tests/process/id.rs +++ b/tests/process/id.rs @@ -67,11 +67,36 @@ fn test_getppid() { #[test] fn test_getpgid() { assert_eq!(process::getpgid(None), process::getpgid(None)); + assert_eq!( + process::getpgid(Some(process::getpid())), + process::getpgid(Some(process::getpid())) + ); unsafe { assert_eq!( - process::getpgid(None).as_raw_nonzero().get() as libc::pid_t, + process::getpgid(None).unwrap().as_raw_nonzero().get() as libc::pid_t, libc::getpgid(0) ); - assert_eq!(process::getpgid(None).is_init(), libc::getpgid(0) == 1); + assert_eq!( + process::getpgid(None).unwrap().is_init(), + libc::getpgid(0) == 1 + ); + assert_eq!( + process::getpgid(Some(process::getpid())) + .unwrap() + .as_raw_nonzero() + .get() as libc::pid_t, + libc::getpgid(libc::getpid()) + ); + } +} + +#[test] +fn test_getpgrp() { + assert_eq!(process::getpgrp(), process::getpgrp()); + unsafe { + assert_eq!( + process::getpgrp().as_raw_nonzero().get() as libc::pid_t, + libc::getpgrp() + ); } }