PR #759 introduced cleanupOrphanedClaude() using syscall.Kill directly, which breaks Windows builds. This extracts the function to: - start_orphan_unix.go: Full implementation with SIGTERM/SIGKILL - start_orphan_windows.go: Stub (orphan signals not supported) Follows existing pattern: process_unix.go / process_windows.go
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
//go:build !windows
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/steveyegge/gastown/internal/style"
|
|
"github.com/steveyegge/gastown/internal/util"
|
|
)
|
|
|
|
// cleanupOrphanedClaude finds and kills orphaned Claude processes with a grace period.
|
|
// This is a simpler synchronous implementation that:
|
|
// 1. Finds orphaned processes (TTY-less, older than 60s, not in Gas Town sessions)
|
|
// 2. Sends SIGTERM to all of them
|
|
// 3. Waits for the grace period
|
|
// 4. Sends SIGKILL to any that are still alive
|
|
func cleanupOrphanedClaude(graceSecs int) {
|
|
// Find orphaned processes
|
|
orphans, err := util.FindOrphanedClaudeProcesses()
|
|
if err != nil {
|
|
fmt.Printf(" %s Warning: %v\n", style.Bold.Render("⚠"), err)
|
|
return
|
|
}
|
|
|
|
if len(orphans) == 0 {
|
|
fmt.Printf(" %s No orphaned processes found\n", style.Dim.Render("○"))
|
|
return
|
|
}
|
|
|
|
// Send SIGTERM to all orphans
|
|
var termPIDs []int
|
|
for _, orphan := range orphans {
|
|
if err := syscall.Kill(orphan.PID, syscall.SIGTERM); err != nil {
|
|
if err != syscall.ESRCH {
|
|
fmt.Printf(" %s PID %d: failed to send SIGTERM: %v\n",
|
|
style.Bold.Render("⚠"), orphan.PID, err)
|
|
}
|
|
continue
|
|
}
|
|
termPIDs = append(termPIDs, orphan.PID)
|
|
fmt.Printf(" %s PID %d: sent SIGTERM (waiting %ds before SIGKILL)\n",
|
|
style.Bold.Render("→"), orphan.PID, graceSecs)
|
|
}
|
|
|
|
if len(termPIDs) == 0 {
|
|
return
|
|
}
|
|
|
|
// Wait for grace period
|
|
fmt.Printf(" %s Waiting %d seconds for processes to terminate gracefully...\n",
|
|
style.Dim.Render("⏳"), graceSecs)
|
|
time.Sleep(time.Duration(graceSecs) * time.Second)
|
|
|
|
// Check which processes are still alive and send SIGKILL
|
|
var killedCount, alreadyDeadCount int
|
|
for _, pid := range termPIDs {
|
|
// Check if process still exists
|
|
if err := syscall.Kill(pid, 0); err != nil {
|
|
// Process is gone (either died from SIGTERM or doesn't exist)
|
|
alreadyDeadCount++
|
|
continue
|
|
}
|
|
|
|
// Process still alive - send SIGKILL
|
|
if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
|
|
if err != syscall.ESRCH {
|
|
fmt.Printf(" %s PID %d: failed to send SIGKILL: %v\n",
|
|
style.Bold.Render("⚠"), pid, err)
|
|
}
|
|
continue
|
|
}
|
|
killedCount++
|
|
fmt.Printf(" %s PID %d: sent SIGKILL (did not respond to SIGTERM)\n",
|
|
style.Bold.Render("✓"), pid)
|
|
}
|
|
|
|
if alreadyDeadCount > 0 {
|
|
fmt.Printf(" %s %d process(es) terminated gracefully from SIGTERM\n",
|
|
style.Bold.Render("✓"), alreadyDeadCount)
|
|
}
|
|
if killedCount == 0 && alreadyDeadCount > 0 {
|
|
fmt.Printf(" %s All processes cleaned up successfully\n",
|
|
style.Bold.Render("✓"))
|
|
}
|
|
}
|