Skip to content

Commit a6dc44a

Browse files
authored
Merge pull request #16 from pmarsceill/feat/map-clean-command
feat(cli): add map clean command for orphaned resource cleanup
2 parents 44f1809 + d6ac2cb commit a6dc44a

2 files changed

Lines changed: 126 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ This creates two binaries in `bin/`:
9797
|---------|-------------|
9898
| `map up [-f]` | Start the daemon (foreground with -f) |
9999
| `map down [-f]` | Stop the daemon (force immediate shutdown with -f) |
100+
| `map clean` | Clean up orphaned processes, tmux sessions, and socket files |
100101
| `map watch` | Stream real-time events from the daemon |
101102

102103
### Agent Management

internal/cli/clean.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/pmarsceill/mapcli/internal/daemon"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
var cleanCmd = &cobra.Command{
15+
Use: "clean",
16+
Short: "Clean up orphaned processes and resources",
17+
Long: `Clean up orphaned mapd processes, tmux sessions, and socket files.
18+
19+
This is useful when the daemon didn't shut down cleanly and left behind
20+
stale processes or socket files that prevent starting a new daemon.`,
21+
RunE: runClean,
22+
}
23+
24+
func init() {
25+
rootCmd.AddCommand(cleanCmd)
26+
}
27+
28+
func runClean(cmd *cobra.Command, args []string) error {
29+
var cleaned bool
30+
31+
// 1. Kill orphaned mapd/map processes
32+
killedProcs, err := killOrphanedProcesses()
33+
if err != nil {
34+
fmt.Printf("warning: error killing processes: %v\n", err)
35+
}
36+
if killedProcs > 0 {
37+
fmt.Printf("killed %d orphaned process(es)\n", killedProcs)
38+
cleaned = true
39+
}
40+
41+
// 2. Kill orphaned tmux sessions
42+
killedSessions, err := killOrphanedTmuxSessions()
43+
if err != nil {
44+
fmt.Printf("warning: error killing tmux sessions: %v\n", err)
45+
}
46+
if killedSessions > 0 {
47+
fmt.Printf("killed %d orphaned tmux session(s)\n", killedSessions)
48+
cleaned = true
49+
}
50+
51+
// 3. Remove socket file if it exists
52+
if _, err := os.Stat(socketPath); err == nil {
53+
if err := os.Remove(socketPath); err != nil {
54+
fmt.Printf("warning: failed to remove socket %s: %v\n", socketPath, err)
55+
} else {
56+
fmt.Printf("removed socket %s\n", socketPath)
57+
cleaned = true
58+
}
59+
}
60+
61+
if !cleaned {
62+
fmt.Println("nothing to clean")
63+
}
64+
65+
return nil
66+
}
67+
68+
// killOrphanedProcesses finds and kills mapd and map processes
69+
func killOrphanedProcesses() (int, error) {
70+
// Get current process ID to avoid killing ourselves
71+
currentPID := os.Getpid()
72+
73+
// Find mapd and map processes using pgrep
74+
output, err := exec.Command("pgrep", "-f", "mapd|map up").Output()
75+
if err != nil {
76+
// pgrep returns exit code 1 when no processes found
77+
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 {
78+
return 0, nil
79+
}
80+
return 0, err
81+
}
82+
83+
var killed int
84+
for line := range strings.SplitSeq(strings.TrimSpace(string(output)), "\n") {
85+
if line == "" {
86+
continue
87+
}
88+
pid, err := strconv.Atoi(line)
89+
if err != nil {
90+
continue
91+
}
92+
// Don't kill ourselves
93+
if pid == currentPID {
94+
continue
95+
}
96+
// Kill the process
97+
proc, err := os.FindProcess(pid)
98+
if err != nil {
99+
continue
100+
}
101+
if err := proc.Kill(); err == nil {
102+
killed++
103+
}
104+
}
105+
106+
return killed, nil
107+
}
108+
109+
// killOrphanedTmuxSessions kills map-agent-* tmux sessions
110+
func killOrphanedTmuxSessions() (int, error) {
111+
sessions, err := daemon.ListTmuxSessions()
112+
if err != nil {
113+
return 0, err
114+
}
115+
116+
var killed int
117+
for _, session := range sessions {
118+
cmd := exec.Command("tmux", "kill-session", "-t", session)
119+
if err := cmd.Run(); err == nil {
120+
killed++
121+
}
122+
}
123+
124+
return killed, nil
125+
}

0 commit comments

Comments
 (0)