1 use crate::convert::{TryFrom, TryInto}; 2 use crate::fmt; 3 use crate::io::{self, Error, ErrorKind}; 4 use crate::mem; 5 use crate::num::NonZeroI32; 6 use crate::os::raw::NonZero_c_int; 7 use crate::ptr; 8 use crate::sys; 9 use crate::sys::cvt; 10 use crate::sys::process::process_common::*; 11 12 #[cfg(target_os = "linux")] 13 use crate::os::linux::process::PidFd; 14 15 #[cfg(target_os = "linux")] 16 use crate::sys::weak::syscall; 17 18 #[cfg(any( 19 target_os = "macos", 20 target_os = "freebsd", 21 all(target_os = "linux", target_env = "gnu"), 22 all(target_os = "linux", target_env = "musl"), 23 ))] 24 use crate::sys::weak::weak; 25 26 #[cfg(target_os = "vxworks")] 27 use libc::RTP_ID as pid_t; 28 29 #[cfg(not(target_os = "vxworks"))] 30 use libc::{c_int, gid_t, pid_t, uid_t}; 31 32 //////////////////////////////////////////////////////////////////////////////// 33 // Command 34 //////////////////////////////////////////////////////////////////////////////// 35 36 impl Command { spawn( &mut self, default: Stdio, needs_stdin: bool, ) -> io::Result<(Process, StdioPipes)>37 pub fn spawn( 38 &mut self, 39 default: Stdio, 40 needs_stdin: bool, 41 ) -> io::Result<(Process, StdioPipes)> { 42 const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX"; 43 44 let envp = self.capture_env(); 45 46 if self.saw_nul() { 47 return Err(io::Error::new_const( 48 ErrorKind::InvalidInput, 49 &"nul byte found in provided data", 50 )); 51 } 52 53 let (ours, theirs) = self.setup_io(default, needs_stdin)?; 54 55 if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { 56 return Ok((ret, ours)); 57 } 58 59 let (input, output) = sys::pipe::anon_pipe()?; 60 61 // Whatever happens after the fork is almost for sure going to touch or 62 // look at the environment in one way or another (PATH in `execvp` or 63 // accessing the `environ` pointer ourselves). Make sure no other thread 64 // is accessing the environment when we do the fork itself. 65 // 66 // Note that as soon as we're done with the fork there's no need to hold 67 // a lock any more because the parent won't do anything and the child is 68 // in its own process. Thus the parent drops the lock guard while the child 69 // forgets it to avoid unlocking it on a new thread, which would be invalid. 70 let env_lock = sys::os::env_read_lock(); 71 let (pid, pidfd) = unsafe { self.do_fork()? }; 72 73 if pid == 0 { 74 crate::panic::always_abort(); 75 mem::forget(env_lock); 76 drop(input); 77 let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; 78 let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; 79 let errno = errno.to_be_bytes(); 80 let bytes = [ 81 errno[0], 82 errno[1], 83 errno[2], 84 errno[3], 85 CLOEXEC_MSG_FOOTER[0], 86 CLOEXEC_MSG_FOOTER[1], 87 CLOEXEC_MSG_FOOTER[2], 88 CLOEXEC_MSG_FOOTER[3], 89 ]; 90 // pipe I/O up to PIPE_BUF bytes should be atomic, and then 91 // we want to be sure we *don't* run at_exit destructors as 92 // we're being torn down regardless 93 rtassert!(output.write(&bytes).is_ok()); 94 unsafe { libc::_exit(1) } 95 } 96 97 drop(env_lock); 98 drop(output); 99 100 // Safety: We obtained the pidfd from calling `clone3` with 101 // `CLONE_PIDFD` so it's valid an otherwise unowned. 102 let mut p = unsafe { Process::new(pid, pidfd) }; 103 let mut bytes = [0; 8]; 104 105 // loop to handle EINTR 106 loop { 107 match input.read(&mut bytes) { 108 Ok(0) => return Ok((p, ours)), 109 Ok(8) => { 110 let (errno, footer) = bytes.split_at(4); 111 assert_eq!( 112 CLOEXEC_MSG_FOOTER, footer, 113 "Validation on the CLOEXEC pipe failed: {:?}", 114 bytes 115 ); 116 let errno = i32::from_be_bytes(errno.try_into().unwrap()); 117 assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); 118 return Err(Error::from_raw_os_error(errno)); 119 } 120 Err(ref e) if e.kind() == ErrorKind::Interrupted => {} 121 Err(e) => { 122 assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); 123 panic!("the CLOEXEC pipe failed: {:?}", e) 124 } 125 Ok(..) => { 126 // pipe I/O up to PIPE_BUF bytes should be atomic 127 assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); 128 panic!("short read on the CLOEXEC pipe") 129 } 130 } 131 } 132 } 133 134 // Attempts to fork the process. If successful, returns Ok((0, -1)) 135 // in the child, and Ok((child_pid, -1)) in the parent. 136 #[cfg(not(target_os = "linux"))] do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error>137 unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { 138 cvt(libc::fork()).map(|res| (res, -1)) 139 } 140 141 // Attempts to fork the process. If successful, returns Ok((0, -1)) 142 // in the child, and Ok((child_pid, child_pidfd)) in the parent. 143 #[cfg(target_os = "linux")] do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error>144 unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { 145 use crate::sync::atomic::{AtomicBool, Ordering}; 146 147 static HAS_CLONE3: AtomicBool = AtomicBool::new(true); 148 const CLONE_PIDFD: u64 = 0x00001000; 149 150 #[repr(C)] 151 struct clone_args { 152 flags: u64, 153 pidfd: u64, 154 child_tid: u64, 155 parent_tid: u64, 156 exit_signal: u64, 157 stack: u64, 158 stack_size: u64, 159 tls: u64, 160 set_tid: u64, 161 set_tid_size: u64, 162 cgroup: u64, 163 } 164 165 syscall! { 166 fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long 167 } 168 169 // Bypassing libc for `clone3` can make further libc calls unsafe, 170 // so we use it sparingly for now. See #89522 for details. 171 // Some tools (e.g. sandboxing tools) may also expect `fork` 172 // rather than `clone3`. 173 let want_clone3_pidfd = self.get_create_pidfd(); 174 175 // If we fail to create a pidfd for any reason, this will 176 // stay as -1, which indicates an error. 177 let mut pidfd: pid_t = -1; 178 179 // Attempt to use the `clone3` syscall, which supports more arguments 180 // (in particular, the ability to create a pidfd). If this fails, 181 // we will fall through this block to a call to `fork()` 182 if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) { 183 let mut args = clone_args { 184 flags: CLONE_PIDFD, 185 pidfd: &mut pidfd as *mut pid_t as u64, 186 child_tid: 0, 187 parent_tid: 0, 188 exit_signal: libc::SIGCHLD as u64, 189 stack: 0, 190 stack_size: 0, 191 tls: 0, 192 set_tid: 0, 193 set_tid_size: 0, 194 cgroup: 0, 195 }; 196 197 let args_ptr = &mut args as *mut clone_args; 198 let args_size = crate::mem::size_of::<clone_args>(); 199 200 let res = cvt(clone3(args_ptr, args_size)); 201 match res { 202 Ok(n) => return Ok((n as pid_t, pidfd)), 203 Err(e) => match e.raw_os_error() { 204 // Multiple threads can race to execute this store, 205 // but that's fine - that just means that multiple threads 206 // will have tried and failed to execute the same syscall, 207 // with no other side effects. 208 Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed), 209 // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp) 210 Some(libc::EPERM) => {} 211 _ => return Err(e), 212 }, 213 } 214 } 215 216 // Generally, we just call `fork`. If we get here after wanting `clone3`, 217 // then the syscall does not exist or we do not have permission to call it. 218 cvt(libc::fork()).map(|res| (res, pidfd)) 219 } 220 exec(&mut self, default: Stdio) -> io::Error221 pub fn exec(&mut self, default: Stdio) -> io::Error { 222 let envp = self.capture_env(); 223 224 if self.saw_nul() { 225 return io::Error::new_const( 226 ErrorKind::InvalidInput, 227 &"nul byte found in provided data", 228 ); 229 } 230 231 match self.setup_io(default, true) { 232 Ok((_, theirs)) => { 233 unsafe { 234 // Similar to when forking, we want to ensure that access to 235 // the environment is synchronized, so make sure to grab the 236 // environment lock before we try to exec. 237 let _lock = sys::os::env_read_lock(); 238 239 let Err(e) = self.do_exec(theirs, envp.as_ref()); 240 e 241 } 242 } 243 Err(e) => e, 244 } 245 } 246 247 // And at this point we've reached a special time in the life of the 248 // child. The child must now be considered hamstrung and unable to 249 // do anything other than syscalls really. Consider the following 250 // scenario: 251 // 252 // 1. Thread A of process 1 grabs the malloc() mutex 253 // 2. Thread B of process 1 forks(), creating thread C 254 // 3. Thread C of process 2 then attempts to malloc() 255 // 4. The memory of process 2 is the same as the memory of 256 // process 1, so the mutex is locked. 257 // 258 // This situation looks a lot like deadlock, right? It turns out 259 // that this is what pthread_atfork() takes care of, which is 260 // presumably implemented across platforms. The first thing that 261 // threads to *before* forking is to do things like grab the malloc 262 // mutex, and then after the fork they unlock it. 263 // 264 // Despite this information, libnative's spawn has been witnessed to 265 // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but 266 // all collected backtraces point at malloc/free traffic in the 267 // child spawned process. 268 // 269 // For this reason, the block of code below should contain 0 270 // invocations of either malloc of free (or their related friends). 271 // 272 // As an example of not having malloc/free traffic, we don't close 273 // this file descriptor by dropping the FileDesc (which contains an 274 // allocation). Instead we just close it manually. This will never 275 // have the drop glue anyway because this code never returns (the 276 // child will either exec() or invoke libc::exit) do_exec( &mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray>, ) -> Result<!, io::Error>277 unsafe fn do_exec( 278 &mut self, 279 stdio: ChildPipes, 280 maybe_envp: Option<&CStringArray>, 281 ) -> Result<!, io::Error> { 282 use crate::sys::{self, cvt_r}; 283 284 if let Some(fd) = stdio.stdin.fd() { 285 cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; 286 } 287 if let Some(fd) = stdio.stdout.fd() { 288 cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; 289 } 290 if let Some(fd) = stdio.stderr.fd() { 291 cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; 292 } 293 294 #[cfg(not(target_os = "l4re"))] 295 { 296 if let Some(_g) = self.get_groups() { 297 //FIXME: Redox kernel does not support setgroups yet 298 #[cfg(not(target_os = "redox"))] 299 cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?; 300 } 301 if let Some(u) = self.get_gid() { 302 cvt(libc::setgid(u as gid_t))?; 303 } 304 if let Some(u) = self.get_uid() { 305 // When dropping privileges from root, the `setgroups` call 306 // will remove any extraneous groups. We only drop groups 307 // if the current uid is 0 and we weren't given an explicit 308 // set of groups. If we don't call this, then even though our 309 // uid has dropped, we may still have groups that enable us to 310 // do super-user things. 311 //FIXME: Redox kernel does not support setgroups yet 312 #[cfg(not(target_os = "redox"))] 313 if libc::getuid() == 0 && self.get_groups().is_none() { 314 cvt(libc::setgroups(0, ptr::null()))?; 315 } 316 cvt(libc::setuid(u as uid_t))?; 317 } 318 } 319 if let Some(ref cwd) = *self.get_cwd() { 320 cvt(libc::chdir(cwd.as_ptr()))?; 321 } 322 323 // emscripten has no signal support. 324 #[cfg(not(target_os = "emscripten"))] 325 { 326 use crate::mem::MaybeUninit; 327 // Reset signal handling so the child process starts in a 328 // standardized state. libstd ignores SIGPIPE, and signal-handling 329 // libraries often set a mask. Child processes inherit ignored 330 // signals and the signal mask from their parent, but most 331 // UNIX programs do not reset these things on their own, so we 332 // need to clean things up now to avoid confusing the program 333 // we're about to run. 334 let mut set = MaybeUninit::<libc::sigset_t>::uninit(); 335 cvt(sigemptyset(set.as_mut_ptr()))?; 336 cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?; 337 338 #[cfg(target_os = "android")] // see issue #88585 339 { 340 let mut action: libc::sigaction = mem::zeroed(); 341 action.sa_sigaction = libc::SIG_DFL; 342 cvt(libc::sigaction(libc::SIGPIPE, &action, ptr::null_mut()))?; 343 } 344 #[cfg(not(target_os = "android"))] 345 { 346 let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); 347 if ret == libc::SIG_ERR { 348 return Err(io::Error::last_os_error()); 349 } 350 } 351 } 352 353 for callback in self.get_closures().iter_mut() { 354 callback()?; 355 } 356 357 // Although we're performing an exec here we may also return with an 358 // error from this function (without actually exec'ing) in which case we 359 // want to be sure to restore the global environment back to what it 360 // once was, ensuring that our temporary override, when free'd, doesn't 361 // corrupt our process's environment. 362 let mut _reset = None; 363 if let Some(envp) = maybe_envp { 364 struct Reset(*const *const libc::c_char); 365 366 impl Drop for Reset { 367 fn drop(&mut self) { 368 unsafe { 369 *sys::os::environ() = self.0; 370 } 371 } 372 } 373 374 _reset = Some(Reset(*sys::os::environ())); 375 *sys::os::environ() = envp.as_ptr(); 376 } 377 378 libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); 379 Err(io::Error::last_os_error()) 380 } 381 382 #[cfg(not(any( 383 target_os = "macos", 384 target_os = "freebsd", 385 all(target_os = "linux", target_env = "gnu"), 386 all(target_os = "linux", target_env = "musl"), 387 )))] posix_spawn( &mut self, _: &ChildPipes, _: Option<&CStringArray>, ) -> io::Result<Option<Process>>388 fn posix_spawn( 389 &mut self, 390 _: &ChildPipes, 391 _: Option<&CStringArray>, 392 ) -> io::Result<Option<Process>> { 393 Ok(None) 394 } 395 396 // Only support platforms for which posix_spawn() can return ENOENT 397 // directly. 398 #[cfg(any( 399 target_os = "macos", 400 target_os = "freebsd", 401 all(target_os = "linux", target_env = "gnu"), 402 all(target_os = "linux", target_env = "musl"), 403 ))] posix_spawn( &mut self, stdio: &ChildPipes, envp: Option<&CStringArray>, ) -> io::Result<Option<Process>>404 fn posix_spawn( 405 &mut self, 406 stdio: &ChildPipes, 407 envp: Option<&CStringArray>, 408 ) -> io::Result<Option<Process>> { 409 use crate::mem::MaybeUninit; 410 use crate::sys::{self, cvt_nz}; 411 412 if self.get_gid().is_some() 413 || self.get_uid().is_some() 414 || (self.env_saw_path() && !self.program_is_path()) 415 || !self.get_closures().is_empty() 416 || self.get_groups().is_some() 417 || self.get_create_pidfd() 418 { 419 return Ok(None); 420 } 421 422 // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. 423 #[cfg(all(target_os = "linux", target_env = "gnu"))] 424 { 425 if let Some(version) = sys::os::glibc_version() { 426 if version < (2, 24) { 427 return Ok(None); 428 } 429 } else { 430 return Ok(None); 431 } 432 } 433 434 // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory, 435 // and maybe others will gain this non-POSIX function too. We'll check 436 // for this weak symbol as soon as it's needed, so we can return early 437 // otherwise to do a manual chdir before exec. 438 weak! { 439 fn posix_spawn_file_actions_addchdir_np( 440 *mut libc::posix_spawn_file_actions_t, 441 *const libc::c_char 442 ) -> libc::c_int 443 } 444 let addchdir = match self.get_cwd() { 445 Some(cwd) => { 446 if cfg!(target_os = "macos") { 447 // There is a bug in macOS where a relative executable 448 // path like "../myprogram" will cause `posix_spawn` to 449 // successfully launch the program, but erroneously return 450 // ENOENT when used with posix_spawn_file_actions_addchdir_np 451 // which was introduced in macOS 10.15. 452 return Ok(None); 453 } 454 match posix_spawn_file_actions_addchdir_np.get() { 455 Some(f) => Some((f, cwd)), 456 None => return Ok(None), 457 } 458 } 459 None => None, 460 }; 461 462 // Safety: -1 indicates we don't have a pidfd. 463 let mut p = unsafe { Process::new(0, -1) }; 464 465 struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>); 466 467 impl Drop for PosixSpawnFileActions<'_> { 468 fn drop(&mut self) { 469 unsafe { 470 libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); 471 } 472 } 473 } 474 475 struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>); 476 477 impl Drop for PosixSpawnattr<'_> { 478 fn drop(&mut self) { 479 unsafe { 480 libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); 481 } 482 } 483 } 484 485 unsafe { 486 let mut attrs = MaybeUninit::uninit(); 487 cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?; 488 let attrs = PosixSpawnattr(&mut attrs); 489 490 let mut file_actions = MaybeUninit::uninit(); 491 cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?; 492 let file_actions = PosixSpawnFileActions(&mut file_actions); 493 494 if let Some(fd) = stdio.stdin.fd() { 495 cvt_nz(libc::posix_spawn_file_actions_adddup2( 496 file_actions.0.as_mut_ptr(), 497 fd, 498 libc::STDIN_FILENO, 499 ))?; 500 } 501 if let Some(fd) = stdio.stdout.fd() { 502 cvt_nz(libc::posix_spawn_file_actions_adddup2( 503 file_actions.0.as_mut_ptr(), 504 fd, 505 libc::STDOUT_FILENO, 506 ))?; 507 } 508 if let Some(fd) = stdio.stderr.fd() { 509 cvt_nz(libc::posix_spawn_file_actions_adddup2( 510 file_actions.0.as_mut_ptr(), 511 fd, 512 libc::STDERR_FILENO, 513 ))?; 514 } 515 if let Some((f, cwd)) = addchdir { 516 cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; 517 } 518 519 let mut set = MaybeUninit::<libc::sigset_t>::uninit(); 520 cvt(sigemptyset(set.as_mut_ptr()))?; 521 cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; 522 cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; 523 cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; 524 525 let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; 526 cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; 527 528 // Make sure we synchronize access to the global `environ` resource 529 let _env_lock = sys::os::env_read_lock(); 530 let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); 531 cvt_nz(libc::posix_spawnp( 532 &mut p.pid, 533 self.get_program_cstr().as_ptr(), 534 file_actions.0.as_ptr(), 535 attrs.0.as_ptr(), 536 self.get_argv().as_ptr() as *const _, 537 envp as *const _, 538 ))?; 539 Ok(Some(p)) 540 } 541 } 542 } 543 544 //////////////////////////////////////////////////////////////////////////////// 545 // Processes 546 //////////////////////////////////////////////////////////////////////////////// 547 548 /// The unique ID of the process (this should never be negative). 549 pub struct Process { 550 pid: pid_t, 551 status: Option<ExitStatus>, 552 // On Linux, stores the pidfd created for this child. 553 // This is None if the user did not request pidfd creation, 554 // or if the pidfd could not be created for some reason 555 // (e.g. the `clone3` syscall was not available). 556 #[cfg(target_os = "linux")] 557 pidfd: Option<PidFd>, 558 } 559 560 impl Process { 561 #[cfg(target_os = "linux")] new(pid: pid_t, pidfd: pid_t) -> Self562 unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { 563 use crate::os::unix::io::FromRawFd; 564 use crate::sys_common::FromInner; 565 // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. 566 let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd))); 567 Process { pid, status: None, pidfd } 568 } 569 570 #[cfg(not(target_os = "linux"))] new(pid: pid_t, _pidfd: pid_t) -> Self571 unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self { 572 Process { pid, status: None } 573 } 574 id(&self) -> u32575 pub fn id(&self) -> u32 { 576 self.pid as u32 577 } 578 kill(&mut self) -> io::Result<()>579 pub fn kill(&mut self) -> io::Result<()> { 580 // If we've already waited on this process then the pid can be recycled 581 // and used for another process, and we probably shouldn't be killing 582 // random processes, so just return an error. 583 if self.status.is_some() { 584 Err(Error::new_const( 585 ErrorKind::InvalidInput, 586 &"invalid argument: can't kill an exited process", 587 )) 588 } else { 589 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) 590 } 591 } 592 wait(&mut self) -> io::Result<ExitStatus>593 pub fn wait(&mut self) -> io::Result<ExitStatus> { 594 use crate::sys::cvt_r; 595 if let Some(status) = self.status { 596 return Ok(status); 597 } 598 let mut status = 0 as c_int; 599 cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; 600 self.status = Some(ExitStatus::new(status)); 601 Ok(ExitStatus::new(status)) 602 } 603 try_wait(&mut self) -> io::Result<Option<ExitStatus>>604 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { 605 if let Some(status) = self.status { 606 return Ok(Some(status)); 607 } 608 let mut status = 0 as c_int; 609 let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; 610 if pid == 0 { 611 Ok(None) 612 } else { 613 self.status = Some(ExitStatus::new(status)); 614 Ok(Some(ExitStatus::new(status))) 615 } 616 } 617 } 618 619 /// Unix exit statuses 620 // 621 // This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". 622 // See the discussion in comments and doc comments for `std::process::ExitStatus`. 623 #[derive(PartialEq, Eq, Clone, Copy)] 624 pub struct ExitStatus(c_int); 625 626 impl fmt::Debug for ExitStatus { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result627 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 628 f.debug_tuple("unix_wait_status").field(&self.0).finish() 629 } 630 } 631 632 impl ExitStatus { new(status: c_int) -> ExitStatus633 pub fn new(status: c_int) -> ExitStatus { 634 ExitStatus(status) 635 } 636 exited(&self) -> bool637 fn exited(&self) -> bool { 638 libc::WIFEXITED(self.0) 639 } 640 exit_ok(&self) -> Result<(), ExitStatusError>641 pub fn exit_ok(&self) -> Result<(), ExitStatusError> { 642 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is 643 // true on all actual versions of Unix, is widely assumed, and is specified in SuS 644 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not 645 // true for a platform pretending to be Unix, the tests (our doctests, and also 646 // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. 647 match NonZero_c_int::try_from(self.0) { 648 /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), 649 /* was zero, couldn't convert */ Err(_) => Ok(()), 650 } 651 } 652 code(&self) -> Option<i32>653 pub fn code(&self) -> Option<i32> { 654 if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } 655 } 656 signal(&self) -> Option<i32>657 pub fn signal(&self) -> Option<i32> { 658 if libc::WIFSIGNALED(self.0) { Some(libc::WTERMSIG(self.0)) } else { None } 659 } 660 core_dumped(&self) -> bool661 pub fn core_dumped(&self) -> bool { 662 libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0) 663 } 664 stopped_signal(&self) -> Option<i32>665 pub fn stopped_signal(&self) -> Option<i32> { 666 if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } 667 } 668 continued(&self) -> bool669 pub fn continued(&self) -> bool { 670 libc::WIFCONTINUED(self.0) 671 } 672 into_raw(&self) -> c_int673 pub fn into_raw(&self) -> c_int { 674 self.0 675 } 676 } 677 678 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. 679 impl From<c_int> for ExitStatus { from(a: c_int) -> ExitStatus680 fn from(a: c_int) -> ExitStatus { 681 ExitStatus(a) 682 } 683 } 684 685 impl fmt::Display for ExitStatus { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result686 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 687 if let Some(code) = self.code() { 688 write!(f, "exit status: {}", code) 689 } else if let Some(signal) = self.signal() { 690 if self.core_dumped() { 691 write!(f, "signal: {} (core dumped)", signal) 692 } else { 693 write!(f, "signal: {}", signal) 694 } 695 } else if let Some(signal) = self.stopped_signal() { 696 write!(f, "stopped (not terminated) by signal: {}", signal) 697 } else if self.continued() { 698 write!(f, "continued (WIFCONTINUED)") 699 } else { 700 write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0) 701 } 702 } 703 } 704 705 #[derive(PartialEq, Eq, Clone, Copy)] 706 pub struct ExitStatusError(NonZero_c_int); 707 708 impl Into<ExitStatus> for ExitStatusError { into(self) -> ExitStatus709 fn into(self) -> ExitStatus { 710 ExitStatus(self.0.into()) 711 } 712 } 713 714 impl fmt::Debug for ExitStatusError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 716 f.debug_tuple("unix_wait_status").field(&self.0).finish() 717 } 718 } 719 720 impl ExitStatusError { code(self) -> Option<NonZeroI32>721 pub fn code(self) -> Option<NonZeroI32> { 722 ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) 723 } 724 } 725 726 #[cfg(target_os = "linux")] 727 #[unstable(feature = "linux_pidfd", issue = "82971")] 728 impl crate::os::linux::process::ChildExt for crate::process::Child { pidfd(&self) -> io::Result<&PidFd>729 fn pidfd(&self) -> io::Result<&PidFd> { 730 self.handle 731 .pidfd 732 .as_ref() 733 .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created.")) 734 } 735 take_pidfd(&mut self) -> io::Result<PidFd>736 fn take_pidfd(&mut self) -> io::Result<PidFd> { 737 self.handle 738 .pidfd 739 .take() 740 .ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created.")) 741 } 742 } 743 744 #[cfg(test)] 745 #[path = "process_unix/tests.rs"] 746 mod tests; 747