Skip to content

Commit 589039d

Browse files
authored
Add support for pidfd_open and waitid (#532)
* Add support for pidfd_open * Fix android/redox builds * Fix various platform issues * Correct test errors * Handle EINVAL error in tests
1 parent 25f5355 commit 589039d

File tree

11 files changed

+410
-18
lines changed

11 files changed

+410
-18
lines changed

src/backend/libc/process/syscalls.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ use super::super::conv::{syscall_ret, syscall_ret_u32};
1717
use super::types::RawCpuSet;
1818
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
1919
use crate::fd::BorrowedFd;
20+
#[cfg(target_os = "linux")]
21+
use crate::fd::{AsRawFd, OwnedFd};
2022
use crate::ffi::CStr;
2123
use crate::io;
24+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
25+
use crate::process::{WaitId, WaitidOptions, WaitidStatus};
2226
use core::mem::MaybeUninit;
2327
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
2428
use {
@@ -27,6 +31,8 @@ use {
2731
crate::process::{Resource, Rlimit},
2832
core::convert::TryInto,
2933
};
34+
#[cfg(target_os = "linux")]
35+
use {super::super::conv::syscall_ret_owned_fd, crate::process::PidfdFlags};
3036
#[cfg(any(target_os = "android", target_os = "linux"))]
3137
use {
3238
super::super::offset::libc_prlimit,
@@ -394,6 +400,85 @@ pub(crate) fn _waitpid(
394400
}
395401
}
396402

403+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
404+
#[inline]
405+
pub(crate) fn waitid(id: WaitId<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
406+
// Get the id to wait on.
407+
match id {
408+
WaitId::All => _waitid_all(options),
409+
WaitId::Pid(pid) => _waitid_pid(pid, options),
410+
#[cfg(target_os = "linux")]
411+
WaitId::PidFd(fd) => _waitid_pidfd(fd, options),
412+
#[cfg(not(target_os = "linux"))]
413+
WaitId::__EatLifetime(_) => unreachable!(),
414+
}
415+
}
416+
417+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
418+
#[inline]
419+
fn _waitid_all(options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
420+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
421+
unsafe {
422+
ret(c::waitid(
423+
c::P_ALL,
424+
0,
425+
status.as_mut_ptr(),
426+
options.bits() as _,
427+
))?
428+
};
429+
430+
Ok(unsafe { cvt_waitid_status(status) })
431+
}
432+
433+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
434+
#[inline]
435+
fn _waitid_pid(pid: Pid, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
436+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
437+
unsafe {
438+
ret(c::waitid(
439+
c::P_PID,
440+
Pid::as_raw(Some(pid)) as _,
441+
status.as_mut_ptr(),
442+
options.bits() as _,
443+
))?
444+
};
445+
446+
Ok(unsafe { cvt_waitid_status(status) })
447+
}
448+
449+
#[cfg(target_os = "linux")]
450+
#[inline]
451+
fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
452+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
453+
unsafe {
454+
ret(c::waitid(
455+
c::P_PIDFD,
456+
fd.as_raw_fd() as _,
457+
status.as_mut_ptr(),
458+
options.bits() as _,
459+
))?
460+
};
461+
462+
Ok(unsafe { cvt_waitid_status(status) })
463+
}
464+
465+
/// Convert a `siginfo_t` to a `WaitidStatus`.
466+
///
467+
/// # Safety
468+
///
469+
/// The caller must ensure that `status` is initialized and that `waitid` returned
470+
/// successfully.
471+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
472+
#[inline]
473+
unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitidStatus> {
474+
let status = status.assume_init();
475+
if status.si_signo == 0 {
476+
None
477+
} else {
478+
Some(WaitidStatus(status))
479+
}
480+
}
481+
397482
#[inline]
398483
pub(crate) fn exit_group(code: c::c_int) -> ! {
399484
// `_exit` and `_Exit` are the same; it's just a matter of which ones
@@ -463,3 +548,14 @@ pub(crate) unsafe fn procctl(
463548
) -> io::Result<()> {
464549
ret(c::procctl(idtype, id, option, data))
465550
}
551+
552+
#[cfg(target_os = "linux")]
553+
pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> {
554+
unsafe {
555+
syscall_ret_owned_fd(libc::syscall(
556+
c::SYS_pidfd_open,
557+
pid.as_raw_nonzero().get(),
558+
flags.bits(),
559+
))
560+
}
561+
}

src/backend/libc/process/wait.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ pub(crate) use c::{
44
WCONTINUED, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG,
55
WTERMSIG, WUNTRACED,
66
};
7+
8+
#[cfg(not(any(target_os = "openbsd", target_os = "redox", target_os = "wasi",)))]
9+
pub(crate) use c::{WEXITED, WNOWAIT, WSTOPPED};

src/backend/linux_raw/c.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@ pub(crate) use linux_raw_sys::ctypes::*;
99
pub(crate) use linux_raw_sys::errno::EINVAL;
1010
pub(crate) use linux_raw_sys::general::{
1111
AF_DECnet, __kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
12-
in6_addr, in_addr, iovec, ip_mreq, ipv6_mreq, linger, sockaddr, sockaddr_in, sockaddr_in6,
13-
sockaddr_un, socklen_t, AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH,
14-
AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN,
15-
AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX,
16-
AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE,
17-
AF_X25, IPPROTO_AH, IPPROTO_BEETPH, IPPROTO_COMP, IPPROTO_DCCP, IPPROTO_EGP, IPPROTO_ENCAP,
18-
IPPROTO_ESP, IPPROTO_ETHERNET, IPPROTO_FRAGMENT, IPPROTO_GRE, IPPROTO_ICMP, IPPROTO_ICMPV6,
19-
IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IPIP, IPPROTO_IPV6, IPPROTO_MH, IPPROTO_MPLS,
20-
IPPROTO_MPTCP, IPPROTO_MTP, IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING,
21-
IPPROTO_RSVP, IPPROTO_SCTP, IPPROTO_TCP, IPPROTO_TP, IPPROTO_UDP, IPPROTO_UDPLITE,
22-
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
23-
IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM,
24-
MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK,
25-
MSG_TRUNC, MSG_WAITALL, O_CLOEXEC, O_NONBLOCK, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM,
12+
in6_addr, in_addr, iovec, ip_mreq, ipv6_mreq, linger, siginfo_t, sockaddr, sockaddr_in,
13+
sockaddr_in6, sockaddr_un, socklen_t, AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25,
14+
AF_BLUETOOTH, AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA,
15+
AF_ISDN, AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET,
16+
AF_PPPOX, AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC,
17+
AF_WANPIPE, AF_X25, IPPROTO_AH, IPPROTO_BEETPH, IPPROTO_COMP, IPPROTO_DCCP, IPPROTO_EGP,
18+
IPPROTO_ENCAP, IPPROTO_ESP, IPPROTO_ETHERNET, IPPROTO_FRAGMENT, IPPROTO_GRE, IPPROTO_ICMP,
19+
IPPROTO_ICMPV6, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IPIP, IPPROTO_IPV6, IPPROTO_MH,
20+
IPPROTO_MPLS, IPPROTO_MPTCP, IPPROTO_MTP, IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW,
21+
IPPROTO_ROUTING, IPPROTO_RSVP, IPPROTO_SCTP, IPPROTO_TCP, IPPROTO_TP, IPPROTO_UDP,
22+
IPPROTO_UDPLITE, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY,
23+
IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL,
24+
MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE,
25+
MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, O_CLOEXEC, O_NONBLOCK,
26+
O_NONBLOCK as PIDFD_NONBLOCK, P_ALL, P_PID, P_PIDFD, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM,
2627
SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_LINGER,
2728
SO_PASSCRED, SO_RCVTIMEO_NEW, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD,
2829
SO_TYPE, TCP_NODELAY,

src/backend/linux_raw/process/syscalls.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ use super::super::conv::{
1212
ret_infallible, ret_usize, ret_usize_infallible, size_of, slice_just_addr, slice_mut, zero,
1313
};
1414
use super::types::{RawCpuSet, RawUname};
15-
use crate::fd::BorrowedFd;
15+
use crate::backend::conv::ret_owned_fd;
16+
use crate::fd::{AsRawFd, BorrowedFd, OwnedFd};
1617
use crate::ffi::CStr;
1718
use crate::io;
1819
use crate::process::{
19-
Cpuid, Gid, MembarrierCommand, MembarrierQuery, Pid, RawNonZeroPid, RawPid, Resource, Rlimit,
20-
Signal, Uid, WaitOptions, WaitStatus,
20+
Cpuid, Gid, MembarrierCommand, MembarrierQuery, Pid, PidfdFlags, RawNonZeroPid, RawPid,
21+
Resource, Rlimit, Signal, Uid, WaitId, WaitOptions, WaitStatus, WaitidOptions, WaitidStatus,
2122
};
2223
use core::convert::TryInto;
2324
use core::mem::MaybeUninit;
@@ -516,6 +517,83 @@ pub(crate) fn _waitpid(
516517
}
517518
}
518519

520+
#[inline]
521+
pub(crate) fn waitid(id: WaitId<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
522+
// Get the id to wait on.
523+
match id {
524+
WaitId::All => _waitid_all(options),
525+
WaitId::Pid(pid) => _waitid_pid(pid, options),
526+
WaitId::PidFd(fd) => _waitid_pidfd(fd, options),
527+
}
528+
}
529+
530+
#[inline]
531+
fn _waitid_all(options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
532+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
533+
unsafe {
534+
ret(syscall!(
535+
__NR_waitid,
536+
c_uint(c::P_ALL),
537+
c_uint(0),
538+
by_mut(&mut status),
539+
c_int(options.bits() as _),
540+
zero()
541+
))?
542+
};
543+
544+
Ok(unsafe { cvt_waitid_status(status) })
545+
}
546+
547+
#[inline]
548+
fn _waitid_pid(pid: Pid, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
549+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
550+
unsafe {
551+
ret(syscall!(
552+
__NR_waitid,
553+
c_uint(c::P_PID),
554+
c_uint(Pid::as_raw(Some(pid))),
555+
by_mut(&mut status),
556+
c_int(options.bits() as _),
557+
zero()
558+
))?
559+
};
560+
561+
Ok(unsafe { cvt_waitid_status(status) })
562+
}
563+
564+
#[inline]
565+
fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitidOptions) -> io::Result<Option<WaitidStatus>> {
566+
let mut status = MaybeUninit::<c::siginfo_t>::uninit();
567+
unsafe {
568+
ret(syscall!(
569+
__NR_waitid,
570+
c_uint(c::P_PIDFD),
571+
c_uint(fd.as_raw_fd() as _),
572+
by_mut(&mut status),
573+
c_int(options.bits() as _),
574+
zero()
575+
))?
576+
};
577+
578+
Ok(unsafe { cvt_waitid_status(status) })
579+
}
580+
581+
/// Convert a `siginfo_t` to a `WaitidStatus`.
582+
///
583+
/// # Safety
584+
///
585+
/// The caller must ensure that `status` is initialized and that `waitid` returned
586+
/// successfully.
587+
#[inline]
588+
unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitidStatus> {
589+
let status = status.assume_init();
590+
if status.__bindgen_anon_1.__bindgen_anon_1.si_signo == 0 {
591+
None
592+
} else {
593+
Some(WaitidStatus(status))
594+
}
595+
}
596+
519597
#[cfg(feature = "runtime")]
520598
#[inline]
521599
pub(crate) fn exit_group(code: c::c_int) -> ! {
@@ -558,3 +636,14 @@ pub(crate) unsafe fn prctl(
558636
) -> io::Result<c::c_int> {
559637
ret_c_int(syscall!(__NR_prctl, c_int(option), arg2, arg3, arg4, arg5))
560638
}
639+
640+
#[inline]
641+
pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> {
642+
unsafe {
643+
ret_owned_fd(syscall_readonly!(
644+
__NR_pidfd_open,
645+
pid,
646+
c_int(flags.bits() as _)
647+
))
648+
}
649+
}

src/backend/linux_raw/process/wait.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// The functions replacing the C macros use the same names as in libc.
22
#![allow(non_snake_case)]
33

4-
pub(crate) use linux_raw_sys::general::{WCONTINUED, WNOHANG, WUNTRACED};
4+
pub(crate) use linux_raw_sys::general::{
5+
WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WSTOPPED, WUNTRACED,
6+
};
57

68
#[inline]
79
pub(crate) fn WIFSTOPPED(status: u32) -> bool {

src/process/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ mod id;
99
mod kill;
1010
#[cfg(any(target_os = "android", target_os = "linux"))]
1111
mod membarrier;
12+
#[cfg(target_os = "linux")]
13+
mod pidfd;
1214
#[cfg(any(target_os = "android", target_os = "linux"))]
1315
mod prctl;
1416
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] // WASI doesn't have [gs]etpriority.
@@ -52,6 +54,8 @@ pub use kill::{kill_current_process_group, kill_process, kill_process_group, Sig
5254
pub use membarrier::{
5355
membarrier, membarrier_cpu, membarrier_query, MembarrierCommand, MembarrierQuery,
5456
};
57+
#[cfg(target_os = "linux")]
58+
pub use pidfd::{pidfd_open, PidfdFlags};
5559
#[cfg(any(target_os = "android", target_os = "linux"))]
5660
pub use prctl::*;
5761
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
@@ -79,6 +83,8 @@ pub use sched_yield::sched_yield;
7983
pub use uname::{uname, Uname};
8084
#[cfg(not(target_os = "wasi"))]
8185
pub use wait::{wait, waitpid, WaitOptions, WaitStatus};
86+
#[cfg(not(any(target_os = "wasi", target_os = "redox", target_os = "openbsd")))]
87+
pub use wait::{waitid, WaitId, WaitidOptions, WaitidStatus};
8288

8389
#[cfg(not(target_os = "wasi"))]
8490
#[cfg(feature = "fs")]

src/process/pidfd.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use crate::fd::OwnedFd;
2+
use crate::process::Pid;
3+
use crate::{backend, io};
4+
5+
bitflags::bitflags! {
6+
/// `PIDFD_*` flags for use with [`pidfd_open`].
7+
///
8+
/// [`pidfd_open`]: crate::process::pidfd_open
9+
pub struct PidfdFlags: backend::c::c_uint {
10+
/// `PIDFD_NONBLOCK`.
11+
const NONBLOCK = backend::c::PIDFD_NONBLOCK;
12+
}
13+
}
14+
15+
/// `syscall(SYS_pidfd_open, pid, flags)`—Creates a file descriptor for
16+
/// a process.
17+
///
18+
/// # References
19+
/// - [Linux]
20+
///
21+
/// [Linux]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html
22+
#[inline]
23+
pub fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> {
24+
backend::process::syscalls::pidfd_open(pid, flags)
25+
}

0 commit comments

Comments
 (0)