rskit-process 0.2.0-alpha.4

Process and subprocess execution with timeout and signal handling
Documentation
//! Child-side controlling-terminal setup, run after fork and before exec.

use std::io;

use tokio::process::Command as TokioCommand;

/// Install the `pre_exec` hook that makes the slave PTY the child's controlling
/// terminal.
///
/// By the time the hook runs, the spawn machinery has already dup'd the slave
/// onto the child's fds 0/1/2. The hook then:
/// 1. `setsid()` — start a new session so the child is a session leader with no
///    controlling terminal (a prerequisite for acquiring one), which also makes
///    it a process-group leader whose group id equals its pid.
/// 2. `ioctl(0, TIOCSCTTY)` — acquire the slave (now fd 0) as the controlling
///    terminal, so the child and its descendants see a real interactive tty.
///
/// Because `setsid` establishes the process group, this hook intentionally
/// replaces the plain `setpgid` isolation used for pipe-backed modes; the
/// resulting group id still equals the child pid. `setsid` is mandatory for
/// acquiring a controlling terminal, so PTY mode always starts a new session
/// and process group: it requires [`SignalPolicy::create_process_group`] to be
/// enabled and rejects the run otherwise (see `run_pty_mode`). Given that, the
/// group layout matches the pipe-backed path, so group-targeted termination
/// under [`SignalPolicy`] behaves identically to the non-PTY modes.
///
/// [`SignalPolicy`]: crate::SignalPolicy
/// [`SignalPolicy::create_process_group`]: crate::SignalPolicy::create_process_group
pub(crate) fn install_controlling_tty(cmd: &mut TokioCommand) {
    // SAFETY: the closure runs in the forked child before `exec`. It calls only
    // async-signal-safe syscalls (`setsid`, `ioctl`) and returns an `io::Error`
    // on failure, which aborts the spawn — the supported `pre_exec` contract.
    unsafe {
        cmd.pre_exec(|| {
            if libc::setsid() < 0 {
                return Err(io::Error::last_os_error());
            }
            // The controlling-tty ioctl takes an int arg on every supported
            // platform; `0` means "do not steal from another session".
            if libc::ioctl(libc::STDIN_FILENO, libc::TIOCSCTTY as _, 0) < 0 {
                return Err(io::Error::last_os_error());
            }
            Ok(())
        });
    }
}