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" )))] |
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(target_arch = "mips" , target_arch = "mips64" )))] |
37 | fn 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] |
57 | fn 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" )))] |
80 | fn 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] |
98 | fn 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] |
112 | fn 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 | ))] |
131 | fn 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" ))] |
146 | mod 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 | |