summaryrefslogtreecommitdiff
path: root/makima/src/daemon/task
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/task
parentd1f5dadb549d499c5aeee9cacf6c9aa0a233c198 (diff)
downloadsoryu-de1eb0a923f6c5b768ac49f0425b7213a89301b7.tar.gz
soryu-de1eb0a923f6c5b768ac49f0425b7213a89301b7.zip
Terminate all processes on makima CLI SIGTERM
Diffstat (limited to 'makima/src/daemon/task')
-rw-r--r--makima/src/daemon/task/manager.rs29
1 files changed, 15 insertions, 14 deletions
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");
}
}
}