summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 11:09:59 -0300
committerGabriel Schneider <[email protected]>2026-10-01 11:10:37 -0300
commit36a9bfbaba649da3d988608f1cc7834e567df95d (patch)
tree432831507023d906f3f5ad46969202c92232bf15 /src
parentc5e5db988e59689b44feb42c59090c07687bcd42 (diff)
downloadpardes-36a9bfbaba649da3d988608f1cc7834e567df95d.tar.gz
pardes-36a9bfbaba649da3d988608f1cc7834e567df95d.zip
Every pane pardes spawns gets $PARDES_MOUNT, the session's own directory under 9ns --mntgen ($NINE_MOUNT/pardes/<name>), unset when it is not known
9ns says --mntgen with NINE_MNTGEN=1 (cloud9). Under `9ns --unix` the mount is the server it dialled, never this session (its socket does not exist until it starts), so it stays unset there; scripts fall back to `9p -a "unix!$PARDES_9P"`. Nothing is statted: the name comes from the session's socket and the environment. selfmount checks both modes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/9p_io.zig46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig
index cb345b00..0b3db274 100644
--- a/src/9p_io.zig
+++ b/src/9p_io.zig
@@ -1925,10 +1925,56 @@ pub fn exportPaneEnv(listener: ?*const Listener, serial: u32, adopts: bool) void
var buf: [16]u8 = undefined;
const id = std.fmt.bufPrintSentinel(&buf, "{d}", .{serial}, 0) catch break :exporting;
if (setenv("PARDES_9P", path, 1) != 0) break :exporting;
+ exportMount(l.path());
if (setenv("PARDES_PANE", id, 1) == 0) return;
}
_ = unsetenv("PARDES_9P");
_ = unsetenv("PARDES_PANE");
+ _ = unsetenv("PARDES_MOUNT");
+}
+
+/// $PARDES_MOUNT, the session's own directory, when the environment says
+/// where it is -- never by looking at the mount, which this editor serves
+/// (the self-mount deadlock). Under `9ns --mntgen` (NINE_MNTGEN=1) it is
+/// `$NINE_MOUNT/pardes/<name>`, the name the registry gives the socket.
+/// Any other $NINE_MOUNT is a mount of another server, made before this
+/// session had a socket: unknown, so unset, and a script falls back to
+/// `9p -a "unix!$PARDES_9P"`. A Tty9p shell sets its own (linux/v9fs.zig).
+fn exportMount(sock_path: []const u8) void {
+ const mntgen = if (libc.getenv("NINE_MNTGEN")) |v| std.mem.eql(u8, std.mem.span(v), "1") else false;
+ const root = if (libc.getenv("NINE_MOUNT")) |v| std.mem.span(v) else "";
+ const base = std.fs.path.basename(sock_path);
+ const sock_prefix = "pardes-9p-";
+ const suffix = ".sock";
+ if (mntgen and root.len > 0 and std.mem.startsWith(u8, base, sock_prefix) and std.mem.endsWith(u8, base, suffix) and base.len > sock_prefix.len + suffix.len) {
+ var buf: [4096]u8 = undefined;
+ if (std.fmt.bufPrintSentinel(&buf, "{s}/pardes/{s}", .{ std.mem.trimEnd(u8, root, "/"), base[sock_prefix.len .. base.len - suffix.len] }, 0)) |mount| {
+ if (setenv("PARDES_MOUNT", mount, 1) == 0) return;
+ } else |_| {}
+ }
+ _ = unsetenv("PARDES_MOUNT");
+}
+
+test "a pane's PARDES_MOUNT is the session's directory under 9ns --mntgen, and unset when not known" {
+ const names = [_][*:0]const u8{ "NINE_MNTGEN", "NINE_MOUNT", "PARDES_MOUNT" };
+ var saved: [names.len]?[:0]u8 = @splat(null);
+ for (names, &saved) |name, *value| {
+ if (libc.getenv(name)) |old| value.* = try testing.allocator.dupeZ(u8, std.mem.span(old));
+ }
+ defer for (names, saved) |name, value| {
+ if (value) |old| {
+ _ = setenv(name, old, 1);
+ testing.allocator.free(old);
+ } else _ = unsetenv(name);
+ };
+ _ = setenv("NINE_MOUNT", "/mnt/9p", 1);
+ _ = setenv("NINE_MNTGEN", "1", 1);
+ exportMount("/run/user/1000/pardes-9p-work.sock");
+ try testing.expectEqualStrings("/mnt/9p/pardes/work", std.mem.span(libc.getenv("PARDES_MOUNT").?));
+ // A mount of another server (9ns --unix): not this session's.
+ _ = unsetenv("NINE_MNTGEN");
+ exportMount("/run/user/1000/pardes-9p-work.sock");
+ try testing.expect(libc.getenv("PARDES_MOUNT") == null);
}
test "9P shell environment states being inside pardes apart from how to reach it" {