use std::collections::HashMap;
use std::ffi::OsString;
use std::io;
use std::os::unix::process::CommandExt; use std::process::{Command, ExitStatus};
use std::time::{SystemTime, UNIX_EPOCH};
use super::launcher::{ChildHandle, Launcher};
#[derive(Default)]
pub struct PosixLauncher;
impl Launcher for PosixLauncher {
fn run_foreground(
&self,
sid: &str,
cli: &[OsString],
env: &HashMap<OsString, OsString>,
) -> io::Result<(ExitStatus, ChildHandle)> {
use std::os::unix::io::AsFd;
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};
use nix::unistd::{setpgid, tcgetpgrp, tcsetpgrp, Pid};
let born = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0);
let tty = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/tty")
.ok();
let launch = crate::config::resolve_launch_command();
let (bin, prefix) = launch.split_first().expect("resolver returns ≥1 token");
let mut cmd = Command::new(bin);
cmd.args(prefix);
cmd.args(cli);
for (k, v) in env {
cmd.env(k, v);
}
unsafe {
cmd.pre_exec(|| {
let _ = setpgid(Pid::from_raw(0), Pid::from_raw(0));
Ok(())
});
}
let child = cmd.spawn()?;
let pid = child.id();
let child_pgid = Pid::from_raw(pid as i32);
let _ = crate::platform::pid::write_pid_file(&crate::paths::pid_file(sid), pid, born);
let _ = setpgid(child_pgid, child_pgid);
let saved_ttou = if tty.is_some() {
unsafe {
let ign = SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty());
sigaction(Signal::SIGTTOU, &ign).ok()
}
} else {
None
};
let parent_pgid = tty.as_ref().and_then(|t| {
let fd = t.as_fd();
let prev = tcgetpgrp(fd).ok();
let _ = tcsetpgrp(fd, child_pgid);
prev
});
let status = wait_for(child)?;
if let (Some(t), Some(prev)) = (tty.as_ref(), parent_pgid) {
let _ = tcsetpgrp(t.as_fd(), prev);
}
if let Some(prev) = saved_ttou {
unsafe {
let _ = sigaction(Signal::SIGTTOU, &prev);
}
}
Ok((status, ChildHandle { pid, born }))
}
}
fn wait_for(mut child: std::process::Child) -> io::Result<ExitStatus> {
loop {
match child.wait() {
Ok(status) => return Ok(status),
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
}
}
}