diff --git a/src/service-manager-probe.ts b/src/service-manager-probe.ts index 8446c78ae..7dbf4b279 100644 --- a/src/service-manager-probe.ts +++ b/src/service-manager-probe.ts @@ -63,6 +63,7 @@ export interface ProbeRunner { stderr: string; timedOut: boolean; spawnFailed: boolean; + spawnErrorCode?: string; }; } @@ -84,13 +85,15 @@ export const defaultProbeRunner: ProbeRunner = (file, args) => { windowsHide: true, timeout: SERVICE_PROBE_TIMEOUT_MS, }); + const spawnErrorCode = (result.error as NodeJS.ErrnoException | undefined)?.code; + const timedOut = spawnErrorCode === "ETIMEDOUT" || result.signal !== null; return { status: result.status, stdout: String(result.stdout ?? ""), stderr: String(result.stderr ?? ""), - // `signal` is SIGTERM when the timeout fired; a spawn failure sets `error`. - timedOut: result.signal !== null && result.error === undefined, - spawnFailed: result.error !== undefined, + timedOut, + spawnFailed: result.error !== undefined && !timedOut, + spawnErrorCode, }; }; @@ -264,8 +267,12 @@ function inspectSystemd(deps: Required>): Servic "--user", "show", TASK, "-p", "LoadState", "-p", "ActiveState", "-p", "FragmentPath", "-p", "NeedDaemonReload", ]); - if (shown.spawnFailed) return { kind: "absent" }; if (shown.timedOut) return unknown("systemctl could not be asked: timed out"); + if (shown.spawnFailed) { + return shown.spawnErrorCode === "ENOENT" + ? { kind: "absent" } + : unknown(`systemctl could not be spawned: ${shown.stderr.trim() || shown.spawnErrorCode || "unknown error"}`); + } if (shown.status !== 0) { // A missing unit still exits ZERO and says not-found; a non-zero status means // the question never reached the bus. diff --git a/tests/service-probe-docker.test.ts b/tests/service-probe-docker.test.ts index 68b97f524..da860f52a 100644 --- a/tests/service-probe-docker.test.ts +++ b/tests/service-probe-docker.test.ts @@ -16,6 +16,7 @@ test("Linux reports systemd absent when systemctl cannot be spawned", () => { stderr: "spawn systemctl ENOENT", timedOut: false, spawnFailed: true, + spawnErrorCode: "ENOENT", }); try { @@ -26,3 +27,24 @@ test("Linux reports systemd absent when systemctl cannot be spawned", () => { rmSync(home, { recursive: true, force: true }); } }); + +test.each([ + ["ETIMEDOUT", true], + ["EACCES", false], +] as const)("Linux does not treat a %s systemctl failure as absent", (spawnErrorCode, timedOut) => { + const home = mkdtempSync(join(tmpdir(), "ocx-probe-failure-")); + const run: ProbeRunner = () => ({ + status: null, + stdout: "", + stderr: `spawn systemctl ${spawnErrorCode}`, + timedOut, + spawnFailed: !timedOut, + spawnErrorCode, + }); + + try { + expect(inspectServiceManagerInstallation({ run, platform: "linux", home }).kind).toBe("unknown"); + } finally { + rmSync(home, { recursive: true, force: true }); + } +});