fix(tmux): use KillSessionWithProcesses to prevent zombie bash processes
When Claude sessions were terminated using KillSession(), bash subprocesses spawned by Claude's Bash tool could survive because they ignore SIGHUP. This caused zombie processes to accumulate over time. Changed all critical session termination paths to use KillSessionWithProcesses() which explicitly kills all descendant processes before terminating the session. Fixes: gt-ew3tk Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -301,9 +301,10 @@ func runDegradedTriage(b *boot.Boot) (action, target string, err error) {
|
||||
// Nudge the session to try to wake it up
|
||||
age := hb.Age()
|
||||
if age > 30*time.Minute {
|
||||
// Very stuck - restart the session
|
||||
// Very stuck - restart the session.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
fmt.Printf("Deacon heartbeat is %s old - restarting session\n", age.Round(time.Minute))
|
||||
if err := tm.KillSession(deaconSession); err == nil {
|
||||
if err := tm.KillSessionWithProcesses(deaconSession); err == nil {
|
||||
return "restart", "deacon-stuck", nil
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -28,11 +28,12 @@ func runCrewRename(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Kill any running session for the old name
|
||||
// Kill any running session for the old name.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
t := tmux.NewTmux()
|
||||
oldSessionID := crewSessionName(r.Name, oldName)
|
||||
if hasSession, _ := t.HasSession(oldSessionID); hasSession {
|
||||
if err := t.KillSession(oldSessionID); err != nil {
|
||||
if err := t.KillSessionWithProcesses(oldSessionID); err != nil {
|
||||
return fmt.Errorf("killing old session: %w", err)
|
||||
}
|
||||
fmt.Printf("Killed session %s\n", oldSessionID)
|
||||
|
||||
@@ -491,8 +491,9 @@ func runDeaconStop(cmd *cobra.Command, args []string) error {
|
||||
_ = t.SendKeysRaw(sessionName, "C-c")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Kill the session
|
||||
if err := t.KillSession(sessionName); err != nil {
|
||||
// Kill the session.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
if err := t.KillSessionWithProcesses(sessionName); err != nil {
|
||||
return fmt.Errorf("killing session: %w", err)
|
||||
}
|
||||
|
||||
@@ -592,8 +593,9 @@ func runDeaconRestart(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("Restarting Deacon...")
|
||||
|
||||
if running {
|
||||
// Kill existing session
|
||||
if err := t.KillSession(sessionName); err != nil {
|
||||
// Kill existing session.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
if err := t.KillSessionWithProcesses(sessionName); err != nil {
|
||||
style.PrintWarning("failed to kill session: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -876,9 +878,10 @@ func runDeaconForceKill(cmd *cobra.Command, args []string) error {
|
||||
mailBody := fmt.Sprintf("Deacon detected %s as unresponsive.\nReason: %s\nAction: force-killing session", agent, reason)
|
||||
sendMail(townRoot, agent, "FORCE_KILL: unresponsive", mailBody)
|
||||
|
||||
// Step 2: Kill the tmux session
|
||||
// Step 2: Kill the tmux session.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
fmt.Printf("%s Killing tmux session %s...\n", style.Dim.Render("2."), sessionName)
|
||||
if err := t.KillSession(sessionName); err != nil {
|
||||
if err := t.KillSessionWithProcesses(sessionName); err != nil {
|
||||
return fmt.Errorf("killing session: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -192,12 +192,13 @@ func runWitnessStop(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Kill tmux session if it exists
|
||||
// Kill tmux session if it exists.
|
||||
// Use KillSessionWithProcesses to ensure all descendant processes are killed.
|
||||
t := tmux.NewTmux()
|
||||
sessionName := witnessSessionName(rigName)
|
||||
running, _ := t.HasSession(sessionName)
|
||||
if running {
|
||||
if err := t.KillSession(sessionName); err != nil {
|
||||
if err := t.KillSessionWithProcesses(sessionName); err != nil {
|
||||
style.PrintWarning("failed to kill session: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user