Commit 77d03a5

Eric Bower  ·  2026-04-02 16:14:24 -0400 EDT
parent cc33276
feat(kill): add `--force` flag to remove the socket file

If we cannot communicate with the daemon then it could mean a number of things.
We don't automatically assume that a communication error means we can never connect to it.

So we add a `--force` command for when there is an error the user cannot recover from and we
will delete the socket file.

Unfortunately, we do not know the pid for the terminal session outside of the daemon so we
cannot automatically kill the pid in some cases which means it could linger.
2 files changed,  +23, -17
+1, -1
......@@ -73,7 +73,7 @@ Commands:
7373 [r]un <name> [command...] Send command without attaching, creating session if needed
7474 [d]etach Detach all clients from current session (ctrl+\ for current client)
7575 [l]ist [--short] List active sessions
76- [k]ill <name>... Kill a session and all attached clients
76+ [k]ill <name>... [--force] Kill a session and all attached clients
7777 [hi]story <name> [--vt|--html] Output session scrollback (--vt or --html for escape sequences)
7878 [w]ait <name>... Wait for session tasks to complete
7979 [c]ompletions <shell> Completion scripts for shell integration (bash, zsh, or fish)
+22, -16
......@@ -176,12 +176,16 @@ pub fn main() !void {
176176 }
177177 args_raw.deinit(alloc);
178178 }
179+ var force = false;
179180 while (args.next()) |session_name| {
181+ if (std.mem.eql(u8, session_name, "--force")) {
182+ force = true;
183+ continue;
184+ }
180185 const sesh = try socket.getSeshName(alloc, session_name);
181186 try args_raw.append(alloc, sesh);
182187 }
183- // if no args are provided we assume they want to wait for all sessions matching the
184- // prefix.
188+ // if no args are provided we assume they want to kill all sessions matching the prefix.
185189 if (args_raw.items.len == 0) {
186190 const prefix = socket.getSeshPrefix();
187191 if (prefix.len == 0) {
......@@ -196,21 +200,23 @@ pub fn main() !void {
196200 }
197201 sessions.deinit(alloc);
198202 }
203+
199204 for (sessions.items) |session| {
200205 for (args_raw.items) |prefix| {
201- if (std.mem.startsWith(u8, session.name, prefix)) {
202- kill(&cfg, session.name) catch |err| {
203- try stderr.print(
204- "failed to kill session={s}: {s}\n",
205- .{ session.name, @errorName(err) },
206- );
207- try stderr.flush();
208- };
209- break;
206+ if (!std.mem.startsWith(u8, session.name, prefix)) {
207+ continue;
210208 }
209+
210+ kill(&cfg, session.name, force) catch |err| {
211+ try stderr.print(
212+ "failed to kill session={s}: {s}\n",
213+ .{ session.name, @errorName(err) },
214+ );
215+ try stderr.flush();
216+ };
217+ break;
211218 }
212219 }
213- return;
214220 } else if (std.mem.eql(u8, cmd, "wait") or std.mem.eql(u8, cmd, "w")) {
215221 var args_raw: std.ArrayList([]const u8) = .empty;
216222 defer {
......@@ -803,7 +809,7 @@ fn help() !void {
803809 \\ [r]un <name> [command...] Send command without attaching, creating session if needed
804810 \\ [d]etach Detach all clients from current session (ctrl+\ for current client)
805811 \\ [l]ist [--short] List active sessions
806- \\ [k]ill <name>... Kill a session and all attached clients
812+ \\ [k]ill <name>... [--force] Kill a session and all attached clients
807813 \\ [hi]story <name> [--vt|--html] Output session scrollback (--vt or --html for escape sequences)
808814 \\ [w]ait <name>... Wait for session tasks to complete
809815 \\ [c]ompletions <shell> Completion scripts for shell integration (bash, zsh, or fish)
......@@ -1009,7 +1015,7 @@ fn detachAll(cfg: *Cfg) !void {
10091015 };
10101016 }
10111017
1012-fn kill(cfg: *Cfg, session_name: []const u8) !void {
1018+fn kill(cfg: *Cfg, session_name: []const u8, force: bool) !void {
10131019 var gpa = std.heap.GeneralPurposeAllocator(.{}){};
10141020 defer _ = gpa.deinit();
10151021 const alloc = gpa.allocator();
......@@ -1035,12 +1041,12 @@ fn kill(cfg: *Cfg, session_name: []const u8) !void {
10351041 std.log.err("session unresponsive: {s}", .{@errorName(err)});
10361042 var buf: [4096]u8 = undefined;
10371043 var w = std.fs.File.stdout().writer(&buf);
1038- if (err == error.ConnectionRefused) {
1044+ if (force or err == error.ConnectionRefused) {
10391045 socket.cleanupStaleSocket(dir, session_name);
10401046 w.interface.print("cleaned up stale session {s}\n", .{session_name}) catch {};
10411047 } else {
10421048 w.interface.print(
1043- "session {s} is unresponsive ({s}) -- daemon may be busy, try again or kill the process directly\n",
1049+ "session {s} is unresponsive ({s})\ndaemon may be busy: try again, add `--force` flag, or kill the process directly\n",
10441050 .{ session_name, @errorName(err) },
10451051 ) catch {};
10461052 }