1use crate::{Result, Signal};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum WaitStatus {
10 Exited(libc::pid_t, libc::c_int),
12
13 Signaled(libc::pid_t, Signal, bool),
16
17 Stopped(libc::pid_t, Signal),
19
20 Continued(libc::pid_t),
23}
24
25impl WaitStatus {
26 pub fn from_raw(pid: libc::pid_t, status: libc::c_int) -> Result<WaitStatus> {
31 Ok(if libc::WIFEXITED(status) {
32 WaitStatus::Exited(pid, libc::WEXITSTATUS(status))
33 } else if libc::WIFSIGNALED(status) {
34 WaitStatus::Signaled(
35 pid,
36 libc::WTERMSIG(status).try_into()?,
37 libc::WCOREDUMP(status),
38 )
39 } else if libc::WIFSTOPPED(status) {
40 WaitStatus::Stopped(pid, libc::WSTOPSIG(status).try_into()?)
41 } else {
42 assert!(libc::WIFCONTINUED(status));
43 WaitStatus::Continued(pid)
44 })
45 }
46}
47
48pub fn wait<P>(pid: P) -> Result<WaitStatus>
56where
57 P: Into<Option<libc::pid_t>>,
58{
59 let mut status: i32 = 0;
60
61 let res = syscall!(waitpid(
62 pid.into().unwrap_or(-1_i32),
63 &mut status as &mut libc::c_int,
64 libc::WUNTRACED
65 ))?;
66
67 WaitStatus::from_raw(res, status)
68}
69
70#[cfg(test)]
71mod tests {
72 use std::process::exit;
73
74 use crate::{wait, Result, Signal, WaitStatus};
75
76 #[test]
81 #[ignore = "must run in isolation"]
82 fn wait_any() -> Result<()> {
83 let child = match syscall!(fork())? {
84 pid if pid != 0 => pid,
86 _ => {
88 exit(42);
89 }
90 };
91
92 if let WaitStatus::Exited(pid, status) = wait(None)? {
93 assert_eq!(pid, child);
94 assert_eq!(42, status);
95 } else {
96 assert!(false);
98 }
99
100 Ok(())
101 }
102
103 #[test]
104 fn wait_exit() -> Result<()> {
105 let child = match syscall!(fork())? {
106 pid if pid != 0 => pid,
108 _ => {
110 exit(42);
111 }
112 };
113
114 if let WaitStatus::Exited(pid, status) = wait(child)? {
115 assert_eq!(pid, child);
116 assert_eq!(42, status);
117 } else {
118 assert!(false);
120 }
121
122 Ok(())
123 }
124
125 #[test]
126 fn wait_stop() -> Result<()> {
127 let child = match syscall!(fork())? {
128 pid if pid != 0 => pid,
130 _ => loop {
132 std::thread::sleep(std::time::Duration::from_millis(5));
133 },
134 };
135
136 syscall!(kill(child, Signal::SIGSTOP as libc::c_int))?;
137
138 if let WaitStatus::Stopped(pid, signal) = wait(child)? {
139 assert_eq!(pid, child);
140 assert_eq!(signal, Signal::SIGSTOP);
141 } else {
142 assert!(false);
144 }
145
146 Ok(())
147 }
148
149 #[test]
150 fn wait_kill() -> Result<()> {
151 let child = match syscall!(fork())? {
152 pid if pid != 0 => pid,
154 _ => loop {
156 std::thread::sleep(std::time::Duration::from_millis(5));
157 },
158 };
159
160 syscall!(kill(child, Signal::SIGKILL as libc::c_int))?;
161
162 if let WaitStatus::Signaled(pid, signal, core) = wait(child)? {
163 assert_eq!(pid, child);
164 assert_eq!(signal, Signal::SIGKILL);
165 assert_eq!(core, false);
166 } else {
167 assert!(false);
169 }
170
171 Ok(())
172 }
173
174 #[test]
175 fn wait_stop_kill() -> Result<()> {
176 let child = match syscall!(fork())? {
177 pid if pid != 0 => pid,
179 _ => loop {
181 std::thread::sleep(std::time::Duration::from_millis(5));
182 },
183 };
184
185 syscall!(kill(child, Signal::SIGSTOP as libc::c_int))?;
186
187 if let WaitStatus::Stopped(pid, signal) = wait(child)? {
188 assert_eq!(pid, child);
189 assert_eq!(signal, Signal::SIGSTOP);
190 } else {
191 assert!(false);
193 }
194
195 syscall!(kill(child, Signal::SIGKILL as libc::c_int))?;
196
197 if let WaitStatus::Signaled(pid, signal, core) = wait(child)? {
198 assert_eq!(pid, child);
199 assert_eq!(signal, Signal::SIGKILL);
200 assert_eq!(core, false);
201 } else {
202 assert!(false);
204 }
205
206 Ok(())
207 }
208
209 #[test]
210 fn wait_unknown() {
211 let res = wait(42);
212
213 assert_eq!(
214 format!("{}", res.err().unwrap()),
215 "System call error: No child processes (os error 10)"
216 );
217 }
218}