1 use libc::_exit;
2 use nix::errno::Errno;
3 use nix::sys::signal::*;
4 use nix::sys::wait::*;
5 use nix::unistd::ForkResult::*;
6 use nix::unistd::*;
7 
8 #[test]
9 #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
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!(
22                 waitpid(child, None),
23                 Ok(WaitStatus::Signaled(child, SIGKILL, false))
24             );
25         }
26     }
27 }
28 
29 #[test]
30 #[cfg(any(
31     target_os = "android",
32     target_os = "freebsd",
33     //target_os = "haiku",
34     all(target_os = "linux", not(target_env = "uclibc")),
35 ))]
36 #[cfg(not(any(
37     target_arch = "mips",
38     target_arch = "mips32r6",
39     target_arch = "mips64",
40     target_arch = "mips64r6"
41 )))]
test_waitid_signal()42 fn test_waitid_signal() {
43     let _m = crate::FORK_MTX.lock();
44 
45     // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
46     match unsafe { fork() }.expect("Error: Fork Failed") {
47         Child => {
48             pause();
49             unsafe { _exit(123) }
50         }
51         Parent { child } => {
52             kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
53             assert_eq!(
54                 waitid(Id::Pid(child), WaitPidFlag::WEXITED),
55                 Ok(WaitStatus::Signaled(child, SIGKILL, false)),
56             );
57         }
58     }
59 }
60 
61 #[test]
test_wait_exit()62 fn test_wait_exit() {
63     let _m = crate::FORK_MTX.lock();
64 
65     // Safe: Child only calls `_exit`, which is async-signal-safe.
66     match unsafe { fork() }.expect("Error: Fork Failed") {
67         Child => unsafe {
68             _exit(12);
69         },
70         Parent { child } => {
71             assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
72         }
73     }
74 }
75 
76 #[cfg(not(target_os = "haiku"))]
77 #[test]
78 #[cfg(any(
79     target_os = "android",
80     target_os = "freebsd",
81     target_os = "haiku",
82     all(target_os = "linux", not(target_env = "uclibc")),
83 ))]
84 #[cfg(not(any(
85     target_arch = "mips",
86     target_arch = "mips32r6",
87     target_arch = "mips64",
88     target_arch = "mips64r6"
89 )))]
test_waitid_exit()90 fn test_waitid_exit() {
91     let _m = crate::FORK_MTX.lock();
92 
93     // Safe: Child only calls `_exit`, which is async-signal-safe.
94     match unsafe { fork() }.expect("Error: Fork Failed") {
95         Child => unsafe {
96             _exit(12);
97         },
98         Parent { child } => {
99             assert_eq!(
100                 waitid(Id::Pid(child), WaitPidFlag::WEXITED),
101                 Ok(WaitStatus::Exited(child, 12)),
102             );
103         }
104     }
105 }
106 
107 #[test]
test_waitstatus_from_raw()108 fn test_waitstatus_from_raw() {
109     let pid = Pid::from_raw(1);
110     assert_eq!(
111         WaitStatus::from_raw(pid, 0x0002),
112         Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))
113     );
114     assert_eq!(
115         WaitStatus::from_raw(pid, 0x0200),
116         Ok(WaitStatus::Exited(pid, 2))
117     );
118     assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
119 }
120 
121 #[test]
test_waitstatus_pid()122 fn test_waitstatus_pid() {
123     let _m = crate::FORK_MTX.lock();
124 
125     match unsafe { fork() }.unwrap() {
126         Child => unsafe { _exit(0) },
127         Parent { child } => {
128             let status = waitpid(child, None).unwrap();
129             assert_eq!(status.pid(), Some(child));
130         }
131     }
132 }
133 
134 #[test]
135 #[cfg(any(
136     target_os = "android",
137     target_os = "freebsd",
138     target_os = "haiku",
139     all(target_os = "linux", not(target_env = "uclibc")),
140 ))]
test_waitid_pid()141 fn test_waitid_pid() {
142     let _m = crate::FORK_MTX.lock();
143 
144     match unsafe { fork() }.unwrap() {
145         Child => unsafe { _exit(0) },
146         Parent { child } => {
147             let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).unwrap();
148             assert_eq!(status.pid(), Some(child));
149         }
150     }
151 }
152 
153 #[cfg(linux_android)]
154 // FIXME: qemu-user doesn't implement ptrace on most arches
155 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
156 mod ptrace {
157     use crate::*;
158     use libc::_exit;
159     use nix::sys::ptrace::{self, Event, Options};
160     use nix::sys::signal::*;
161     use nix::sys::wait::*;
162     use nix::unistd::ForkResult::*;
163     use nix::unistd::*;
164 
ptrace_child() -> !165     fn ptrace_child() -> ! {
166         ptrace::traceme().unwrap();
167         // As recommended by ptrace(2), raise SIGTRAP to pause the child
168         // until the parent is ready to continue
169         raise(SIGTRAP).unwrap();
170         unsafe { _exit(0) }
171     }
172 
ptrace_wait_parent(child: Pid)173     fn ptrace_wait_parent(child: Pid) {
174         // Wait for the raised SIGTRAP
175         assert_eq!(
176             waitpid(child, None),
177             Ok(WaitStatus::Stopped(child, SIGTRAP))
178         );
179         // We want to test a syscall stop and a PTRACE_EVENT stop
180         ptrace::setoptions(
181             child,
182             Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
183         )
184         .expect("setoptions failed");
185 
186         // First, stop on the next system call, which will be exit()
187         ptrace::syscall(child, None).expect("syscall failed");
188         assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
189         // Then get the ptrace event for the process exiting
190         ptrace::cont(child, None).expect("cont failed");
191         assert_eq!(
192             waitpid(child, None),
193             Ok(WaitStatus::PtraceEvent(
194                 child,
195                 SIGTRAP,
196                 Event::PTRACE_EVENT_EXIT as i32
197             ))
198         );
199         // Finally get the normal wait() result, now that the process has exited
200         ptrace::cont(child, None).expect("cont failed");
201         assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
202     }
203 
204     #[cfg(not(target_env = "uclibc"))]
ptrace_waitid_parent(child: Pid)205     fn ptrace_waitid_parent(child: Pid) {
206         // Wait for the raised SIGTRAP
207         //
208         // Unlike waitpid(), waitid() can distinguish trap events from regular
209         // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
210         assert_eq!(
211             waitid(Id::Pid(child), WaitPidFlag::WEXITED),
212             Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)),
213         );
214         // We want to test a syscall stop and a PTRACE_EVENT stop
215         ptrace::setoptions(
216             child,
217             Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
218         )
219         .expect("setopts failed");
220 
221         // First, stop on the next system call, which will be exit()
222         ptrace::syscall(child, None).expect("syscall failed");
223         assert_eq!(
224             waitid(Id::Pid(child), WaitPidFlag::WEXITED),
225             Ok(WaitStatus::PtraceSyscall(child)),
226         );
227         // Then get the ptrace event for the process exiting
228         ptrace::cont(child, None).expect("cont failed");
229         assert_eq!(
230             waitid(Id::Pid(child), WaitPidFlag::WEXITED),
231             Ok(WaitStatus::PtraceEvent(
232                 child,
233                 SIGTRAP,
234                 Event::PTRACE_EVENT_EXIT as i32
235             )),
236         );
237         // Finally get the normal wait() result, now that the process has exited
238         ptrace::cont(child, None).expect("cont failed");
239         assert_eq!(
240             waitid(Id::Pid(child), WaitPidFlag::WEXITED),
241             Ok(WaitStatus::Exited(child, 0)),
242         );
243     }
244 
245     #[test]
test_wait_ptrace()246     fn test_wait_ptrace() {
247         require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
248         let _m = crate::FORK_MTX.lock();
249 
250         match unsafe { fork() }.expect("Error: Fork Failed") {
251             Child => ptrace_child(),
252             Parent { child } => ptrace_wait_parent(child),
253         }
254     }
255 
256     #[test]
257     #[cfg(not(target_env = "uclibc"))]
test_waitid_ptrace()258     fn test_waitid_ptrace() {
259         require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE);
260         let _m = crate::FORK_MTX.lock();
261 
262         match unsafe { fork() }.expect("Error: Fork Failed") {
263             Child => ptrace_child(),
264             Parent { child } => ptrace_waitid_parent(child),
265         }
266     }
267 }
268