1 use nix::errno::Errno;
2 use nix::unistd::*;
3 use nix::unistd::ForkResult::*;
4 use nix::sys::signal::*;
5 use nix::sys::wait::*;
6 use libc::_exit;
7
8 #[test]
9 #[cfg(not(target_os = "redox"))]
test_wait_signal()10 fn test_wait_signal() {
11 let _m = crate::FORK_MTX.lock();
12
13 // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
14 match unsafe{fork()}.expect("Error: Fork Failed") {
15 Child => {
16 pause();
17 unsafe { _exit(123) }
18 },
19 Parent { child } => {
20 kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
21 assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false)));
22 },
23 }
24 }
25
26 #[test]
test_wait_exit()27 fn test_wait_exit() {
28 let _m = crate::FORK_MTX.lock();
29
30 // Safe: Child only calls `_exit`, which is async-signal-safe.
31 match unsafe{fork()}.expect("Error: Fork Failed") {
32 Child => unsafe { _exit(12); },
33 Parent { child } => {
34 assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
35 },
36 }
37 }
38
39 #[test]
test_waitstatus_from_raw()40 fn test_waitstatus_from_raw() {
41 let pid = Pid::from_raw(1);
42 assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
43 assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2)));
44 assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
45 }
46
47 #[test]
test_waitstatus_pid()48 fn test_waitstatus_pid() {
49 let _m = crate::FORK_MTX.lock();
50
51 match unsafe{fork()}.unwrap() {
52 Child => unsafe { _exit(0) },
53 Parent { child } => {
54 let status = waitpid(child, None).unwrap();
55 assert_eq!(status.pid(), Some(child));
56 }
57 }
58 }
59
60 #[cfg(any(target_os = "linux", target_os = "android"))]
61 // FIXME: qemu-user doesn't implement ptrace on most arches
62 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
63 mod ptrace {
64 use nix::sys::ptrace::{self, Options, Event};
65 use nix::sys::signal::*;
66 use nix::sys::wait::*;
67 use nix::unistd::*;
68 use nix::unistd::ForkResult::*;
69 use libc::_exit;
70 use crate::*;
71
ptrace_child() -> !72 fn ptrace_child() -> ! {
73 ptrace::traceme().unwrap();
74 // As recommended by ptrace(2), raise SIGTRAP to pause the child
75 // until the parent is ready to continue
76 raise(SIGTRAP).unwrap();
77 unsafe { _exit(0) }
78 }
79
ptrace_parent(child: Pid)80 fn ptrace_parent(child: Pid) {
81 // Wait for the raised SIGTRAP
82 assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP)));
83 // We want to test a syscall stop and a PTRACE_EVENT stop
84 assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok());
85
86 // First, stop on the next system call, which will be exit()
87 assert!(ptrace::syscall(child, None).is_ok());
88 assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
89 // Then get the ptrace event for the process exiting
90 assert!(ptrace::cont(child, None).is_ok());
91 assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32)));
92 // Finally get the normal wait() result, now that the process has exited
93 assert!(ptrace::cont(child, None).is_ok());
94 assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
95 }
96
97 #[test]
test_wait_ptrace()98 fn test_wait_ptrace() {
99 require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
100 let _m = crate::FORK_MTX.lock();
101
102 match unsafe{fork()}.expect("Error: Fork Failed") {
103 Child => ptrace_child(),
104 Parent { child } => ptrace_parent(child),
105 }
106 }
107 }
108