From 36a9bfbaba649da3d988608f1cc7834e567df95d Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 11:09:59 -0300 Subject: Every pane pardes spawns gets $PARDES_MOUNT, the session's own directory under 9ns --mntgen ($NINE_MOUNT/pardes/), 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 --- src/9p_io.zig | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'src') 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/`, 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" { -- cgit v1.3