repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

commit
7b9083b
parent
b0a88af
author
Eric Bower
date
2025-12-15 20:30:41 -0500 EST
feat(list): sort by session name
1 files changed,  +52, -8
M src/main.zig
+52, -8
 1@@ -254,6 +254,18 @@ fn help() !void {
 2     try w.interface.flush();
 3 }
 4 
 5+const SessionEntry = struct {
 6+    name: []const u8,
 7+    pid: ?i32,
 8+    clients_len: ?usize,
 9+    is_error: bool,
10+    error_name: ?[]const u8,
11+
12+    fn lessThan(_: void, a: SessionEntry, b: SessionEntry) bool {
13+        return std.mem.order(u8, a.name, b.name) == .lt;
14+    }
15+};
16+
17 fn list(cfg: *Cfg) !void {
18     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
19     defer _ = gpa.deinit();
20@@ -262,32 +274,64 @@ fn list(cfg: *Cfg) !void {
21     var dir = try std.fs.openDirAbsolute(cfg.socket_dir, .{ .iterate = true });
22     defer dir.close();
23     var iter = dir.iterate();
24-    var hasSessions = false;
25     var buf: [4096]u8 = undefined;
26     var w = std.fs.File.stdout().writer(&buf);
27+
28+    var sessions = try std.ArrayList(SessionEntry).initCapacity(alloc, 16);
29+    defer {
30+        for (sessions.items) |session| {
31+            alloc.free(session.name);
32+        }
33+        sessions.deinit(alloc);
34+    }
35+
36     while (try iter.next()) |entry| {
37         const exists = sessionExists(dir, entry.name) catch continue;
38         if (exists) {
39-            hasSessions = true;
40+            const name = try alloc.dupe(u8, entry.name);
41+            errdefer alloc.free(name);
42+
43             const socket_path = try getSocketPath(alloc, cfg.socket_dir, entry.name);
44             defer alloc.free(socket_path);
45 
46             const result = probeSession(alloc, socket_path) catch |err| {
47-                w.interface.print("session_name={s}\tstatus={s}\t(cleaning up)\n", .{ entry.name, @errorName(err) }) catch {};
48-                w.interface.flush() catch {};
49+                try sessions.append(alloc, .{
50+                    .name = name,
51+                    .pid = null,
52+                    .clients_len = null,
53+                    .is_error = true,
54+                    .error_name = @errorName(err),
55+                });
56                 cleanupStaleSocket(dir, entry.name);
57                 continue;
58             };
59-            defer posix.close(result.fd);
60+            posix.close(result.fd);
61 
62-            try w.interface.print("session_name={s}\tpid={d}\tclients={d}\n", .{ entry.name, result.info.pid, result.info.clients_len });
63-            try w.interface.flush();
64+            try sessions.append(alloc, .{
65+                .name = name,
66+                .pid = result.info.pid,
67+                .clients_len = result.info.clients_len,
68+                .is_error = false,
69+                .error_name = null,
70+            });
71         }
72     }
73 
74-    if (!hasSessions) {
75+    if (sessions.items.len == 0) {
76         try w.interface.print("no sessions found in {s}\n", .{cfg.socket_dir});
77         try w.interface.flush();
78+        return;
79+    }
80+
81+    std.mem.sort(SessionEntry, sessions.items, {}, SessionEntry.lessThan);
82+
83+    for (sessions.items) |session| {
84+        if (session.is_error) {
85+            try w.interface.print("session_name={s}\tstatus={s}\t(cleaning up)\n", .{ session.name, session.error_name.? });
86+        } else {
87+            try w.interface.print("session_name={s}\tpid={d}\tclients={d}\n", .{ session.name, session.pid.?, session.clients_len.? });
88+        }
89+        try w.interface.flush();
90     }
91 }
92