mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 13:44:32 +00:00
fix: detect zombie processes in isPidAlive on Linux
kill(pid, 0) succeeds for zombie processes, causing the gateway lock to treat a zombie lock owner as alive. Read /proc/<pid>/status on Linux to check for 'Z' (zombie) state before reporting the process as alive. This prevents the lock from being held indefinitely by a zombie process during gateway restart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
src/shared/pid-alive.test.ts
Normal file
47
src/shared/pid-alive.test.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import fsSync from "node:fs";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { isPidAlive } from "./pid-alive.js";
|
||||||
|
|
||||||
|
describe("isPidAlive", () => {
|
||||||
|
it("returns true for the current running process", () => {
|
||||||
|
expect(isPidAlive(process.pid)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for a non-existent PID", () => {
|
||||||
|
expect(isPidAlive(2 ** 30)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for invalid PIDs", () => {
|
||||||
|
expect(isPidAlive(0)).toBe(false);
|
||||||
|
expect(isPidAlive(-1)).toBe(false);
|
||||||
|
expect(isPidAlive(Number.NaN)).toBe(false);
|
||||||
|
expect(isPidAlive(Number.POSITIVE_INFINITY)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for zombie processes on Linux", async () => {
|
||||||
|
const zombiePid = process.pid;
|
||||||
|
|
||||||
|
// Mock readFileSync to return zombie state for /proc/<pid>/status
|
||||||
|
const originalReadFileSync = fsSync.readFileSync;
|
||||||
|
vi.spyOn(fsSync, "readFileSync").mockImplementation((filePath, encoding) => {
|
||||||
|
if (filePath === `/proc/${zombiePid}/status`) {
|
||||||
|
return `Name:\tnode\nUmask:\t0022\nState:\tZ (zombie)\nTgid:\t${zombiePid}\nPid:\t${zombiePid}\n`;
|
||||||
|
}
|
||||||
|
return originalReadFileSync(filePath as never, encoding as never) as never;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Override platform to linux so the zombie check runs
|
||||||
|
const originalPlatform = process.platform;
|
||||||
|
Object.defineProperty(process, "platform", { value: "linux", writable: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Re-import the module so it picks up the mocked platform and fs
|
||||||
|
vi.resetModules();
|
||||||
|
const { isPidAlive: freshIsPidAlive } = await import("./pid-alive.js");
|
||||||
|
expect(freshIsPidAlive(zombiePid)).toBe(false);
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(process, "platform", { value: originalPlatform, writable: true });
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,11 +1,33 @@
|
|||||||
|
import fsSync from "node:fs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a process is a zombie on Linux by reading /proc/<pid>/status.
|
||||||
|
* Returns false on non-Linux platforms or if the proc file can't be read.
|
||||||
|
*/
|
||||||
|
function isZombieProcess(pid: number): boolean {
|
||||||
|
if (process.platform !== "linux") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const status = fsSync.readFileSync(`/proc/${pid}/status`, "utf8");
|
||||||
|
const stateMatch = status.match(/^State:\s+(\S)/m);
|
||||||
|
return stateMatch?.[1] === "Z";
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function isPidAlive(pid: number): boolean {
|
export function isPidAlive(pid: number): boolean {
|
||||||
if (!Number.isFinite(pid) || pid <= 0) {
|
if (!Number.isFinite(pid) || pid <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
process.kill(pid, 0);
|
process.kill(pid, 0);
|
||||||
return true;
|
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (isZombieProcess(pid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user