From e9897bd9566832dfa09f4d3cf5daa6f3d0a84bd5 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 27 Aug 2026 14:27:05 -0300 Subject: detached: a daemon serves acme's control filesystem, in its own poll and with no thread Step 1 of the 9P chain (docs/9p.typ 12.1, docs/registry.typ 9P-14). A detached session was the one configuration no script could drive. The core, the panes and the undo history outlive every frontend that attaches -- and the filesystem that would let a program read or change any of it was never mounted, because `push_fs_reply` was one of the host methods this process left null. Nothing prevented it; the call was simply not there. It costs less here than in the desktop shells. They start a thread that blocks on poll() and pokes a loop it does not otherwise share (`fs_service.wake`); this process already runs ONE poll over its listener, its frontends, its pane shells and inotify, so /dev/fuse is one more descriptor in the same syscall and there is no thread at all. `Source.fuse`'s arm does nothing on purpose: being in the set is the whole point, because the wake must end the sleep so that `pollFrame` -- which runs after `pull_wait_input` returns, where re-entering the core is legal -- reaches the drain. `main.zig` refused `--detach --fs` outright, with a comment saying that serving it would mean mounting FUSE in the detached core and that this was a feature rather than a fix. It was right, and this is the feature. `--attach` is still refused: a frontend has no core to serve. Verified against the project's own clients: examples/acmefs/pardesctl panes, new, send, body and del all drive a daemon, and the pane shells it forks now inherit PARDES_FS/PARDES_PANE like every other host's. --- src/main.zig | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'src/main.zig') diff --git a/src/main.zig b/src/main.zig index b8cb6b2d..1104aeb8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -304,19 +304,23 @@ fn nativeMain(init: std.process.Init) !void { // reading. Refused rather than resolved by declaration order, which would // silently drop whichever flag lost. if (detach != null and attach != null) return error.BadArgs; - // `--fs` mounts the acme control filesystem, and only a LOCAL session has - // one: `fs_service.start` is called inside tty.zig's `localSession` and - // gui.zig's equivalent, both of which an `--attach` skips entirely, and - // `detached/server.zig` never reads `opts.fs` at all. So `--fs` with either - // of these was parsed, stored, and then served by nobody. + // `--fs` mounts the acme control filesystem, and it needs a CORE to serve. + // `--attach` has none — it is a terminal whose state lives in another + // process — so the flag there would be parsed, stored, and served by + // nobody. Refused rather than dropped, and it is the stronger case of the + // line above: a contradiction is at least visible, whereas a silently + // dropped mount is invisible until someone waits for a directory that will + // never appear. // - // Refused for the same reason as the line above, and it is the stronger - // case: a contradiction is at least visible, whereas a silently dropped - // mount is invisible until someone waits for a directory that will never - // appear. Serving it instead would mean mounting FUSE in the detached core, - // which is a feature rather than a fix — `push_fs_reply` is one of the five - // host methods that core deliberately leaves null (docs/detached.md). - if (opts.fs != null and (detach != null or attach != null)) return error.BadArgs; + // `--detach` used to be refused here too, and is not any more. The reason + // given was that `push_fs_reply` was one of the host methods the detached + // core deliberately left null; it no longer is. A daemon mounts its own + // /dev/fuse and polls it in the same `poll(2)` as its frontends and its + // pane shells, which costs it one descriptor and no thread — strictly less + // than the desktop shells pay. `--detach --fs` is now the configuration + // that most wants a control filesystem, because it is the one whose panes + // outlive every terminal that could otherwise have scripted them. + if (opts.fs != null and attach != null) return error.BadArgs; // `--detach` replaces the frontend rather than choosing among them: the // core runs here, with no terminal, and the frontends are elsewhere on a // socket (src/detached/). It is checked before `platform` because it is not -- cgit v1.3