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