- commit
- 34e2e3b
- parent
- f03cdc1
- author
- Eric Bower
- date
- 2025-10-11 16:03:12 -0400 EDT
fix: use xev for pty writes
1 files changed,
+54,
-4
+54,
-4
1@@ -40,6 +40,12 @@ const PtyReadContext = struct {
2 server_ctx: *ServerContext,
3 };
4
5+// Context for PTY write callbacks
6+const PtyWriteContext = struct {
7+ allocator: std.mem.Allocator,
8+ message: []u8,
9+};
10+
11 // A PTY session that manages a persistent shell process
12 // Stores the PTY master file descriptor, shell process PID, scrollback buffer,
13 // and a read buffer for async I/O with libxev
14@@ -894,14 +900,35 @@ fn readPtyCallback(
15 response_buf.appendSlice(session.allocator, "\"}}\n") catch return .disarm;
16 const response = response_buf.items;
17
18- // Send to all attached clients
19+ // Send to all attached clients using async write
20 var it = session.attached_clients.keyIterator();
21 while (it.next()) |client_fd| {
22 const attached_client = ctx.clients.get(client_fd.*) orelse continue;
23- std.debug.print("Sending response to client fd={d}\n", .{client_fd.*});
24- _ = posix.write(attached_client.fd, response) catch |err| {
25- std.debug.print("Error writing to fd={d}: {s}\n", .{ client_fd.*, @errorName(err) });
26+ const owned_response = session.allocator.dupe(u8, response) catch continue;
27+
28+ const write_ctx = session.allocator.create(PtyWriteContext) catch {
29+ session.allocator.free(owned_response);
30+ continue;
31+ };
32+ write_ctx.* = .{
33+ .allocator = session.allocator,
34+ .message = owned_response,
35 };
36+
37+ const write_completion = session.allocator.create(xev.Completion) catch {
38+ session.allocator.free(owned_response);
39+ session.allocator.destroy(write_ctx);
40+ continue;
41+ };
42+
43+ attached_client.stream.write(
44+ loop,
45+ write_completion,
46+ .{ .slice = owned_response },
47+ PtyWriteContext,
48+ write_ctx,
49+ ptyWriteCallback,
50+ );
51 }
52 }
53
54@@ -935,6 +962,29 @@ fn readPtyCallback(
55 unreachable;
56 }
57
58+fn ptyWriteCallback(
59+ write_ctx_opt: ?*PtyWriteContext,
60+ _: *xev.Loop,
61+ completion: *xev.Completion,
62+ _: xev.Stream,
63+ _: xev.WriteBuffer,
64+ write_result: xev.WriteError!usize,
65+) xev.CallbackAction {
66+ const write_ctx = write_ctx_opt.?;
67+ const allocator = write_ctx.allocator;
68+
69+ if (write_result) |_| {
70+ // Successfully sent PTY output to client
71+ } else |_| {
72+ // Silently ignore write errors to prevent log spam
73+ }
74+
75+ allocator.free(write_ctx.message);
76+ allocator.destroy(write_ctx);
77+ allocator.destroy(completion);
78+ return .disarm;
79+}
80+
81 fn execShellWithPrompt(allocator: std.mem.Allocator, session_name: []const u8, shell: [*:0]const u8) noreturn {
82 // Detect shell type and add prompt injection
83 const shell_name = std.fs.path.basename(std.mem.span(shell));