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