| use super::PidFd as InternalPidFd; | |
| use crate::assert_matches; | |
| use crate::os::fd::AsRawFd; | |
| use crate::os::linux::process::{ChildExt, CommandExt as _}; | |
| use crate::os::unix::process::{CommandExt as _, ExitStatusExt}; | |
| use crate::process::Command; | |
| use crate::sys::AsInner; | |
| #[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(); | |
| let id = child.id(); | |
| assert!(id > 0 && id < -1i32 as u32, "spawning with pidfd still returns a sane pid"); | |
| if pidfd_open_available { | |
| assert!(child.pidfd().is_ok()) | |
| } | |
| if let Ok(pidfd) = child.pidfd() { | |
| match pidfd.as_inner().pid() { | |
| Ok(pid) => assert_eq!(pid, id), | |
| Err(e) if matches!(e.raw_os_error(), Some(libc::EINVAL | libc::ENOTTY)) => { | |
| /* older kernel */ | |
| } | |
| Err(e) => panic!("unexpected error getting pid from pidfd: {}", e), | |
| } | |
| } | |
| 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 may return an error. | |
| let res = fd.wait(); | |
| match res { | |
| // older kernels | |
| Err(e) if e.raw_os_error() == Some(libc::ECHILD) => {} | |
| // 6.15+ | |
| Ok(exit) if exit.signal() == Some(libc::SIGKILL) => {} | |
| other => panic!("expected ECHILD error, got {:?}", other), | |
| } | |
| // 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 { | |
| InternalPidFd::current_process().is_ok() | |
| } |