Skip to main content
Glama
platform_unix.go5.15 kB
//go:build !windows package process import ( "context" "os" "os/exec" "strconv" "strings" "syscall" "time" ) // setupProcessGroup sets up process group for Unix systems func setupProcessGroup(cmd *exec.Cmd) { cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } // killProcessTree kills a process and all its children on Unix func killProcessTree(pid int) { // First, try to kill all child processes recursively killAllChildren(pid) // Kill the entire process group with SIGTERM first _ = syscall.Kill(-pid, syscall.SIGTERM) // Ignore errors during cleanup // Give processes more time to cleanup gracefully, especially for npm/pnpm time.Sleep(200 * time.Millisecond) // Try killing individual process in case process group didn't work if proc, err := os.FindProcess(pid); err == nil { _ = proc.Signal(syscall.SIGTERM) } // Give a bit more time for graceful shutdown time.Sleep(100 * time.Millisecond) // Now be more aggressive - kill the process group with SIGKILL _ = syscall.Kill(-pid, syscall.SIGKILL) // Ignore errors during cleanup // Also directly kill the main process if proc, err := os.FindProcess(pid); err == nil { _ = proc.Kill() } // Final cleanup: kill any remaining children with SIGKILL (non-blocking) go func() { time.Sleep(50 * time.Millisecond) killAllChildrenForce(pid) verifyProcessDead(pid) }() } // killAllChildren recursively finds and kills all child processes func killAllChildren(parentPID int) { // Use pgrep to find all children of this process ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "pgrep", "-P", strconv.Itoa(parentPID)) output, err := cmd.Output() if err != nil { return } lines := strings.TrimSpace(string(output)) if lines == "" { return } var childPIDs []int for _, line := range strings.Split(lines, "\n") { if childPID, err := strconv.Atoi(strings.TrimSpace(line)); err == nil { childPIDs = append(childPIDs, childPID) } } // First, recursively kill grandchildren for _, childPID := range childPIDs { killAllChildren(childPID) } // Then kill direct children for _, childPID := range childPIDs { // Try SIGTERM first if proc, err := os.FindProcess(childPID); err == nil { _ = proc.Signal(syscall.SIGTERM) } } // Give children time to exit gracefully time.Sleep(50 * time.Millisecond) // Kill any remaining children with SIGKILL for _, childPID := range childPIDs { if proc, err := os.FindProcess(childPID); err == nil { _ = proc.Kill() } } } // killAllChildrenForce kills all child processes with SIGKILL func killAllChildrenForce(parentPID int) { ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "pgrep", "-P", strconv.Itoa(parentPID)) output, err := cmd.Output() if err != nil { return } lines := strings.TrimSpace(string(output)) if lines == "" { return } for _, line := range strings.Split(lines, "\n") { if childPID, err := strconv.Atoi(strings.TrimSpace(line)); err == nil { // Recursively kill grandchildren first killAllChildrenForce(childPID) // Then force kill this child if proc, err := os.FindProcess(childPID); err == nil { _ = proc.Kill() } } } } // verifyProcessDead checks if a process is actually dead and force kills if needed func verifyProcessDead(pid int) { time.Sleep(100 * time.Millisecond) if proc, err := os.FindProcess(pid); err == nil { // Check if process still exists by sending signal 0 if err := proc.Signal(syscall.Signal(0)); err == nil { // Process still exists, force kill it one more time _ = proc.Kill() // Also try to kill the process group _ = syscall.Kill(-pid, syscall.SIGKILL) // Give it one final moment time.Sleep(50 * time.Millisecond) } } } // killProcessByPID kills a single process on Unix func killProcessByPID(pid int) { if proc, err := os.FindProcess(pid); err == nil { // First kill any children of this process killAllChildren(pid) // Try graceful termination first _ = proc.Signal(syscall.SIGTERM) // Ignore errors during cleanup time.Sleep(50 * time.Millisecond) // Check if process still exists if err := proc.Signal(syscall.Signal(0)); err == nil { // Process still exists, force kill it _ = proc.Kill() // Ignore errors during cleanup } } } // ensureProcessDead makes sure a process is really dead on Unix func ensureProcessDead(pid int) { if proc, err := os.FindProcess(pid); err == nil { // Try to send signal 0 to check if process exists if err := proc.Signal(syscall.Signal(0)); err == nil { // Process still exists, kill it _ = proc.Kill() // Ignore errors during forced cleanup } } } // findProcessUsingPort finds the process ID using a specific port on Unix (stub) func findProcessUsingPort(port int) (int, error) { // This is handled differently in the manager for Unix return 0, nil } // killProcessesByName kills all processes matching a name pattern on Unix (stub) func killProcessesByName(pattern string) { // This is handled differently in the manager for Unix }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server