diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 20:51:06 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 015152bb7e28a26d135fc4bc67b09a5f53d5080b (patch) | |
| tree | d5ed31d3a0dee08e07c54f045f2fd0c13e2e4d8c /src/ninep/tree.zig | |
| parent | 78ee8672307adbdf30a2afffc38efb70217dc212 (diff) | |
| download | pardes-015152bb7e28a26d135fc4bc67b09a5f53d5080b.tar.gz pardes-015152bb7e28a26d135fc4bc67b09a5f53d5080b.zip | |
A shell that exits under a run answers exit N, and the log says exit before del
A dogfood agent ran `exit 3` through pty/run: the reader got ENOENT, and
the log went straight to del. The hosts now read a shell's exit status at
its pty's end as they do a command's; a run waiting on the line answers
`exit 3` with what it printed, the log says `exit <serial> 3` before
the pane's `del`, and an open run still stats after its pane is gone
(cat fstats its input). Pins cloud9 f35b7ed, whose stat of an open fid names
its open. Writes to a gone pane's pty/data, held open or not, fail ENOENT.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/tree.zig')
| -rw-r--r-- | src/ninep/tree.zig | 20 |
1 files changed, 19 insertions, 1 deletions
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index b3f3dde6..779ecff3 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -385,7 +385,7 @@ pub fn handle(p: *Pardes, req: Req) Reply { }; return switch (req.op) { .lookup => lookup(p, req, target), - .getattr => .{ .tag = req.tag, .attr = attrOf(p, target) orelse return Reply.fail(req.tag, E.NOENT) }, + .getattr => .{ .tag = req.tag, .attr = attrOf(p, target) orelse runAttr(p, req, target) orelse return Reply.fail(req.tag, E.NOENT) }, .setattr => setattr(p, req, target), .open => open(p, req, target), .release => release(p, req), @@ -395,6 +395,24 @@ pub fn handle(p: *Pardes, req: Req) Reply { }; } +/// A run's answer outlives its pane, so its file still stats while a run +/// is open on it (a reader's fstat, cat's), as acme's fid keeps its window. +/// The stat names the open when it came on one; a mount's comes on the +/// file's walked name, and serials are never reused, so any run open on +/// this node is the reader's. +fn runAttr(p: *Pardes, req: Req, target: Target) ?Reply.Attr { + const t = switch (target) { + .pane => |t| t, + .top => return null, + }; + if (t.file != .pty_run) return null; + const run = if (req.handle != 0) openOf(p, req) else for (&p.fs.opens) |*o| { + if (o.node == req.node and o.what == .run) break o; + } else null; + if ((run orelse return null).what != .run) return null; + return .{ .name = t.file.fileName(), .node = Node.of(t.serial, t.file), .mode = t.file.mode(), .mtime = p.fs.started }; +} + fn attrOf(p: *Pardes, target: Target) ?Reply.Attr { switch (target) { .top => |f| return .{ |
