]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use crate::convert::TryInto; |
e74abb32 | 2 | use crate::fmt; |
532ac7d7 XL |
3 | use crate::io::{self, Error, ErrorKind}; |
4 | use crate::ptr; | |
dfeec247 | 5 | use crate::sys; |
532ac7d7 XL |
6 | use crate::sys::cvt; |
7 | use crate::sys::process::process_common::*; | |
532ac7d7 XL |
8 | |
9 | use libc::{c_int, gid_t, pid_t, uid_t}; | |
476ff2be SL |
10 | |
11 | //////////////////////////////////////////////////////////////////////////////// | |
12 | // Command | |
13 | //////////////////////////////////////////////////////////////////////////////// | |
14 | ||
15 | impl Command { | |
dfeec247 XL |
16 | pub fn spawn( |
17 | &mut self, | |
18 | default: Stdio, | |
19 | needs_stdin: bool, | |
20 | ) -> io::Result<(Process, StdioPipes)> { | |
3dfed10e | 21 | const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX"; |
476ff2be | 22 | |
ff7c6d11 XL |
23 | let envp = self.capture_env(); |
24 | ||
476ff2be | 25 | if self.saw_nul() { |
dfeec247 | 26 | return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")); |
476ff2be SL |
27 | } |
28 | ||
29 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
0531ce1d XL |
30 | |
31 | if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { | |
dfeec247 | 32 | return Ok((ret, ours)); |
0531ce1d XL |
33 | } |
34 | ||
476ff2be SL |
35 | let (input, output) = sys::pipe::anon_pipe()?; |
36 | ||
13cf67c4 XL |
37 | // Whatever happens after the fork is almost for sure going to touch or |
38 | // look at the environment in one way or another (PATH in `execvp` or | |
39 | // accessing the `environ` pointer ourselves). Make sure no other thread | |
40 | // is accessing the environment when we do the fork itself. | |
41 | // | |
42 | // Note that as soon as we're done with the fork there's no need to hold | |
43 | // a lock any more because the parent won't do anything and the child is | |
44 | // in its own process. | |
45 | let result = unsafe { | |
46 | let _env_lock = sys::os::env_lock(); | |
47 | cvt(libc::fork())? | |
48 | }; | |
49 | ||
476ff2be | 50 | let pid = unsafe { |
13cf67c4 | 51 | match result { |
476ff2be SL |
52 | 0 => { |
53 | drop(input); | |
dc9dc135 | 54 | let Err(err) = self.do_exec(theirs, envp.as_ref()); |
476ff2be | 55 | let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; |
3dfed10e | 56 | let errno = errno.to_be_bytes(); |
476ff2be | 57 | let bytes = [ |
3dfed10e XL |
58 | errno[0], |
59 | errno[1], | |
60 | errno[2], | |
61 | errno[3], | |
dfeec247 XL |
62 | CLOEXEC_MSG_FOOTER[0], |
63 | CLOEXEC_MSG_FOOTER[1], | |
64 | CLOEXEC_MSG_FOOTER[2], | |
65 | CLOEXEC_MSG_FOOTER[3], | |
476ff2be SL |
66 | ]; |
67 | // pipe I/O up to PIPE_BUF bytes should be atomic, and then | |
68 | // we want to be sure we *don't* run at_exit destructors as | |
69 | // we're being torn down regardless | |
1b1a35ee | 70 | rtassert!(output.write(&bytes).is_ok()); |
476ff2be SL |
71 | libc::_exit(1) |
72 | } | |
73 | n => n, | |
74 | } | |
75 | }; | |
76 | ||
ba9703b0 | 77 | let mut p = Process { pid, status: None }; |
476ff2be SL |
78 | drop(output); |
79 | let mut bytes = [0; 8]; | |
80 | ||
81 | // loop to handle EINTR | |
82 | loop { | |
83 | match input.read(&mut bytes) { | |
84 | Ok(0) => return Ok((p, ours)), | |
85 | Ok(8) => { | |
3dfed10e XL |
86 | let (errno, footer) = bytes.split_at(4); |
87 | assert_eq!( | |
88 | CLOEXEC_MSG_FOOTER, footer, | |
dfeec247 XL |
89 | "Validation on the CLOEXEC pipe failed: {:?}", |
90 | bytes | |
91 | ); | |
3dfed10e | 92 | let errno = i32::from_be_bytes(errno.try_into().unwrap()); |
dfeec247 XL |
93 | assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); |
94 | return Err(Error::from_raw_os_error(errno)); | |
476ff2be SL |
95 | } |
96 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
97 | Err(e) => { | |
dfeec247 | 98 | assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); |
476ff2be | 99 | panic!("the CLOEXEC pipe failed: {:?}", e) |
dfeec247 XL |
100 | } |
101 | Ok(..) => { | |
102 | // pipe I/O up to PIPE_BUF bytes should be atomic | |
103 | assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); | |
476ff2be SL |
104 | panic!("short read on the CLOEXEC pipe") |
105 | } | |
106 | } | |
107 | } | |
476ff2be SL |
108 | } |
109 | ||
110 | pub fn exec(&mut self, default: Stdio) -> io::Error { | |
ff7c6d11 XL |
111 | let envp = self.capture_env(); |
112 | ||
476ff2be | 113 | if self.saw_nul() { |
dfeec247 | 114 | return io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data"); |
476ff2be SL |
115 | } |
116 | ||
117 | match self.setup_io(default, true) { | |
13cf67c4 XL |
118 | Ok((_, theirs)) => { |
119 | unsafe { | |
120 | // Similar to when forking, we want to ensure that access to | |
121 | // the environment is synchronized, so make sure to grab the | |
122 | // environment lock before we try to exec. | |
123 | let _lock = sys::os::env_lock(); | |
124 | ||
dc9dc135 XL |
125 | let Err(e) = self.do_exec(theirs, envp.as_ref()); |
126 | e | |
13cf67c4 XL |
127 | } |
128 | } | |
476ff2be SL |
129 | Err(e) => e, |
130 | } | |
131 | } | |
132 | ||
133 | // And at this point we've reached a special time in the life of the | |
134 | // child. The child must now be considered hamstrung and unable to | |
135 | // do anything other than syscalls really. Consider the following | |
136 | // scenario: | |
137 | // | |
138 | // 1. Thread A of process 1 grabs the malloc() mutex | |
139 | // 2. Thread B of process 1 forks(), creating thread C | |
140 | // 3. Thread C of process 2 then attempts to malloc() | |
141 | // 4. The memory of process 2 is the same as the memory of | |
142 | // process 1, so the mutex is locked. | |
143 | // | |
144 | // This situation looks a lot like deadlock, right? It turns out | |
145 | // that this is what pthread_atfork() takes care of, which is | |
146 | // presumably implemented across platforms. The first thing that | |
147 | // threads to *before* forking is to do things like grab the malloc | |
148 | // mutex, and then after the fork they unlock it. | |
149 | // | |
150 | // Despite this information, libnative's spawn has been witnessed to | |
cc61c64b | 151 | // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but |
476ff2be SL |
152 | // all collected backtraces point at malloc/free traffic in the |
153 | // child spawned process. | |
154 | // | |
155 | // For this reason, the block of code below should contain 0 | |
156 | // invocations of either malloc of free (or their related friends). | |
157 | // | |
158 | // As an example of not having malloc/free traffic, we don't close | |
159 | // this file descriptor by dropping the FileDesc (which contains an | |
160 | // allocation). Instead we just close it manually. This will never | |
161 | // have the drop glue anyway because this code never returns (the | |
162 | // child will either exec() or invoke libc::exit) | |
ff7c6d11 XL |
163 | unsafe fn do_exec( |
164 | &mut self, | |
165 | stdio: ChildPipes, | |
dfeec247 | 166 | maybe_envp: Option<&CStringArray>, |
dc9dc135 | 167 | ) -> Result<!, io::Error> { |
532ac7d7 | 168 | use crate::sys::{self, cvt_r}; |
476ff2be | 169 | |
476ff2be | 170 | if let Some(fd) = stdio.stdin.fd() { |
dc9dc135 | 171 | cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; |
476ff2be SL |
172 | } |
173 | if let Some(fd) = stdio.stdout.fd() { | |
dc9dc135 | 174 | cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; |
476ff2be SL |
175 | } |
176 | if let Some(fd) = stdio.stderr.fd() { | |
dc9dc135 | 177 | cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; |
476ff2be SL |
178 | } |
179 | ||
e1599b0c XL |
180 | #[cfg(not(target_os = "l4re"))] |
181 | { | |
ea8adc8c | 182 | if let Some(u) = self.get_gid() { |
dc9dc135 | 183 | cvt(libc::setgid(u as gid_t))?; |
ea8adc8c XL |
184 | } |
185 | if let Some(u) = self.get_uid() { | |
e1599b0c XL |
186 | // When dropping privileges from root, the `setgroups` call |
187 | // will remove any extraneous groups. If we don't call this, | |
188 | // then even though our uid has dropped, we may still have | |
189 | // groups that enable us to do super-user things. This will | |
190 | // fail if we aren't root, so don't bother checking the | |
191 | // return value, this is just done as an optimistic | |
192 | // privilege dropping function. | |
416331ca | 193 | //FIXME: Redox kernel does not support setgroups yet |
e1599b0c XL |
194 | #[cfg(not(target_os = "redox"))] |
195 | let _ = libc::setgroups(0, ptr::null()); | |
dc9dc135 | 196 | cvt(libc::setuid(u as uid_t))?; |
ea8adc8c | 197 | } |
476ff2be SL |
198 | } |
199 | if let Some(ref cwd) = *self.get_cwd() { | |
dc9dc135 | 200 | cvt(libc::chdir(cwd.as_ptr()))?; |
476ff2be | 201 | } |
476ff2be | 202 | |
ea8adc8c | 203 | // emscripten has no signal support. |
e1599b0c | 204 | #[cfg(not(target_os = "emscripten"))] |
3b2f2976 | 205 | { |
dc9dc135 | 206 | use crate::mem::MaybeUninit; |
476ff2be SL |
207 | // Reset signal handling so the child process starts in a |
208 | // standardized state. libstd ignores SIGPIPE, and signal-handling | |
209 | // libraries often set a mask. Child processes inherit ignored | |
210 | // signals and the signal mask from their parent, but most | |
211 | // UNIX programs do not reset these things on their own, so we | |
212 | // need to clean things up now to avoid confusing the program | |
213 | // we're about to run. | |
dc9dc135 | 214 | let mut set = MaybeUninit::<libc::sigset_t>::uninit(); |
e1599b0c | 215 | cvt(sigemptyset(set.as_mut_ptr()))?; |
dfeec247 | 216 | cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?; |
476ff2be SL |
217 | let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); |
218 | if ret == libc::SIG_ERR { | |
dfeec247 | 219 | return Err(io::Error::last_os_error()); |
476ff2be SL |
220 | } |
221 | } | |
222 | ||
223 | for callback in self.get_closures().iter_mut() { | |
dc9dc135 | 224 | callback()?; |
476ff2be SL |
225 | } |
226 | ||
13cf67c4 XL |
227 | // Although we're performing an exec here we may also return with an |
228 | // error from this function (without actually exec'ing) in which case we | |
229 | // want to be sure to restore the global environment back to what it | |
230 | // once was, ensuring that our temporary override, when free'd, doesn't | |
231 | // corrupt our process's environment. | |
232 | let mut _reset = None; | |
233 | if let Some(envp) = maybe_envp { | |
234 | struct Reset(*const *const libc::c_char); | |
235 | ||
236 | impl Drop for Reset { | |
237 | fn drop(&mut self) { | |
238 | unsafe { | |
239 | *sys::os::environ() = self.0; | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | _reset = Some(Reset(*sys::os::environ())); | |
245 | *sys::os::environ() = envp.as_ptr(); | |
246 | } | |
247 | ||
1b1a35ee | 248 | libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); |
dc9dc135 | 249 | Err(io::Error::last_os_error()) |
476ff2be | 250 | } |
0531ce1d | 251 | |
dfeec247 XL |
252 | #[cfg(not(any( |
253 | target_os = "macos", | |
254 | target_os = "freebsd", | |
1b1a35ee XL |
255 | all(target_os = "linux", target_env = "gnu"), |
256 | all(target_os = "linux", target_env = "musl"), | |
dfeec247 XL |
257 | )))] |
258 | fn posix_spawn( | |
259 | &mut self, | |
260 | _: &ChildPipes, | |
261 | _: Option<&CStringArray>, | |
262 | ) -> io::Result<Option<Process>> { | |
0531ce1d XL |
263 | Ok(None) |
264 | } | |
265 | ||
266 | // Only support platforms for which posix_spawn() can return ENOENT | |
267 | // directly. | |
dfeec247 XL |
268 | #[cfg(any( |
269 | target_os = "macos", | |
270 | target_os = "freebsd", | |
1b1a35ee XL |
271 | all(target_os = "linux", target_env = "gnu"), |
272 | all(target_os = "linux", target_env = "musl"), | |
dfeec247 XL |
273 | ))] |
274 | fn posix_spawn( | |
275 | &mut self, | |
276 | stdio: &ChildPipes, | |
277 | envp: Option<&CStringArray>, | |
278 | ) -> io::Result<Option<Process>> { | |
dc9dc135 | 279 | use crate::mem::MaybeUninit; |
532ac7d7 | 280 | use crate::sys; |
0531ce1d | 281 | |
dfeec247 XL |
282 | if self.get_gid().is_some() |
283 | || self.get_uid().is_some() | |
284 | || self.env_saw_path() | |
285 | || !self.get_closures().is_empty() | |
286 | { | |
287 | return Ok(None); | |
0531ce1d XL |
288 | } |
289 | ||
290 | // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. | |
291 | #[cfg(all(target_os = "linux", target_env = "gnu"))] | |
292 | { | |
293 | if let Some(version) = sys::os::glibc_version() { | |
294 | if version < (2, 24) { | |
dfeec247 | 295 | return Ok(None); |
0531ce1d XL |
296 | } |
297 | } else { | |
dfeec247 | 298 | return Ok(None); |
0531ce1d XL |
299 | } |
300 | } | |
301 | ||
1b1a35ee XL |
302 | // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory, |
303 | // and maybe others will gain this non-POSIX function too. We'll check | |
304 | // for this weak symbol as soon as it's needed, so we can return early | |
305 | // otherwise to do a manual chdir before exec. | |
9fa01778 XL |
306 | weak! { |
307 | fn posix_spawn_file_actions_addchdir_np( | |
308 | *mut libc::posix_spawn_file_actions_t, | |
309 | *const libc::c_char | |
310 | ) -> libc::c_int | |
311 | } | |
312 | let addchdir = match self.get_cwd() { | |
313 | Some(cwd) => match posix_spawn_file_actions_addchdir_np.get() { | |
314 | Some(f) => Some((f, cwd)), | |
315 | None => return Ok(None), | |
316 | }, | |
317 | None => None, | |
318 | }; | |
319 | ||
0531ce1d XL |
320 | let mut p = Process { pid: 0, status: None }; |
321 | ||
dc9dc135 | 322 | struct PosixSpawnFileActions(MaybeUninit<libc::posix_spawn_file_actions_t>); |
0531ce1d XL |
323 | |
324 | impl Drop for PosixSpawnFileActions { | |
325 | fn drop(&mut self) { | |
326 | unsafe { | |
dc9dc135 | 327 | libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); |
0531ce1d XL |
328 | } |
329 | } | |
330 | } | |
331 | ||
dc9dc135 | 332 | struct PosixSpawnattr(MaybeUninit<libc::posix_spawnattr_t>); |
0531ce1d XL |
333 | |
334 | impl Drop for PosixSpawnattr { | |
335 | fn drop(&mut self) { | |
336 | unsafe { | |
dc9dc135 | 337 | libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); |
0531ce1d XL |
338 | } |
339 | } | |
340 | } | |
341 | ||
342 | unsafe { | |
dc9dc135 XL |
343 | let mut file_actions = PosixSpawnFileActions(MaybeUninit::uninit()); |
344 | let mut attrs = PosixSpawnattr(MaybeUninit::uninit()); | |
0531ce1d | 345 | |
dc9dc135 XL |
346 | libc::posix_spawnattr_init(attrs.0.as_mut_ptr()); |
347 | libc::posix_spawn_file_actions_init(file_actions.0.as_mut_ptr()); | |
0531ce1d XL |
348 | |
349 | if let Some(fd) = stdio.stdin.fd() { | |
dfeec247 XL |
350 | cvt(libc::posix_spawn_file_actions_adddup2( |
351 | file_actions.0.as_mut_ptr(), | |
352 | fd, | |
353 | libc::STDIN_FILENO, | |
354 | ))?; | |
0531ce1d XL |
355 | } |
356 | if let Some(fd) = stdio.stdout.fd() { | |
dfeec247 XL |
357 | cvt(libc::posix_spawn_file_actions_adddup2( |
358 | file_actions.0.as_mut_ptr(), | |
359 | fd, | |
360 | libc::STDOUT_FILENO, | |
361 | ))?; | |
0531ce1d XL |
362 | } |
363 | if let Some(fd) = stdio.stderr.fd() { | |
dfeec247 XL |
364 | cvt(libc::posix_spawn_file_actions_adddup2( |
365 | file_actions.0.as_mut_ptr(), | |
366 | fd, | |
367 | libc::STDERR_FILENO, | |
368 | ))?; | |
0531ce1d | 369 | } |
9fa01778 | 370 | if let Some((f, cwd)) = addchdir { |
dc9dc135 | 371 | cvt(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; |
9fa01778 | 372 | } |
0531ce1d | 373 | |
dc9dc135 | 374 | let mut set = MaybeUninit::<libc::sigset_t>::uninit(); |
e1599b0c | 375 | cvt(sigemptyset(set.as_mut_ptr()))?; |
dfeec247 | 376 | cvt(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; |
e1599b0c | 377 | cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; |
dfeec247 | 378 | cvt(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; |
0531ce1d | 379 | |
dfeec247 | 380 | let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; |
dc9dc135 | 381 | cvt(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; |
0531ce1d | 382 | |
13cf67c4 XL |
383 | // Make sure we synchronize access to the global `environ` resource |
384 | let _env_lock = sys::os::env_lock(); | |
dfeec247 | 385 | let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); |
0531ce1d XL |
386 | let ret = libc::posix_spawnp( |
387 | &mut p.pid, | |
1b1a35ee | 388 | self.get_program_cstr().as_ptr(), |
dc9dc135 XL |
389 | file_actions.0.as_ptr(), |
390 | attrs.0.as_ptr(), | |
0531ce1d XL |
391 | self.get_argv().as_ptr() as *const _, |
392 | envp as *const _, | |
393 | ); | |
dfeec247 | 394 | if ret == 0 { Ok(Some(p)) } else { Err(io::Error::from_raw_os_error(ret)) } |
0531ce1d XL |
395 | } |
396 | } | |
476ff2be SL |
397 | } |
398 | ||
399 | //////////////////////////////////////////////////////////////////////////////// | |
400 | // Processes | |
401 | //////////////////////////////////////////////////////////////////////////////// | |
402 | ||
9fa01778 | 403 | /// The unique ID of the process (this should never be negative). |
476ff2be SL |
404 | pub struct Process { |
405 | pid: pid_t, | |
406 | status: Option<ExitStatus>, | |
407 | } | |
408 | ||
409 | impl Process { | |
410 | pub fn id(&self) -> u32 { | |
411 | self.pid as u32 | |
412 | } | |
413 | ||
414 | pub fn kill(&mut self) -> io::Result<()> { | |
415 | // If we've already waited on this process then the pid can be recycled | |
416 | // and used for another process, and we probably shouldn't be killing | |
417 | // random processes, so just return an error. | |
418 | if self.status.is_some() { | |
dfeec247 XL |
419 | Err(Error::new( |
420 | ErrorKind::InvalidInput, | |
421 | "invalid argument: can't kill an exited process", | |
422 | )) | |
476ff2be | 423 | } else { |
dfeec247 | 424 | cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) |
476ff2be SL |
425 | } |
426 | } | |
32a655c1 | 427 | |
476ff2be | 428 | pub fn wait(&mut self) -> io::Result<ExitStatus> { |
532ac7d7 | 429 | use crate::sys::cvt_r; |
476ff2be | 430 | if let Some(status) = self.status { |
dfeec247 | 431 | return Ok(status); |
476ff2be SL |
432 | } |
433 | let mut status = 0 as c_int; | |
434 | cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; | |
435 | self.status = Some(ExitStatus::new(status)); | |
436 | Ok(ExitStatus::new(status)) | |
437 | } | |
32a655c1 | 438 | |
8bb4bdeb | 439 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
32a655c1 | 440 | if let Some(status) = self.status { |
dfeec247 | 441 | return Ok(Some(status)); |
32a655c1 SL |
442 | } |
443 | let mut status = 0 as c_int; | |
dfeec247 | 444 | let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; |
32a655c1 | 445 | if pid == 0 { |
8bb4bdeb | 446 | Ok(None) |
32a655c1 SL |
447 | } else { |
448 | self.status = Some(ExitStatus::new(status)); | |
8bb4bdeb | 449 | Ok(Some(ExitStatus::new(status))) |
32a655c1 SL |
450 | } |
451 | } | |
476ff2be | 452 | } |
e74abb32 XL |
453 | |
454 | /// Unix exit statuses | |
455 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
456 | pub struct ExitStatus(c_int); | |
457 | ||
458 | impl ExitStatus { | |
459 | pub fn new(status: c_int) -> ExitStatus { | |
460 | ExitStatus(status) | |
461 | } | |
462 | ||
463 | fn exited(&self) -> bool { | |
1b1a35ee XL |
464 | // On Linux-like OSes this function is safe, on others it is not. See |
465 | // libc issue: https://github.com/rust-lang/libc/issues/1888. | |
466 | #[cfg_attr( | |
467 | any(target_os = "linux", target_os = "android", target_os = "emscripten"), | |
468 | allow(unused_unsafe) | |
469 | )] | |
470 | unsafe { | |
471 | libc::WIFEXITED(self.0) | |
472 | } | |
e74abb32 XL |
473 | } |
474 | ||
475 | pub fn success(&self) -> bool { | |
476 | self.code() == Some(0) | |
477 | } | |
478 | ||
479 | pub fn code(&self) -> Option<i32> { | |
1b1a35ee XL |
480 | // On Linux-like OSes this function is safe, on others it is not. See |
481 | // libc issue: https://github.com/rust-lang/libc/issues/1888. | |
482 | #[cfg_attr( | |
483 | any(target_os = "linux", target_os = "android", target_os = "emscripten"), | |
484 | allow(unused_unsafe) | |
485 | )] | |
dfeec247 | 486 | if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None } |
e74abb32 XL |
487 | } |
488 | ||
489 | pub fn signal(&self) -> Option<i32> { | |
1b1a35ee XL |
490 | // On Linux-like OSes this function is safe, on others it is not. See |
491 | // libc issue: https://github.com/rust-lang/libc/issues/1888. | |
492 | #[cfg_attr( | |
493 | any(target_os = "linux", target_os = "android", target_os = "emscripten"), | |
494 | allow(unused_unsafe) | |
495 | )] | |
dfeec247 | 496 | if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None } |
e74abb32 XL |
497 | } |
498 | } | |
499 | ||
3dfed10e | 500 | /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. |
e74abb32 XL |
501 | impl From<c_int> for ExitStatus { |
502 | fn from(a: c_int) -> ExitStatus { | |
503 | ExitStatus(a) | |
504 | } | |
505 | } | |
506 | ||
507 | impl fmt::Display for ExitStatus { | |
508 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
509 | if let Some(code) = self.code() { | |
510 | write!(f, "exit code: {}", code) | |
511 | } else { | |
512 | let signal = self.signal().unwrap(); | |
513 | write!(f, "signal: {}", signal) | |
514 | } | |
515 | } | |
516 | } |