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