fix(terminal): properly catch child EOF

And be much more explicit about error handling.
This commit is contained in:
CJ van den Berg 2026-02-25 19:12:49 +01:00
parent 61a509cf2f
commit 69b0885f4b
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 54 additions and 20 deletions

View file

@ -30,8 +30,8 @@
.hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D", .hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D",
}, },
.vaxis = .{ .vaxis = .{
.url = "git+https://github.com/neurocyte/libvaxis?ref=main#03fe1b123afdaf879829da507f54e42780d4ec65", .url = "git+https://github.com/neurocyte/libvaxis?ref=main#8fba2b2b14174f73a957b3b4b0e1c30f25a78577",
.hash = "vaxis-0.5.1-BWNV_EJOCQCOD5tzfVqOgDUSYfvBLrokxE0fr9MSb05q", .hash = "vaxis-0.5.1-BWNV_HJQCQAVq-jmKeSh0Ur4DLbkc3DUP-amXI3k22TB",
}, },
.zeit = .{ .zeit = .{
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88",

View file

@ -271,7 +271,7 @@ const Vt = struct {
self.cwd.deinit(allocator); self.cwd.deinit(allocator);
self.title.deinit(allocator); self.title.deinit(allocator);
if (self.pty_pid) |pid| { if (self.pty_pid) |pid| {
pid.send(.{ "pty_actor", "quit" }) catch {}; pid.send(.{"quit"}) catch {};
pid.deinit(); pid.deinit();
self.pty_pid = null; self.pty_pid = null;
} }
@ -340,16 +340,20 @@ const pty = struct {
errdefer self.deinit(); errdefer self.deinit();
if (try m.match(.{ "fd", "pty", "read_ready" })) { if (try m.match(.{ "fd", "pty", "read_ready" })) {
try self.read_and_process(); self.read_and_process() catch |e| return switch (e) {
return; error.Terminated => tp.exit_normal(),
} error.InputOutput => tp.exit_normal(),
error.SendFailed => tp.exit_normal(),
if (try m.match(.{ "pty_actor", "quit" })) { error.Unexpected => tp.exit_normal(),
};
} else if (try m.match(.{"quit"})) {
return tp.exit_normal(); return tp.exit_normal();
} else {
return tp.unexpected(m);
} }
} }
fn read_and_process(self: *@This()) tp.result { fn read_and_process(self: *@This()) error{ Terminated, InputOutput, SendFailed, Unexpected }!void {
var buf: [4096]u8 = undefined; var buf: [4096]u8 = undefined;
while (true) { while (true) {
@ -359,27 +363,57 @@ const pty = struct {
const code = self.vt.cmd.wait(); const code = self.vt.cmd.wait();
self.vt.event_queue.push(.{ .exited = code }); self.vt.event_queue.push(.{ .exited = code });
self.parent.send(.{ "terminal_view", "output" }) catch {}; self.parent.send(.{ "terminal_view", "output" }) catch {};
return tp.exit_normal(); return error.InputOutput;
},
error.SystemResources,
error.IsDir,
error.OperationAborted,
error.BrokenPipe,
error.ConnectionResetByPeer,
error.ConnectionTimedOut,
error.NotOpenForReading,
error.SocketNotConnected,
error.Canceled,
error.AccessDenied,
error.ProcessNotFound,
error.LockViolation,
error.Unexpected,
=> {
std.log.err("terminal_view: read unexpected: {}", .{e});
return error.Unexpected;
}, },
else => return tp.exit_error(e, @errorReturnTrace()),
}; };
if (n == 0) { if (n == 0) {
const code = self.vt.cmd.wait(); const code = self.vt.cmd.wait();
self.vt.event_queue.push(.{ .exited = code }); self.vt.event_queue.push(.{ .exited = code });
self.parent.send(.{ "terminal_view", "output" }) catch {}; self.parent.send(.{ "terminal_view", "output" }) catch {};
return tp.exit_normal(); return error.Terminated;
} }
const exited = self.vt.processOutput(&self.parser, buf[0..n]) catch |e| defer self.parent.send(.{ "terminal_view", "output" }) catch {};
return tp.exit_error(e, @errorReturnTrace());
if (exited) { switch (self.vt.processOutput(&self.parser, buf[0..n]) catch |e| switch (e) {
self.parent.send(.{ "terminal_view", "output" }) catch {}; error.WriteFailed,
return tp.exit_normal(); error.ReadFailed,
error.OutOfMemory,
error.Utf8InvalidStartByte,
=> {
std.log.err("terminal_view: processOutput unexpected: {}", .{e});
return error.Unexpected;
},
}) {
.exited => {
return error.Terminated;
},
.running => {},
} }
// Notify parent that new output is available.
self.parent.send(.{ "terminal_view", "output" }) catch {};
} }
self.fd.wait_read() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.fd.wait_read() catch |e| switch (e) {
error.ThespianFileDescriptorWaitReadFailed => {
std.log.err("terminal_view: wait_read unexpected: {}", .{e});
return error.Unexpected;
},
};
} }
}; };