• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(target_arch = "mips", target_arch = "mips64")))]
test_waitid_signal()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]
test_wait_exit()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")))]
test_waitid_exit()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]
test_waitstatus_from_raw()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]
test_waitstatus_pid()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 ))]
test_waitid_pid()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 
ptrace_child() -> !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 
ptrace_wait_parent(child: Pid)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"))]
ptrace_waitid_parent(child: Pid)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]
test_wait_ptrace()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"))]
test_waitid_ptrace()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