use nix::Error; use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::signal::*; use nix::sys::wait::*; use libc::_exit; #[test] #[cfg(not(target_os = "redox"))] fn test_wait_signal() { let _ = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. match fork().expect("Error: Fork Failed") { Child => { pause(); unsafe { _exit(123) } }, Parent { child } => { kill(child, Some(SIGKILL)).expect("Error: Kill Failed"); assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); }, } } #[test] fn test_wait_exit() { let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); // Safe: Child only calls `_exit`, which is async-signal-safe. match fork().expect("Error: Fork Failed") { Child => unsafe { _exit(12); }, Parent { child } => { assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); }, } } #[test] fn test_waitstatus_from_raw() { let pid = Pid::from_raw(1); assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2))); assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Error::invalid_argument())); } #[test] fn test_waitstatus_pid() { let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); match fork().unwrap() { Child => unsafe { _exit(0) }, Parent { child } => { let status = waitpid(child, None).unwrap(); assert_eq!(status.pid(), Some(child)); } } } #[cfg(any(target_os = "linux", target_os = "android"))] // FIXME: qemu-user doesn't implement ptrace on most arches #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod ptrace { use nix::sys::ptrace::{self, Options, Event}; use nix::sys::signal::*; use nix::sys::wait::*; use nix::unistd::*; use nix::unistd::ForkResult::*; use libc::_exit; fn ptrace_child() -> ! { ptrace::traceme().unwrap(); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue raise(SIGTRAP).unwrap(); unsafe { _exit(0) } } fn ptrace_parent(child: Pid) { // Wait for the raised SIGTRAP assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); // We want to test a syscall stop and a PTRACE_EVENT stop assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok()); // First, stop on the next system call, which will be exit() assert!(ptrace::syscall(child, None).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); // Then get the ptrace event for the process exiting assert!(ptrace::cont(child, None).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32))); // Finally get the normal wait() result, now that the process has exited assert!(ptrace::cont(child, None).is_ok()); assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); } #[test] fn test_wait_ptrace() { require_capability!(CAP_SYS_PTRACE); let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test"); match fork().expect("Error: Fork Failed") { Child => ptrace_child(), Parent { child } => ptrace_parent(child), } } }