diff options
| author | soryu <soryu@soryu.co> | 2026-01-26 22:41:18 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-26 22:41:30 +0000 |
| commit | de1eb0a923f6c5b768ac49f0425b7213a89301b7 (patch) | |
| tree | 410e886a638f91bc9d0e9bc7b6a6f1beead4e818 /makima/src/daemon/process/claude.rs | |
| parent | d1f5dadb549d499c5aeee9cacf6c9aa0a233c198 (diff) | |
| download | soryu-de1eb0a923f6c5b768ac49f0425b7213a89301b7.tar.gz soryu-de1eb0a923f6c5b768ac49f0425b7213a89301b7.zip | |
Terminate all processes on makima CLI SIGTERM
Diffstat (limited to 'makima/src/daemon/process/claude.rs')
| -rw-r--r-- | makima/src/daemon/process/claude.rs | 78 |
1 files changed, 47 insertions, 31 deletions
diff --git a/makima/src/daemon/process/claude.rs b/makima/src/daemon/process/claude.rs index f3aa421..c8add1c 100644 --- a/makima/src/daemon/process/claude.rs +++ b/makima/src/daemon/process/claude.rs @@ -117,19 +117,21 @@ impl ClaudeProcess { self.child.id() } - /// Send SIGTERM to gracefully terminate the process (Unix only). + /// Send SIGTERM to gracefully terminate the process and all its children (Unix only). /// Returns Ok(true) if signal was sent, Ok(false) if process already exited. + /// Sends signal to the entire process group (negative PID) to kill all children. #[cfg(unix)] pub fn terminate(&self) -> Result<bool, ClaudeProcessError> { - use nix::sys::signal::{kill, Signal}; + use nix::sys::signal::{killpg, Signal}; use nix::unistd::Pid; if let Some(pid) = self.child.id() { - match kill(Pid::from_raw(pid as i32), Signal::SIGTERM) { + // Kill the entire process group (the process is its own group leader) + match killpg(Pid::from_raw(pid as i32), Signal::SIGTERM) { Ok(()) => Ok(true), - Err(nix::errno::Errno::ESRCH) => Ok(false), // Process doesn't exist + Err(nix::errno::Errno::ESRCH) => Ok(false), // Process group doesn't exist Err(e) => Err(ClaudeProcessError::OutputRead(format!( - "Failed to send SIGTERM: {}", + "Failed to send SIGTERM to process group: {}", e ))), } @@ -552,27 +554,34 @@ impl ProcessManager { (self.claude_command.clone(), claude_args) }; - // Spawn the process - let mut child = Command::new(&command) - .args(&args) + // Spawn the process in its own process group so we can kill all children + let mut cmd = Command::new(&command); + cmd.args(&args) .current_dir(working_dir) .envs(env) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .map_err(|e| { - if e.kind() == std::io::ErrorKind::NotFound { - if use_bubblewrap { - ClaudeProcessError::BubblewrapNotFound - } else { - ClaudeProcessError::CommandNotFound(self.claude_command.clone()) - } + .kill_on_drop(true); + + // On Unix, create a new process group so we can kill all child processes + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + cmd.process_group(0); + } + + let mut child = cmd.spawn().map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + if use_bubblewrap { + ClaudeProcessError::BubblewrapNotFound } else { - ClaudeProcessError::SpawnFailed(e) + ClaudeProcessError::CommandNotFound(self.claude_command.clone()) } - })?; + } else { + ClaudeProcessError::SpawnFailed(e) + } + })?; // Create output channel let (tx, rx) = mpsc::channel(1000); @@ -730,23 +739,30 @@ impl ProcessManager { tracing::debug!(args = ?args, "Claude continue command arguments"); - // Spawn the process - let mut child = Command::new(&self.claude_command) - .args(&args) + // Spawn the process in its own process group so we can kill all children + let mut cmd = Command::new(&self.claude_command); + cmd.args(&args) .current_dir(working_dir) .envs(env) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .map_err(|e| { - if e.kind() == std::io::ErrorKind::NotFound { - ClaudeProcessError::CommandNotFound(self.claude_command.clone()) - } else { - ClaudeProcessError::SpawnFailed(e) - } - })?; + .kill_on_drop(true); + + // On Unix, create a new process group so we can kill all child processes + #[cfg(unix)] + { + use std::os::unix::process::CommandExt; + cmd.process_group(0); + } + + let mut child = cmd.spawn().map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + ClaudeProcessError::CommandNotFound(self.claude_command.clone()) + } else { + ClaudeProcessError::SpawnFailed(e) + } + })?; // Create output channel let (tx, rx) = mpsc::channel(1000); |
