Description
The Unix implementation of process::spawn
(specifically, do_exec
in sys/unix/process/process_unix.rs
) contains logic to restore the signal mask to the default in child processes...
// 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.
let mut set: libc::sigset_t = mem::uninitialized();
t!(cvt(libc::sigemptyset(&mut set)));
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
ptr::null_mut())));
let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
if ret == libc::SIG_ERR {
return io::Error::last_os_error()
}
Completely clearing the signal mask here is subtly wrong. It should instead be restored to whatever it was when the parent process started up. Certain standard shell utilities — the best-known is nohup
— deliberately start a process with some signals already masked, and expect that those settings will be inherited by any further subprocesses.
For the same reason, the handler for SIGPIPE
should be reset not to SIG_DFL
, but to whichever of SIG_DFL
or SIG_IGN
it was before libstd ignored it.
(In order to make it possible to implement such utilities in Rust, the ideal resolution here would involve adding mask_signals
and ignore_signals
knobs to unix::process::CommandExt
, and document that the defaults for these are whatever was observed on process startup. But I don't see a clean way to do that without a lot more support for signals in general in libstd, maybe more than is properly in scope.)