summaryrefslogtreecommitdiff
path: root/src/host_io.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 15:49:45 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit2868137e34c973a47cbf667a431e885ca23cd9a2 (patch)
tree2756597f564038f22f61d77f00598a6c996fcd05 /src/host_io.zig
parent5570377c4997a5abe9f433bc15c927bcde237dc1 (diff)
downloadpardes-2868137e34c973a47cbf667a431e885ca23cd9a2.tar.gz
pardes-2868137e34c973a47cbf667a431e885ca23cd9a2.zip
Tty with a shell that is not there says so, and never sets the caller's shell
Tty /nonexistent or Tty fsh started the host's fallback shell without a word, and when spawnTty made no pane, the argument became the calling pane's shell. Tty now looks the shell up first and fails 'Tty: no shell "fsh"' when it is neither a name on the usual paths nor a path to one; spawnTty answers the pane it made, the one given the shell, and says why when it made none. The lookup (host_io Shell.find) gives the turn up around its access() calls, since a typed path may be under a mount this editor serves, and forkShell copies the pane's shell before looking it up and checks the pane is still its own after. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/host_io.zig')
-rw-r--r--src/host_io.zig25
1 files changed, 19 insertions, 6 deletions
diff --git a/src/host_io.zig b/src/host_io.zig
index 0f6fd87e..68d35a9c 100644
--- a/src/host_io.zig
+++ b/src/host_io.zig
@@ -743,8 +743,13 @@ pub const Shell = struct {
return .{ .path = path, .argv = .{ path, marks[0], marks[1], null } };
}
- fn find(bin: []const u8, buf: *[std.fs.max_path_bytes]u8) ?[*:0]const u8 {
+ /// A shell by its path or its name in the usual directories, or null.
+ /// A path may be under a mount this editor serves: the turn goes out
+ /// with the lookups (pardes.Turn.yield).
+ pub fn find(bin: []const u8, buf: *[std.fs.max_path_bytes]u8) ?[*:0]const u8 {
if (bin.len == 0 or bin.len + 1 > buf.len) return null;
+ pardes.turn.yield();
+ defer pardes.turn.back();
if (std.mem.indexOfScalar(u8, bin, '/') != null) {
@memcpy(buf[0..bin.len], bin);
buf[bin.len] = 0;
@@ -975,14 +980,22 @@ pub fn forkShell(
if (stat.kind != .directory) return error.NotDir;
break :dir path;
};
- // The turn was given up for the stat: a 9P client may have closed the
- // pane, and another taken its slot, whose command this is not.
- if (core) |c| if ((if (c.panes[pane]) |pn| pn.serial else 0) != serial) return error.PaneGone;
var master: c_int = -1;
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
- // A terminal opened on a shell of its own (`Tty fish`) runs that one.
- const own = if (core) |c| if (c.panes[pane]) |pn| pn.shell else null else null;
+ // A terminal opened on a shell of its own (`Tty fish`) runs that one,
+ // copied: looking it up gives the turn up, and the pane may go.
+ var own_buf: [256]u8 = undefined;
+ var own: ?[]const u8 = null;
+ if (core) |c| if (c.panes[pane]) |pn| if (pn.shell) |name| {
+ const n = @min(name.len, own_buf.len);
+ @memcpy(own_buf[0..n], name[0..n]);
+ own = own_buf[0..n];
+ };
var spawn = Shell.resolve(own orelse bin, &path_buf, prompt_rcs);
+ // The turn was given up for the stat and the lookup: a 9P client may
+ // have closed the pane, and another taken its slot, whose command this
+ // is not.
+ if (core) |c| if ((if (c.panes[pane]) |pn| pn.serial else 0) != serial) return error.PaneGone;
// A command pane's child is the shell running its one line, which has
// no prompt to mark (exec.zig runCommand).
const one_line = if (core) |c| if (c.panes[pane]) |pn| pn.command else null else null;