use crate::assert_matches::assert_matches; use crate::os::fd::{AsRawFd, RawFd}; use crate::os::linux::process::{ChildExt, CommandExt as _}; use crate::os::unix::process::{CommandExt as _, ExitStatusExt}; use crate::process::Command; #[test] fn test_command_pidfd() { let pidfd_open_available = probe_pidfd_support(); // always exercise creation attempts let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); // but only check if we know that the kernel supports pidfds. // We don't assert the precise value, since the standard library // might have opened other file descriptors before our code runs. if pidfd_open_available { assert!(child.pidfd().is_ok()); } if let Ok(pidfd) = child.pidfd() { let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); assert!(flags & libc::FD_CLOEXEC != 0); } assert!(child.id() > 0 && child.id() < -1i32 as u32); let status = child.wait().expect("error waiting on pidfd"); assert_eq!(status.code(), Some(1)); let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); assert_matches!(child.try_wait(), Ok(None)); child.kill().expect("failed to kill child"); let status = child.wait().expect("error waiting on pidfd"); assert_eq!(status.signal(), Some(libc::SIGKILL)); let _ = Command::new("echo") .create_pidfd(false) .spawn() .unwrap() .pidfd() .expect_err("pidfd should not have been created when create_pid(false) is set"); let _ = Command::new("echo") .spawn() .unwrap() .pidfd() .expect_err("pidfd should not have been created"); // exercise the fork/exec path since the earlier attempts may have used pidfd_spawnp() let mut cmd = Command::new("false"); let mut child = unsafe { cmd.pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); assert!(child.id() > 0 && child.id() < -1i32 as u32); if pidfd_open_available { assert!(child.pidfd().is_ok()) } child.wait().expect("error waiting on child"); } #[test] fn test_pidfd() { if !probe_pidfd_support() { return; } let child = Command::new("sleep") .arg("1000") .create_pidfd(true) .spawn() .expect("executing 'sleep' failed"); let fd = child.into_pidfd().unwrap(); assert_matches!(fd.try_wait(), Ok(None)); fd.kill().expect("kill failed"); fd.kill().expect("sending kill twice failed"); let status = fd.wait().expect("1st wait failed"); assert_eq!(status.signal(), Some(libc::SIGKILL)); // Trying to wait again for a reaped child is safe since there's no pid-recycling race. // But doing so will return an error. let res = fd.wait(); assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD)); // Ditto for additional attempts to kill an already-dead child. let res = fd.kill(); assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ESRCH)); } fn probe_pidfd_support() -> bool { // pidfds require the pidfd_open syscall let our_pid = crate::process::id(); let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; if pidfd >= 0 { unsafe { libc::close(pidfd as RawFd) }; true } else { false } }