fix(tmux): bound calls on our own server and run them with the server's own binary

A tmux 3.8 installed under a running 3.6 server left PATH's 3.8 client
asking the 3.6 server, which spun at 100% CPU; the dashboard's
synchronous `has-session` on it had no limit and froze every page, hook
and terminal for five minutes (14:07–14:12 on the shared server).

- Every call on our own server now has a 5 s limit and the member's
  one-minute back-off (`host: tmux server not answering` meanwhile), so a
  stuck server costs the event loop one timeout per minute, not one per
  poll.
- Every call on a running server runs that server's own binary
  (`/proc/<pid>/exe`, found from its listening socket in /proc — asking
  tmux would be the very call that hangs), re-read every 10 s; PATH's
  tmux only starts a server or answers `-V`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 14:23:16 +07:00
parent 38c7ac6635
commit bac6c04a0b
3 changed files with 179 additions and 11 deletions
+53
View File
@@ -344,3 +344,56 @@ describe("tmux wrapper", () => {
assert.equal(tmux.upgradeAdvice("3.3.2", "freebsd"), null);
});
});
describe("our own tmux server: bounded and version-matched", () => {
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
beforeEach(() => tmux.__reset());
it("backs off after a call to our server times out, instead of blocking every poll", () => {
let calls = 0;
tmux.__setExecImpl(() => {
calls++;
throw Object.assign(new Error("spawnSync tmux ETIMEDOUT"), { code: "ETIMEDOUT" });
});
assert.equal(tmux.hasSession("agenthub-lane-1"), false);
const after = calls;
assert.ok(after >= 1);
// Meanwhile nothing reaches tmux: the server is left alone for a minute.
assert.equal(tmux.hasSession("agenthub-lane-1"), false);
assert.equal(tmux.serverPid(), null);
assert.equal(calls, after);
});
it("finds the binary of the server listening on our socket from /proc alone", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "tmux-proc-"));
const sock = "/tmp/tmux-1000/default";
fs.mkdirSync(path.join(root, "net"));
fs.writeFileSync(
path.join(root, "net", "unix"),
"Num RefCount Protocol Flags Type St Inode Path\n" +
`0000: 00000003 00000000 00000000 0001 03 777 ${sock}\n` + // a client's
`0000: 00000002 00000000 00010000 0001 01 4242 ${sock}\n` // the listener
);
const proc = (pid, comm, fds) => {
fs.mkdirSync(path.join(root, pid, "fd"), { recursive: true });
fs.writeFileSync(path.join(root, pid, "comm"), `${comm}\n`);
fs.symlinkSync(process.execPath, path.join(root, pid, "exe"));
fds.forEach((t, i) => fs.symlinkSync(t, path.join(root, pid, "fd", String(i))));
};
proc("10", "bash", ["socket:[4242]"]); // not tmux
proc("11", "tmux: client", ["socket:[777]"]);
proc("12", "tmux: server", ["/dev/null", "socket:[4242]"]);
try {
assert.equal(
tmux.serverExeFor(sock, { procRoot: root, uid: null }),
path.join(root, "12", "exe")
);
assert.equal(tmux.serverExeFor("/tmp/other", { procRoot: root, uid: null }), null);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
});