summaryrefslogtreecommitdiff
path: root/makima/src/daemon/process/claude.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-26 22:41:18 +0000
committersoryu <soryu@soryu.co>2026-01-26 22:41:30 +0000
commitde1eb0a923f6c5b768ac49f0425b7213a89301b7 (patch)
tree410e886a638f91bc9d0e9bc7b6a6f1beead4e818 /makima/src/daemon/process/claude.rs
parentd1f5dadb549d499c5aeee9cacf6c9aa0a233c198 (diff)
downloadsoryu-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.rs78
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);