From 6e05b824797cb47423cf686b786ec50863be6632 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 30 Aug 2026 13:59:17 +0000 Subject: [PATCH] fix(fleet): the `who` recipe reported nobody on a machine full of users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `who || w` was wrong, and wrong in a way that reads as correct. `who` reads utmp, which on a systemd host with pty-less SSH sessions is routinely empty while people are very much logged in — and it exits 0 either way. So `||` never reached `w`, and the recipe returned nothing at all. Confirmed here: `who` prints zero lines and exits 0, while `w` lists two live sessions on the same box. The failure mode is empty output, not a non-zero exit, and `||` cannot see the difference. The recipe now falls back on emptiness, and says "no interactive logins on this host" when both agree there are none — because a status command that prints nothing is indistinguishable from a broken one, which is exactly how this got reported. Audited the other four reporting recipes through the real fleet path against a live host first, to check the reporter's hunch that output had gone missing more widely: check-updates 8 lines, disk 6, uptime 1, reboot-required 1. Only `who` was silent, so nothing in the streaming path is dropping anything. Adds a test that no reporting recipe may produce empty output, which is the invariant that was actually broken, plus two asserting `who` branches on emptiness rather than on exit status. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UeSWg1Czsb2Lwxj8vHUnA4 --- packages/fleet-core/src/recipes.test.ts | 33 +++++++++++++++++++++++++ packages/fleet-core/src/recipes.ts | 22 ++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/fleet-core/src/recipes.test.ts b/packages/fleet-core/src/recipes.test.ts index 446b776..aa4deec 100644 --- a/packages/fleet-core/src/recipes.test.ts +++ b/packages/fleet-core/src/recipes.test.ts @@ -102,6 +102,39 @@ describe('check-updates', () => { }, 90_000) }) +describe('no status recipe is ever silent', () => { + /* + * A status command that prints nothing is indistinguishable from a broken + * one. `who || w` shipped exactly that: `who` reads utmp, which is routinely + * empty on a systemd host with pty-less SSH sessions, and it exits 0 either + * way — so `||` never reached the fallback and the recipe reported nothing + * on a machine with users logged into it. + */ + const reporting = BUILTIN_RECIPES.filter((entry) => entry.name !== 'upgrade') + + for (const entry of reporting) { + it(`${entry.name} says something`, async () => { + const binary = entry.interpreter === 'bash' ? 'bash' : '/bin/sh' + const { code, stdout } = await runScript(binary, entry.script) + expect(code).toBe(0) + expect(stdout.trim(), `${entry.name} produced no output`).not.toBe('') + }, 90_000) + } +}) + +describe('who', () => { + it('falls back on empty output, not on a non-zero exit', () => { + // `||` cannot see the difference, and the difference is the whole bug. + const script = findRecipe('who')!.script + expect(script).not.toMatch(/who\s*\|\|/) + expect(script).toMatch(/-z "\$found"/) + }) + + it('states that nobody is logged in rather than printing nothing', () => { + expect(findRecipe('who')!.script).toMatch(/no interactive logins/) + }) +}) + describe('reboot-required', () => { it('succeeds when a reboot is needed but no package list exists', async () => { // The exact case the old `[ -f pkgs ] && cat pkgs` form got wrong: its diff --git a/packages/fleet-core/src/recipes.ts b/packages/fleet-core/src/recipes.ts index 0ae35fa..f190194 100644 --- a/packages/fleet-core/src/recipes.ts +++ b/packages/fleet-core/src/recipes.ts @@ -95,7 +95,27 @@ fi`.trim(), id: 'builtin:who', name: 'who', description: 'Who is logged in right now.', - script: 'who || w', + /* + * `who || w` was wrong, and wrong in a way that looked fine. + * + * `who` reads utmp, which on a systemd host with pty-less SSH sessions is + * routinely empty while people are very much logged in — and it exits 0 + * either way. So `||` never reached `w`, and the recipe reported an empty + * result on a machine with users on it. The failure mode here is empty + * output, not a non-zero exit, and `||` cannot see the difference. + * + * It also ends by saying so out loud. A status command that prints + * nothing is indistinguishable from a broken one, which is exactly how + * this got reported. + */ + script: ` +found=$(who 2>/dev/null) +[ -z "$found" ] && found=$(w -h 2>/dev/null) +if [ -n "$found" ]; then + printf '%s\n' "$found" +else + echo "no interactive logins on this host" +fi`.trim(), timeoutSeconds: 30, }), ]