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};
use super::proc_check::{is_claude_or_node_name, ProcCheck};
fn claude_bin() -> OsString {
std::env::var_os("CLAUDE_SMART_CLAUDE_BIN").unwrap_or_else(|| OsString::from("claude"))
}
#[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 mut cmd = Command::new(claude_bin());
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),
}
}
}
pub struct PosixProcCheck;
impl ProcCheck for PosixProcCheck {
fn is_live_claude_or_node(pid: u32) -> bool {
use std::process::Command;
let out = Command::new("ps")
.args(["-o", "comm=", "-p", &pid.to_string()])
.output();
match out {
Ok(o) if o.status.success() => {
let comm = String::from_utf8_lossy(&o.stdout);
let basename = comm.trim();
let name = std::path::Path::new(basename)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(basename);
is_claude_or_node_name(name)
}
_ => false,
}
}
}