2021-03-03 12:17:16 +00:00
|
|
|
use crate::convert::{TryFrom, TryInto};
|
2019-09-25 04:34:44 +00:00
|
|
|
use crate::fmt;
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::io::{self, Error, ErrorKind};
|
2021-03-09 20:42:38 +00:00
|
|
|
use crate::mem;
|
2021-03-03 12:17:16 +00:00
|
|
|
use crate::num::NonZeroI32;
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::ptr;
|
2019-12-22 22:42:04 +00:00
|
|
|
use crate::sys;
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::sys::cvt;
|
|
|
|
use crate::sys::process::process_common::*;
|
2022-03-01 22:46:41 +00:00
|
|
|
use core::ffi::NonZero_c_int;
|
2021-02-06 13:15:49 +00:00
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
use crate::os::linux::process::PidFd;
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
Refactor weak symbols in std::sys::unix
This makes a few changes to the weak symbol macros in `sys::unix`:
- `dlsym!` is added to keep the functionality for runtime `dlsym`
lookups, like for `__pthread_get_minstack@GLIBC_PRIVATE` that we don't
want to show up in ELF symbol tables.
- `weak!` now uses `#[linkage = "extern_weak"]` symbols, so its runtime
behavior is just a simple null check. This is also used by `syscall!`.
- On non-ELF targets (macos/ios) where that linkage is not known to
behave, `weak!` is just an alias to `dlsym!` for the old behavior.
- `raw_syscall!` is added to always call `libc::syscall` on linux and
android, for cases like `clone3` that have no known libc wrapper.
The new `weak!` linkage does mean that you'll get versioned symbols if
you build with a newer glibc, like `WEAK DEFAULT UND statx@GLIBC_2.28`.
This might seem problematic, but old non-weak symbols can tie the build
to new versions too, like `dlsym@GLIBC_2.34` from their recent library
unification. If you build with an old glibc like `dist-x86_64-linux`
does, you'll still get unversioned `WEAK DEFAULT UND statx`, which may
be resolved based on the runtime glibc.
I also found a few functions that don't need to be weak anymore:
- Android can directly use `ftruncate64`, `pread64`, and `pwrite64`, as
these were added in API 12, and our baseline is API 14.
- Linux can directly use `splice`, added way back in glibc 2.5 and
similarly old musl. Android only added it in API 21 though.
2021-11-12 20:58:38 +00:00
|
|
|
use crate::sys::weak::raw_syscall;
|
2019-02-10 19:23:21 +00:00
|
|
|
|
2021-07-06 03:28:10 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "freebsd",
|
|
|
|
all(target_os = "linux", target_env = "gnu"),
|
|
|
|
all(target_os = "linux", target_env = "musl"),
|
|
|
|
))]
|
|
|
|
use crate::sys::weak::weak;
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(target_os = "vxworks")]
|
|
|
|
use libc::RTP_ID as pid_t;
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "vxworks"))]
|
2019-02-10 19:23:21 +00:00
|
|
|
use libc::{c_int, gid_t, pid_t, uid_t};
|
2016-11-23 21:58:13 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Command
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
impl Command {
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn spawn(
|
|
|
|
&mut self,
|
|
|
|
default: Stdio,
|
|
|
|
needs_stdin: bool,
|
|
|
|
) -> io::Result<(Process, StdioPipes)> {
|
2020-07-12 16:47:15 +00:00
|
|
|
const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
|
2016-11-23 21:58:13 +00:00
|
|
|
|
2017-12-17 15:21:47 +00:00
|
|
|
let envp = self.capture_env();
|
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
if self.saw_nul() {
|
2021-08-08 08:04:33 +00:00
|
|
|
return Err(io::const_io_error!(
|
2021-03-21 19:22:38 +00:00
|
|
|
ErrorKind::InvalidInput,
|
2021-08-08 08:04:33 +00:00
|
|
|
"nul byte found in provided data",
|
2021-03-21 19:22:38 +00:00
|
|
|
));
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
|
2018-01-26 02:13:45 +00:00
|
|
|
|
|
|
|
if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Ok((ret, ours));
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
let (input, output) = sys::pipe::anon_pipe()?;
|
|
|
|
|
2018-11-13 22:57:10 +00:00
|
|
|
// Whatever happens after the fork is almost for sure going to touch or
|
|
|
|
// look at the environment in one way or another (PATH in `execvp` or
|
|
|
|
// accessing the `environ` pointer ourselves). Make sure no other thread
|
|
|
|
// is accessing the environment when we do the fork itself.
|
|
|
|
//
|
|
|
|
// Note that as soon as we're done with the fork there's no need to hold
|
|
|
|
// a lock any more because the parent won't do anything and the child is
|
2021-03-09 20:42:38 +00:00
|
|
|
// in its own process. Thus the parent drops the lock guard while the child
|
|
|
|
// forgets it to avoid unlocking it on a new thread, which would be invalid.
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
let env_lock = sys::os::env_read_lock();
|
2021-02-06 13:15:49 +00:00
|
|
|
let (pid, pidfd) = unsafe { self.do_fork()? };
|
2018-11-13 22:57:10 +00:00
|
|
|
|
2021-03-15 20:35:47 +00:00
|
|
|
if pid == 0 {
|
2021-04-20 19:04:31 +00:00
|
|
|
crate::panic::always_abort();
|
2021-03-15 20:35:47 +00:00
|
|
|
mem::forget(env_lock);
|
|
|
|
drop(input);
|
|
|
|
let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
|
|
|
|
let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
|
|
|
|
let errno = errno.to_be_bytes();
|
|
|
|
let bytes = [
|
|
|
|
errno[0],
|
|
|
|
errno[1],
|
|
|
|
errno[2],
|
|
|
|
errno[3],
|
|
|
|
CLOEXEC_MSG_FOOTER[0],
|
|
|
|
CLOEXEC_MSG_FOOTER[1],
|
|
|
|
CLOEXEC_MSG_FOOTER[2],
|
|
|
|
CLOEXEC_MSG_FOOTER[3],
|
|
|
|
];
|
|
|
|
// pipe I/O up to PIPE_BUF bytes should be atomic, and then
|
|
|
|
// we want to be sure we *don't* run at_exit destructors as
|
|
|
|
// we're being torn down regardless
|
|
|
|
rtassert!(output.write(&bytes).is_ok());
|
|
|
|
unsafe { libc::_exit(1) }
|
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
|
2021-03-15 20:35:47 +00:00
|
|
|
drop(env_lock);
|
2016-11-23 21:58:13 +00:00
|
|
|
drop(output);
|
2021-03-15 20:35:47 +00:00
|
|
|
|
2021-08-03 12:31:59 +00:00
|
|
|
// Safety: We obtained the pidfd from calling `clone3` with
|
|
|
|
// `CLONE_PIDFD` so it's valid an otherwise unowned.
|
|
|
|
let mut p = unsafe { Process::new(pid, pidfd) };
|
2016-11-23 21:58:13 +00:00
|
|
|
let mut bytes = [0; 8];
|
|
|
|
|
|
|
|
// loop to handle EINTR
|
|
|
|
loop {
|
|
|
|
match input.read(&mut bytes) {
|
|
|
|
Ok(0) => return Ok((p, ours)),
|
|
|
|
Ok(8) => {
|
2020-07-12 16:47:15 +00:00
|
|
|
let (errno, footer) = bytes.split_at(4);
|
2020-07-15 08:14:55 +00:00
|
|
|
assert_eq!(
|
|
|
|
CLOEXEC_MSG_FOOTER, footer,
|
2019-12-22 22:42:04 +00:00
|
|
|
"Validation on the CLOEXEC pipe failed: {:?}",
|
|
|
|
bytes
|
|
|
|
);
|
2020-07-15 08:14:55 +00:00
|
|
|
let errno = i32::from_be_bytes(errno.try_into().unwrap());
|
2019-12-22 22:42:04 +00:00
|
|
|
assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
|
|
|
|
return Err(Error::from_raw_os_error(errno));
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
|
|
Err(e) => {
|
2019-12-22 22:42:04 +00:00
|
|
|
assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
|
2016-11-23 21:58:13 +00:00
|
|
|
panic!("the CLOEXEC pipe failed: {:?}", e)
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
|
|
|
Ok(..) => {
|
|
|
|
// pipe I/O up to PIPE_BUF bytes should be atomic
|
|
|
|
assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
|
2016-11-23 21:58:13 +00:00
|
|
|
panic!("short read on the CLOEXEC pipe")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-06 13:15:49 +00:00
|
|
|
// Attempts to fork the process. If successful, returns Ok((0, -1))
|
|
|
|
// in the child, and Ok((child_pid, -1)) in the parent.
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
|
|
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
|
|
|
|
cvt(libc::fork()).map(|res| (res, -1))
|
|
|
|
}
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
|
2021-02-06 13:15:49 +00:00
|
|
|
// Attempts to fork the process. If successful, returns Ok((0, -1))
|
|
|
|
// in the child, and Ok((child_pid, child_pidfd)) in the parent.
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
|
|
|
|
use crate::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
|
|
|
|
static HAS_CLONE3: AtomicBool = AtomicBool::new(true);
|
|
|
|
const CLONE_PIDFD: u64 = 0x00001000;
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
struct clone_args {
|
|
|
|
flags: u64,
|
|
|
|
pidfd: u64,
|
|
|
|
child_tid: u64,
|
|
|
|
parent_tid: u64,
|
|
|
|
exit_signal: u64,
|
|
|
|
stack: u64,
|
|
|
|
stack_size: u64,
|
|
|
|
tls: u64,
|
|
|
|
set_tid: u64,
|
|
|
|
set_tid_size: u64,
|
|
|
|
cgroup: u64,
|
|
|
|
}
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
|
Refactor weak symbols in std::sys::unix
This makes a few changes to the weak symbol macros in `sys::unix`:
- `dlsym!` is added to keep the functionality for runtime `dlsym`
lookups, like for `__pthread_get_minstack@GLIBC_PRIVATE` that we don't
want to show up in ELF symbol tables.
- `weak!` now uses `#[linkage = "extern_weak"]` symbols, so its runtime
behavior is just a simple null check. This is also used by `syscall!`.
- On non-ELF targets (macos/ios) where that linkage is not known to
behave, `weak!` is just an alias to `dlsym!` for the old behavior.
- `raw_syscall!` is added to always call `libc::syscall` on linux and
android, for cases like `clone3` that have no known libc wrapper.
The new `weak!` linkage does mean that you'll get versioned symbols if
you build with a newer glibc, like `WEAK DEFAULT UND statx@GLIBC_2.28`.
This might seem problematic, but old non-weak symbols can tie the build
to new versions too, like `dlsym@GLIBC_2.34` from their recent library
unification. If you build with an old glibc like `dist-x86_64-linux`
does, you'll still get unversioned `WEAK DEFAULT UND statx`, which may
be resolved based on the runtime glibc.
I also found a few functions that don't need to be weak anymore:
- Android can directly use `ftruncate64`, `pread64`, and `pwrite64`, as
these were added in API 12, and our baseline is API 14.
- Linux can directly use `splice`, added way back in glibc 2.5 and
similarly old musl. Android only added it in API 21 though.
2021-11-12 20:58:38 +00:00
|
|
|
raw_syscall! {
|
2021-02-06 13:15:49 +00:00
|
|
|
fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
|
|
|
|
}
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
|
2021-10-15 19:21:45 +00:00
|
|
|
// Bypassing libc for `clone3` can make further libc calls unsafe,
|
|
|
|
// so we use it sparingly for now. See #89522 for details.
|
2021-10-15 22:29:15 +00:00
|
|
|
// Some tools (e.g. sandboxing tools) may also expect `fork`
|
|
|
|
// rather than `clone3`.
|
2021-10-15 19:21:45 +00:00
|
|
|
let want_clone3_pidfd = self.get_create_pidfd();
|
|
|
|
|
2021-02-06 13:15:49 +00:00
|
|
|
// If we fail to create a pidfd for any reason, this will
|
|
|
|
// stay as -1, which indicates an error.
|
|
|
|
let mut pidfd: pid_t = -1;
|
|
|
|
|
|
|
|
// Attempt to use the `clone3` syscall, which supports more arguments
|
|
|
|
// (in particular, the ability to create a pidfd). If this fails,
|
|
|
|
// we will fall through this block to a call to `fork()`
|
2021-10-15 19:21:45 +00:00
|
|
|
if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) {
|
2021-02-06 13:15:49 +00:00
|
|
|
let mut args = clone_args {
|
2021-10-15 19:21:45 +00:00
|
|
|
flags: CLONE_PIDFD,
|
2021-02-06 13:15:49 +00:00
|
|
|
pidfd: &mut pidfd as *mut pid_t as u64,
|
|
|
|
child_tid: 0,
|
|
|
|
parent_tid: 0,
|
|
|
|
exit_signal: libc::SIGCHLD as u64,
|
|
|
|
stack: 0,
|
|
|
|
stack_size: 0,
|
|
|
|
tls: 0,
|
|
|
|
set_tid: 0,
|
|
|
|
set_tid_size: 0,
|
|
|
|
cgroup: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
let args_ptr = &mut args as *mut clone_args;
|
|
|
|
let args_size = crate::mem::size_of::<clone_args>();
|
|
|
|
|
|
|
|
let res = cvt(clone3(args_ptr, args_size));
|
|
|
|
match res {
|
|
|
|
Ok(n) => return Ok((n as pid_t, pidfd)),
|
|
|
|
Err(e) => match e.raw_os_error() {
|
|
|
|
// Multiple threads can race to execute this store,
|
|
|
|
// but that's fine - that just means that multiple threads
|
|
|
|
// will have tried and failed to execute the same syscall,
|
|
|
|
// with no other side effects.
|
|
|
|
Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed),
|
|
|
|
// Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp)
|
|
|
|
Some(libc::EPERM) => {}
|
|
|
|
_ => return Err(e),
|
|
|
|
},
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 21:45:23 +00:00
|
|
|
// Generally, we just call `fork`. If we get here after wanting `clone3`,
|
|
|
|
// then the syscall does not exist or we do not have permission to call it.
|
2021-02-06 13:15:49 +00:00
|
|
|
cvt(libc::fork()).map(|res| (res, pidfd))
|
|
|
|
}
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
pub fn exec(&mut self, default: Stdio) -> io::Error {
|
2017-12-17 15:21:47 +00:00
|
|
|
let envp = self.capture_env();
|
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
if self.saw_nul() {
|
2021-08-08 08:04:33 +00:00
|
|
|
return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match self.setup_io(default, true) {
|
2018-11-13 22:57:10 +00:00
|
|
|
Ok((_, theirs)) => {
|
|
|
|
unsafe {
|
|
|
|
// Similar to when forking, we want to ensure that access to
|
|
|
|
// the environment is synchronized, so make sure to grab the
|
|
|
|
// environment lock before we try to exec.
|
2021-03-14 18:10:34 +00:00
|
|
|
let _lock = sys::os::env_read_lock();
|
2018-11-13 22:57:10 +00:00
|
|
|
|
2019-05-06 13:40:34 +00:00
|
|
|
let Err(e) = self.do_exec(theirs, envp.as_ref());
|
|
|
|
e
|
2018-11-13 22:57:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
Err(e) => e,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// And at this point we've reached a special time in the life of the
|
|
|
|
// child. The child must now be considered hamstrung and unable to
|
|
|
|
// do anything other than syscalls really. Consider the following
|
|
|
|
// scenario:
|
|
|
|
//
|
|
|
|
// 1. Thread A of process 1 grabs the malloc() mutex
|
|
|
|
// 2. Thread B of process 1 forks(), creating thread C
|
|
|
|
// 3. Thread C of process 2 then attempts to malloc()
|
|
|
|
// 4. The memory of process 2 is the same as the memory of
|
|
|
|
// process 1, so the mutex is locked.
|
|
|
|
//
|
|
|
|
// This situation looks a lot like deadlock, right? It turns out
|
|
|
|
// that this is what pthread_atfork() takes care of, which is
|
|
|
|
// presumably implemented across platforms. The first thing that
|
|
|
|
// threads to *before* forking is to do things like grab the malloc
|
|
|
|
// mutex, and then after the fork they unlock it.
|
|
|
|
//
|
|
|
|
// Despite this information, libnative's spawn has been witnessed to
|
2017-03-12 18:13:35 +00:00
|
|
|
// deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
|
2016-11-23 21:58:13 +00:00
|
|
|
// all collected backtraces point at malloc/free traffic in the
|
|
|
|
// child spawned process.
|
|
|
|
//
|
|
|
|
// For this reason, the block of code below should contain 0
|
|
|
|
// invocations of either malloc of free (or their related friends).
|
|
|
|
//
|
|
|
|
// As an example of not having malloc/free traffic, we don't close
|
|
|
|
// this file descriptor by dropping the FileDesc (which contains an
|
|
|
|
// allocation). Instead we just close it manually. This will never
|
|
|
|
// have the drop glue anyway because this code never returns (the
|
|
|
|
// child will either exec() or invoke libc::exit)
|
2017-12-17 15:21:47 +00:00
|
|
|
unsafe fn do_exec(
|
|
|
|
&mut self,
|
|
|
|
stdio: ChildPipes,
|
2019-12-22 22:42:04 +00:00
|
|
|
maybe_envp: Option<&CStringArray>,
|
2019-05-06 13:40:34 +00:00
|
|
|
) -> Result<!, io::Error> {
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::sys::{self, cvt_r};
|
2016-11-23 21:58:13 +00:00
|
|
|
|
|
|
|
if let Some(fd) = stdio.stdin.fd() {
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
if let Some(fd) = stdio.stdout.fd() {
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
if let Some(fd) = stdio.stderr.fd() {
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
2019-08-19 20:02:50 +00:00
|
|
|
#[cfg(not(target_os = "l4re"))]
|
2019-08-19 20:01:02 +00:00
|
|
|
{
|
2020-06-16 04:39:34 +00:00
|
|
|
if let Some(_g) = self.get_groups() {
|
|
|
|
//FIXME: Redox kernel does not support setgroups yet
|
|
|
|
#[cfg(not(target_os = "redox"))]
|
|
|
|
cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
|
|
|
|
}
|
2017-08-17 11:23:00 +00:00
|
|
|
if let Some(u) = self.get_gid() {
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt(libc::setgid(u as gid_t))?;
|
2017-08-17 11:23:00 +00:00
|
|
|
}
|
|
|
|
if let Some(u) = self.get_uid() {
|
2019-08-19 19:58:35 +00:00
|
|
|
// When dropping privileges from root, the `setgroups` call
|
2020-06-16 04:39:34 +00:00
|
|
|
// will remove any extraneous groups. We only drop groups
|
|
|
|
// if the current uid is 0 and we weren't given an explicit
|
|
|
|
// set of groups. If we don't call this, then even though our
|
|
|
|
// uid has dropped, we may still have groups that enable us to
|
|
|
|
// do super-user things.
|
2019-04-07 14:39:54 +00:00
|
|
|
//FIXME: Redox kernel does not support setgroups yet
|
2019-08-19 19:58:35 +00:00
|
|
|
#[cfg(not(target_os = "redox"))]
|
2020-06-16 04:39:34 +00:00
|
|
|
if libc::getuid() == 0 && self.get_groups().is_none() {
|
|
|
|
cvt(libc::setgroups(0, ptr::null()))?;
|
|
|
|
}
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt(libc::setuid(u as uid_t))?;
|
2017-08-17 11:23:00 +00:00
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
if let Some(ref cwd) = *self.get_cwd() {
|
2019-05-06 12:54:27 +00:00
|
|
|
cvt(libc::chdir(cwd.as_ptr()))?;
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
2017-10-05 03:01:41 +00:00
|
|
|
// emscripten has no signal support.
|
2019-08-19 20:02:50 +00:00
|
|
|
#[cfg(not(target_os = "emscripten"))]
|
2017-07-17 16:24:05 +00:00
|
|
|
{
|
2019-06-26 23:27:54 +00:00
|
|
|
use crate::mem::MaybeUninit;
|
2016-11-23 21:58:13 +00:00
|
|
|
// Reset signal handling so the child process starts in a
|
|
|
|
// standardized state. libstd ignores SIGPIPE, and signal-handling
|
|
|
|
// libraries often set a mask. Child processes inherit ignored
|
|
|
|
// signals and the signal mask from their parent, but most
|
|
|
|
// UNIX programs do not reset these things on their own, so we
|
|
|
|
// need to clean things up now to avoid confusing the program
|
|
|
|
// we're about to run.
|
2019-06-26 22:03:15 +00:00
|
|
|
let mut set = MaybeUninit::<libc::sigset_t>::uninit();
|
2019-08-19 23:14:07 +00:00
|
|
|
cvt(sigemptyset(set.as_mut_ptr()))?;
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?;
|
2021-10-01 19:22:18 +00:00
|
|
|
|
|
|
|
#[cfg(target_os = "android")] // see issue #88585
|
|
|
|
{
|
|
|
|
let mut action: libc::sigaction = mem::zeroed();
|
|
|
|
action.sa_sigaction = libc::SIG_DFL;
|
|
|
|
cvt(libc::sigaction(libc::SIGPIPE, &action, ptr::null_mut()))?;
|
|
|
|
}
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
{
|
|
|
|
let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
|
|
|
|
if ret == libc::SIG_ERR {
|
|
|
|
return Err(io::Error::last_os_error());
|
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for callback in self.get_closures().iter_mut() {
|
2019-05-06 12:54:27 +00:00
|
|
|
callback()?;
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
2018-11-13 22:57:10 +00:00
|
|
|
// Although we're performing an exec here we may also return with an
|
|
|
|
// error from this function (without actually exec'ing) in which case we
|
|
|
|
// want to be sure to restore the global environment back to what it
|
|
|
|
// once was, ensuring that our temporary override, when free'd, doesn't
|
|
|
|
// corrupt our process's environment.
|
|
|
|
let mut _reset = None;
|
|
|
|
if let Some(envp) = maybe_envp {
|
|
|
|
struct Reset(*const *const libc::c_char);
|
|
|
|
|
|
|
|
impl Drop for Reset {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
*sys::os::environ() = self.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_reset = Some(Reset(*sys::os::environ()));
|
|
|
|
*sys::os::environ() = envp.as_ptr();
|
|
|
|
}
|
|
|
|
|
2020-09-21 18:32:06 +00:00
|
|
|
libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
|
2019-05-06 13:40:34 +00:00
|
|
|
Err(io::Error::last_os_error())
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "freebsd",
|
2021-02-06 13:15:49 +00:00
|
|
|
all(target_os = "linux", target_env = "gnu"),
|
|
|
|
all(target_os = "linux", target_env = "musl"),
|
2019-12-22 22:42:04 +00:00
|
|
|
)))]
|
|
|
|
fn posix_spawn(
|
|
|
|
&mut self,
|
|
|
|
_: &ChildPipes,
|
|
|
|
_: Option<&CStringArray>,
|
|
|
|
) -> io::Result<Option<Process>> {
|
2018-01-26 02:13:45 +00:00
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2018-03-01 17:18:16 +00:00
|
|
|
// Only support platforms for which posix_spawn() can return ENOENT
|
|
|
|
// directly.
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "freebsd",
|
2021-02-06 13:15:49 +00:00
|
|
|
all(target_os = "linux", target_env = "gnu"),
|
|
|
|
all(target_os = "linux", target_env = "musl"),
|
2019-12-22 22:42:04 +00:00
|
|
|
))]
|
|
|
|
fn posix_spawn(
|
|
|
|
&mut self,
|
|
|
|
stdio: &ChildPipes,
|
|
|
|
envp: Option<&CStringArray>,
|
|
|
|
) -> io::Result<Option<Process>> {
|
2019-06-26 22:03:15 +00:00
|
|
|
use crate::mem::MaybeUninit;
|
2020-10-20 00:00:00 +00:00
|
|
|
use crate::sys::{self, cvt_nz};
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
if self.get_gid().is_some()
|
|
|
|
|| self.get_uid().is_some()
|
2020-10-02 14:44:32 +00:00
|
|
|
|| (self.env_saw_path() && !self.program_is_path())
|
2019-12-22 22:42:04 +00:00
|
|
|
|| !self.get_closures().is_empty()
|
2020-06-16 04:39:34 +00:00
|
|
|
|| self.get_groups().is_some()
|
2021-02-06 13:15:49 +00:00
|
|
|
|| self.get_create_pidfd()
|
2019-12-22 22:42:04 +00:00
|
|
|
{
|
|
|
|
return Ok(None);
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
|
2018-03-02 21:02:38 +00:00
|
|
|
// Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
|
|
|
|
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
|
|
|
{
|
|
|
|
if let Some(version) = sys::os::glibc_version() {
|
|
|
|
if version < (2, 24) {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Ok(None);
|
2018-03-02 21:02:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Ok(None);
|
2018-03-02 21:02:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-01 00:00:00 +00:00
|
|
|
// Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
|
|
|
|
// and maybe others will gain this non-POSIX function too. We'll check
|
|
|
|
// for this weak symbol as soon as it's needed, so we can return early
|
|
|
|
// otherwise to do a manual chdir before exec.
|
2019-02-13 20:20:23 +00:00
|
|
|
weak! {
|
|
|
|
fn posix_spawn_file_actions_addchdir_np(
|
|
|
|
*mut libc::posix_spawn_file_actions_t,
|
|
|
|
*const libc::c_char
|
|
|
|
) -> libc::c_int
|
|
|
|
}
|
|
|
|
let addchdir = match self.get_cwd() {
|
2020-12-30 22:56:51 +00:00
|
|
|
Some(cwd) => {
|
|
|
|
if cfg!(target_os = "macos") {
|
|
|
|
// There is a bug in macOS where a relative executable
|
|
|
|
// path like "../myprogram" will cause `posix_spawn` to
|
|
|
|
// successfully launch the program, but erroneously return
|
|
|
|
// ENOENT when used with posix_spawn_file_actions_addchdir_np
|
|
|
|
// which was introduced in macOS 10.15.
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
match posix_spawn_file_actions_addchdir_np.get() {
|
|
|
|
Some(f) => Some((f, cwd)),
|
|
|
|
None => return Ok(None),
|
|
|
|
}
|
|
|
|
}
|
2019-02-13 20:20:23 +00:00
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
|
2021-08-03 12:31:59 +00:00
|
|
|
// Safety: -1 indicates we don't have a pidfd.
|
|
|
|
let mut p = unsafe { Process::new(0, -1) };
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2020-10-08 00:00:00 +00:00
|
|
|
struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit<libc::posix_spawn_file_actions_t>);
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2020-10-08 00:00:00 +00:00
|
|
|
impl Drop for PosixSpawnFileActions<'_> {
|
2018-01-26 02:13:45 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2019-06-26 22:03:15 +00:00
|
|
|
libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-08 00:00:00 +00:00
|
|
|
struct PosixSpawnattr<'a>(&'a mut MaybeUninit<libc::posix_spawnattr_t>);
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2020-10-08 00:00:00 +00:00
|
|
|
impl Drop for PosixSpawnattr<'_> {
|
2018-01-26 02:13:45 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2019-06-26 22:03:15 +00:00
|
|
|
libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
2020-10-08 00:00:00 +00:00
|
|
|
let mut attrs = MaybeUninit::uninit();
|
|
|
|
cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?;
|
|
|
|
let attrs = PosixSpawnattr(&mut attrs);
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2020-10-08 00:00:00 +00:00
|
|
|
let mut file_actions = MaybeUninit::uninit();
|
|
|
|
cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?;
|
|
|
|
let file_actions = PosixSpawnFileActions(&mut file_actions);
|
2018-01-26 02:13:45 +00:00
|
|
|
|
|
|
|
if let Some(fd) = stdio.stdin.fd() {
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawn_file_actions_adddup2(
|
2019-12-22 22:42:04 +00:00
|
|
|
file_actions.0.as_mut_ptr(),
|
|
|
|
fd,
|
|
|
|
libc::STDIN_FILENO,
|
|
|
|
))?;
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
if let Some(fd) = stdio.stdout.fd() {
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawn_file_actions_adddup2(
|
2019-12-22 22:42:04 +00:00
|
|
|
file_actions.0.as_mut_ptr(),
|
|
|
|
fd,
|
|
|
|
libc::STDOUT_FILENO,
|
|
|
|
))?;
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
if let Some(fd) = stdio.stderr.fd() {
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawn_file_actions_adddup2(
|
2019-12-22 22:42:04 +00:00
|
|
|
file_actions.0.as_mut_ptr(),
|
|
|
|
fd,
|
|
|
|
libc::STDERR_FILENO,
|
|
|
|
))?;
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
2019-02-13 20:20:23 +00:00
|
|
|
if let Some((f, cwd)) = addchdir {
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
|
2019-02-13 20:20:23 +00:00
|
|
|
}
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2019-06-26 22:03:15 +00:00
|
|
|
let mut set = MaybeUninit::<libc::sigset_t>::uninit();
|
2019-08-19 23:14:07 +00:00
|
|
|
cvt(sigemptyset(set.as_mut_ptr()))?;
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?;
|
2019-08-19 23:14:07 +00:00
|
|
|
cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?;
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?;
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK;
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
|
2018-01-26 02:13:45 +00:00
|
|
|
|
2018-11-13 22:57:10 +00:00
|
|
|
// Make sure we synchronize access to the global `environ` resource
|
2021-03-14 18:10:34 +00:00
|
|
|
let _env_lock = sys::os::env_read_lock();
|
2019-12-22 22:42:04 +00:00
|
|
|
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
|
2020-10-08 00:00:00 +00:00
|
|
|
cvt_nz(libc::posix_spawnp(
|
2018-01-26 02:13:45 +00:00
|
|
|
&mut p.pid,
|
2020-09-21 18:32:06 +00:00
|
|
|
self.get_program_cstr().as_ptr(),
|
2019-06-26 22:03:15 +00:00
|
|
|
file_actions.0.as_ptr(),
|
|
|
|
attrs.0.as_ptr(),
|
2018-01-26 02:13:45 +00:00
|
|
|
self.get_argv().as_ptr() as *const _,
|
|
|
|
envp as *const _,
|
2020-10-08 00:00:00 +00:00
|
|
|
))?;
|
|
|
|
Ok(Some(p))
|
2018-01-26 02:13:45 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Processes
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-02-09 22:16:58 +00:00
|
|
|
/// The unique ID of the process (this should never be negative).
|
2016-11-23 21:58:13 +00:00
|
|
|
pub struct Process {
|
|
|
|
pid: pid_t,
|
|
|
|
status: Option<ExitStatus>,
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
// On Linux, stores the pidfd created for this child.
|
2021-02-06 13:15:49 +00:00
|
|
|
// This is None if the user did not request pidfd creation,
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
// or if the pidfd could not be created for some reason
|
|
|
|
// (e.g. the `clone3` syscall was not available).
|
|
|
|
#[cfg(target_os = "linux")]
|
2021-02-06 13:15:49 +00:00
|
|
|
pidfd: Option<PidFd>,
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Process {
|
2021-02-06 13:15:49 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-08-03 12:31:59 +00:00
|
|
|
unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self {
|
2021-08-04 12:58:06 +00:00
|
|
|
use crate::os::unix::io::FromRawFd;
|
2021-02-06 13:15:49 +00:00
|
|
|
use crate::sys_common::FromInner;
|
2021-08-03 12:31:59 +00:00
|
|
|
// Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned.
|
2021-08-24 08:07:16 +00:00
|
|
|
let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd)));
|
2021-02-06 13:15:49 +00:00
|
|
|
Process { pid, status: None, pidfd }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
2021-08-03 12:31:59 +00:00
|
|
|
unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self {
|
2021-02-06 13:15:49 +00:00
|
|
|
Process { pid, status: None }
|
|
|
|
}
|
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
pub fn id(&self) -> u32 {
|
|
|
|
self.pid as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn kill(&mut self) -> io::Result<()> {
|
|
|
|
// If we've already waited on this process then the pid can be recycled
|
|
|
|
// and used for another process, and we probably shouldn't be killing
|
|
|
|
// random processes, so just return an error.
|
|
|
|
if self.status.is_some() {
|
2021-08-08 08:04:33 +00:00
|
|
|
Err(io::const_io_error!(
|
2019-12-22 22:42:04 +00:00
|
|
|
ErrorKind::InvalidInput,
|
2021-08-08 08:04:33 +00:00
|
|
|
"invalid argument: can't kill an exited process",
|
2019-12-22 22:42:04 +00:00
|
|
|
))
|
2016-11-23 21:58:13 +00:00
|
|
|
} else {
|
2020-01-02 08:56:12 +00:00
|
|
|
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-06 06:47:09 +00:00
|
|
|
|
2016-11-23 21:58:13 +00:00
|
|
|
pub fn wait(&mut self) -> io::Result<ExitStatus> {
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::sys::cvt_r;
|
2016-11-23 21:58:13 +00:00
|
|
|
if let Some(status) = self.status {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Ok(status);
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
|
|
|
let mut status = 0 as c_int;
|
|
|
|
cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
|
|
|
|
self.status = Some(ExitStatus::new(status));
|
|
|
|
Ok(ExitStatus::new(status))
|
|
|
|
}
|
2017-01-06 06:47:09 +00:00
|
|
|
|
2017-02-03 22:39:41 +00:00
|
|
|
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
|
2017-01-06 06:47:09 +00:00
|
|
|
if let Some(status) = self.status {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Ok(Some(status));
|
2017-01-06 06:47:09 +00:00
|
|
|
}
|
|
|
|
let mut status = 0 as c_int;
|
2019-12-22 22:42:04 +00:00
|
|
|
let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
|
2017-01-06 06:47:09 +00:00
|
|
|
if pid == 0 {
|
2017-02-03 22:39:41 +00:00
|
|
|
Ok(None)
|
2017-01-06 06:47:09 +00:00
|
|
|
} else {
|
|
|
|
self.status = Some(ExitStatus::new(status));
|
2017-02-03 22:39:41 +00:00
|
|
|
Ok(Some(ExitStatus::new(status)))
|
2017-01-06 06:47:09 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-23 21:58:13 +00:00
|
|
|
}
|
2019-09-25 04:34:44 +00:00
|
|
|
|
|
|
|
/// Unix exit statuses
|
2021-11-08 17:38:01 +00:00
|
|
|
//
|
|
|
|
// This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status".
|
|
|
|
// See the discussion in comments and doc comments for `std::process::ExitStatus`.
|
2021-08-24 17:56:24 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
2019-09-25 04:34:44 +00:00
|
|
|
pub struct ExitStatus(c_int);
|
|
|
|
|
2021-08-24 17:56:24 +00:00
|
|
|
impl fmt::Debug for ExitStatus {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
f.debug_tuple("unix_wait_status").field(&self.0).finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 04:34:44 +00:00
|
|
|
impl ExitStatus {
|
|
|
|
pub fn new(status: c_int) -> ExitStatus {
|
|
|
|
ExitStatus(status)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exited(&self) -> bool {
|
2020-10-01 04:30:07 +00:00
|
|
|
libc::WIFEXITED(self.0)
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
|
|
|
|
2021-03-03 12:17:16 +00:00
|
|
|
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
|
|
|
|
// This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
|
2021-05-19 23:34:06 +00:00
|
|
|
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
|
2021-03-03 12:17:16 +00:00
|
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
|
|
|
|
// true for a platform pretending to be Unix, the tests (our doctests, and also
|
|
|
|
// procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
|
|
|
|
match NonZero_c_int::try_from(self.0) {
|
|
|
|
/* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
|
|
|
|
/* was zero, couldn't convert */ Err(_) => Ok(()),
|
|
|
|
}
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn code(&self) -> Option<i32> {
|
2020-10-01 04:30:07 +00:00
|
|
|
if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn signal(&self) -> Option<i32> {
|
unix ExitStatus: Do not treat WIFSTOPPED as WIFSIGNALED
A unix wait status can contain, at least, exit statuses, termination
signals, and stop signals.
WTERMSIG is only valid if WIFSIGNALED.
https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html
It will not be easy to experience this bug with `Command`, because
that doesn't pass WUNTRACED. But you could make an ExitStatus
containing, say, a WIFSTOPPED, from a call to one of the libc wait
functions.
(In the WIFSTOPPED case, there is WSTOPSIG. But a stop signal is
encoded differently to a termination signal, so WTERMSIG and WSTOPSIG
are by no means the same.)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
2020-12-12 21:31:54 +00:00
|
|
|
if libc::WIFSIGNALED(self.0) { Some(libc::WTERMSIG(self.0)) } else { None }
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
2020-12-12 21:41:55 +00:00
|
|
|
|
2020-12-12 21:44:13 +00:00
|
|
|
pub fn core_dumped(&self) -> bool {
|
|
|
|
libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
|
|
|
|
}
|
|
|
|
|
2020-12-12 21:47:47 +00:00
|
|
|
pub fn stopped_signal(&self) -> Option<i32> {
|
|
|
|
if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
|
|
|
|
}
|
|
|
|
|
2020-12-12 21:52:17 +00:00
|
|
|
pub fn continued(&self) -> bool {
|
|
|
|
libc::WIFCONTINUED(self.0)
|
|
|
|
}
|
|
|
|
|
2020-12-12 21:41:55 +00:00
|
|
|
pub fn into_raw(&self) -> c_int {
|
|
|
|
self.0
|
|
|
|
}
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 11:47:18 +00:00
|
|
|
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
|
2019-09-25 04:34:44 +00:00
|
|
|
impl From<c_int> for ExitStatus {
|
|
|
|
fn from(a: c_int) -> ExitStatus {
|
|
|
|
ExitStatus(a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ExitStatus {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
if let Some(code) = self.code() {
|
ExitStatus: print "exit status: {}" rather than "exit code: {}"
Proper Unix terminology is "exit status" (vs "wait status"). "exit
code" is imprecise on Unix and therefore unclear. (As far as I can
tell, "exit code" is correct terminology on Windows.)
This new wording is unfortunately inconsistent with the identifier
names in the Rust stdlib.
It is the identifier names that are wrong, as discussed at length in eg
https://doc.rust-lang.org/nightly/std/process/struct.ExitStatus.html
https://doc.rust-lang.org/nightly/std/os/unix/process/trait.ExitStatusExt.html
Unfortunately for API stability reasons it would be a lot of work, and
a lot of disruption, to change the names in the stdlib (eg to rename
`std::process::ExitStatus` to `std::process::ChildStatus` or
something), but we should fix the message output. Many (probably
most) readers of these messages about exit statuses will be users and
system administrators, not programmers, who won't even know that Rust
has this wrong terminology.
So I think the right thing is to fix the documentation (as I have
already done) and, now, the terminology in the implementation.
This is a user-visible change to the behaviour of all Rust programs
which run Unix subprocesses. Hopefully no-one is matching against the
exit status string, except perhaps in tests.
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
2021-03-25 10:27:53 +00:00
|
|
|
write!(f, "exit status: {}", code)
|
2021-02-22 14:30:03 +00:00
|
|
|
} else if let Some(signal) = self.signal() {
|
|
|
|
if self.core_dumped() {
|
|
|
|
write!(f, "signal: {} (core dumped)", signal)
|
|
|
|
} else {
|
|
|
|
write!(f, "signal: {}", signal)
|
|
|
|
}
|
|
|
|
} else if let Some(signal) = self.stopped_signal() {
|
|
|
|
write!(f, "stopped (not terminated) by signal: {}", signal)
|
|
|
|
} else if self.continued() {
|
|
|
|
write!(f, "continued (WIFCONTINUED)")
|
2019-09-25 04:34:44 +00:00
|
|
|
} else {
|
2021-02-22 14:30:03 +00:00
|
|
|
write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
|
2019-09-25 04:34:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 14:58:52 +00:00
|
|
|
|
2021-08-24 17:56:24 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
2021-03-03 12:17:16 +00:00
|
|
|
pub struct ExitStatusError(NonZero_c_int);
|
|
|
|
|
|
|
|
impl Into<ExitStatus> for ExitStatusError {
|
|
|
|
fn into(self) -> ExitStatus {
|
|
|
|
ExitStatus(self.0.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:56:24 +00:00
|
|
|
impl fmt::Debug for ExitStatusError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
f.debug_tuple("unix_wait_status").field(&self.0).finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-03 12:17:16 +00:00
|
|
|
impl ExitStatusError {
|
|
|
|
pub fn code(self) -> Option<NonZeroI32> {
|
|
|
|
ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2021-03-10 10:21:01 +00:00
|
|
|
#[unstable(feature = "linux_pidfd", issue = "82971")]
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
impl crate::os::linux::process::ChildExt for crate::process::Child {
|
2021-02-06 13:15:49 +00:00
|
|
|
fn pidfd(&self) -> io::Result<&PidFd> {
|
|
|
|
self.handle
|
|
|
|
.pidfd
|
|
|
|
.as_ref()
|
|
|
|
.ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn take_pidfd(&mut self) -> io::Result<PidFd> {
|
|
|
|
self.handle
|
|
|
|
.pidfd
|
|
|
|
.take()
|
|
|
|
.ok_or_else(|| Error::new(ErrorKind::Other, "No pidfd was created."))
|
Add Linux-specific pidfd process extensions
Background:
Over the last year, pidfd support was added to the Linux kernel. This
allows interacting with other processes. In particular, this allows
waiting on a child process with a timeout in a race-free way, bypassing
all of the awful signal-handler tricks that are usually required.
Pidfds can be obtained for a child process (as well as any other
process) via the `pidfd_open` syscall. Unfortunately, this requires
several conditions to hold in order to be race-free (i.e. the pid is not
reused).
Per `man pidfd_open`:
```
· the disposition of SIGCHLD has not been explicitly set to SIG_IGN
(see sigaction(2));
· the SA_NOCLDWAIT flag was not specified while establishing a han‐
dler for SIGCHLD or while setting the disposition of that signal to
SIG_DFL (see sigaction(2)); and
· the zombie process was not reaped elsewhere in the program (e.g.,
either by an asynchronously executed signal handler or by wait(2)
or similar in another thread).
If any of these conditions does not hold, then the child process
(along with a PID file descriptor that refers to it) should instead
be created using clone(2) with the CLONE_PIDFD flag.
```
Sadly, these conditions are impossible to guarantee once any libraries
are used. For example, C code runnng in a different thread could call
`wait()`, which is impossible to detect from Rust code trying to open a
pidfd.
While pid reuse issues should (hopefully) be rare in practice, we can do
better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
can obtain a pidfd for the child process in a guaranteed race-free
manner.
This PR:
This PR adds Linux-specific process extension methods to allow obtaining
pidfds for processes spawned via the standard `Command` API. Other than
being made available to user code, the standard library does not make
use of these pidfds in any way. In particular, the implementation of
`Child::wait` is completely unchanged.
Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
and `ChildExt::pidfd`. These methods are intended to serve as a building
block for libraries to build higher-level abstractions - in particular,
waiting on a process with a timeout.
I've included a basic test, which verifies that pidfds are created iff
the `create_pidfd` method is used. This test is somewhat special - it
should always succeed on systems with the `clone3` system call
available, and always fail on systems without `clone3` available. I'm
not sure how to best ensure this programatically.
This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
rather than the older `clone` system call. `clone3` was added to Linux
in the same release as pidfds, so this shouldn't unnecessarily limit the
kernel versions that this code supports.
Unresolved questions:
* What should the name of the feature gate be for these newly added
methods?
* Should the `pidfd` method distinguish between an error occurring
and `create_pidfd` not being called?
2020-09-16 03:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 14:58:52 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
#[path = "process_unix/tests.rs"]
|
|
|
|
mod tests;
|