repos / zmx

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

commit
e463586
parent
b2b7ece
author
Eric Bower
date
2025-10-11 17:12:34 -0400 EDT
fix: segfault on daemon after existing pty
1 files changed,  +16, -10
M src/daemon.zig
+16, -10
 1@@ -718,6 +718,17 @@ fn renderTerminalSnapshot(session: *Session, allocator: std.mem.Allocator) ![]u8
 2 fn notifyAttachedClientsAndCleanup(session: *Session, ctx: *ServerContext, reason: []const u8) void {
 3     std.debug.print("Session '{s}' ending: {s}\n", .{ session.name, reason });
 4 
 5+    // Copy the session name before cleanup since HashMap key points to session.name
 6+    const session_name = ctx.allocator.dupe(u8, session.name) catch {
 7+        // Fallback: just use the existing name and skip removal if allocation fails
 8+        std.debug.print("Failed to allocate session name copy\n", .{});
 9+        posix.close(session.pty_master_fd);
10+        session.deinit();
11+        ctx.allocator.destroy(session);
12+        return;
13+    };
14+    defer ctx.allocator.free(session_name);
15+
16     // Notify all attached clients
17     var it = session.attached_clients.keyIterator();
18     while (it.next()) |client_fd| {
19@@ -726,24 +737,19 @@ fn notifyAttachedClientsAndCleanup(session: *Session, ctx: *ServerContext, reaso
20             client.allocator,
21             client.fd,
22             .kill_notification,
23-            protocol.KillNotification{ .session_name = session.name },
24+            protocol.KillNotification{ .session_name = session_name },
25         ) catch |err| {
26             std.debug.print("Failed to notify client {d}: {s}\n", .{ client_fd.*, @errorName(err) });
27         };
28-        // Clear client's attached session reference
29-        if (client.attached_session) |attached| {
30-            client.allocator.free(attached);
31-            client.attached_session = null;
32-        }
33+        // Clear client's attached session reference (just null it, don't free - it points to session.name)
34+        client.attached_session = null;
35     }
36 
37     // Close PTY master fd
38     posix.close(session.pty_master_fd);
39 
40-    // Remove from sessions map BEFORE cleaning up (session.deinit frees session.name)
41-    const session_name_copy = ctx.allocator.dupe(u8, session.name) catch return;
42-    defer ctx.allocator.free(session_name_copy);
43-    _ = ctx.sessions.remove(session_name_copy);
44+    // Remove from sessions map BEFORE session.deinit frees the key
45+    _ = ctx.sessions.remove(session_name);
46 
47     // Clean up session
48     session.deinit();