From de1eb0a923f6c5b768ac49f0425b7213a89301b7 Mon Sep 17 00:00:00 2001 From: soryu Date: Mon, 26 Jan 2026 22:41:18 +0000 Subject: Terminate all processes on makima CLI SIGTERM --- makima/src/daemon/task/manager.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'makima/src/daemon/task') diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index bbcf661..b162f33 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -1412,13 +1412,14 @@ impl TaskManager { Ok(concurrency_key) } - /// Gracefully shutdown all running Claude processes. + /// Gracefully shutdown all running Claude processes and their children. /// - /// This sends SIGTERM to all active processes, waits for them to exit gracefully, + /// This sends SIGTERM to all active process groups, waits for them to exit gracefully, /// and then sends SIGKILL to any that don't exit within the timeout. + /// Uses process groups to ensure all child processes (bash commands, etc.) are also killed. #[cfg(unix)] pub async fn shutdown_all_processes(&self, timeout: std::time::Duration) { - use nix::sys::signal::{kill, Signal}; + use nix::sys::signal::{killpg, Signal}; use nix::unistd::Pid; let pids: Vec<(Uuid, u32)> = { @@ -1431,19 +1432,19 @@ impl TaskManager { return; } - tracing::info!(count = pids.len(), "Sending SIGTERM to all Claude processes"); + tracing::info!(count = pids.len(), "Sending SIGTERM to all Claude process groups"); - // Send SIGTERM to all processes + // Send SIGTERM to all process groups (each Claude process is its own group leader) for (task_id, pid) in &pids { - match kill(Pid::from_raw(*pid as i32), Signal::SIGTERM) { + match killpg(Pid::from_raw(*pid as i32), Signal::SIGTERM) { Ok(()) => { - tracing::debug!(task_id = %task_id, pid = pid, "Sent SIGTERM to process"); + tracing::debug!(task_id = %task_id, pid = pid, "Sent SIGTERM to process group"); } Err(nix::errno::Errno::ESRCH) => { - tracing::debug!(task_id = %task_id, pid = pid, "Process already exited"); + tracing::debug!(task_id = %task_id, pid = pid, "Process group already exited"); } Err(e) => { - tracing::warn!(task_id = %task_id, pid = pid, error = %e, "Failed to send SIGTERM"); + tracing::warn!(task_id = %task_id, pid = pid, error = %e, "Failed to send SIGTERM to process group"); } } } @@ -1466,7 +1467,7 @@ impl TaskManager { tokio::time::sleep(check_interval).await; } - // Send SIGKILL to any remaining processes + // Send SIGKILL to any remaining process groups let remaining: Vec<(Uuid, u32)> = { let guard = self.active_pids.read().await; guard.iter().map(|(k, v)| (*k, *v)).collect() @@ -1475,15 +1476,15 @@ impl TaskManager { if !remaining.is_empty() { tracing::warn!( count = remaining.len(), - "Some processes did not exit gracefully, sending SIGKILL" + "Some process groups did not exit gracefully, sending SIGKILL" ); for (task_id, pid) in &remaining { - match kill(Pid::from_raw(*pid as i32), Signal::SIGKILL) { + match killpg(Pid::from_raw(*pid as i32), Signal::SIGKILL) { Ok(()) => { - tracing::debug!(task_id = %task_id, pid = pid, "Sent SIGKILL to process"); + tracing::debug!(task_id = %task_id, pid = pid, "Sent SIGKILL to process group"); } Err(e) => { - tracing::warn!(task_id = %task_id, pid = pid, error = %e, "Failed to send SIGKILL"); + tracing::warn!(task_id = %task_id, pid = pid, error = %e, "Failed to send SIGKILL to process group"); } } } -- cgit v1.2.3