mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-02 02:34:42 +08:00
47 lines
1.5 KiB
JavaScript
47 lines
1.5 KiB
JavaScript
import { spawn } from "node:child_process";
|
|
import { setTimeout as delay } from "node:timers/promises";
|
|
|
|
// Unix children must be spawned detached so their process group belongs to us.
|
|
export async function stopProcessTree(child) {
|
|
if (!child?.pid) return;
|
|
if (process.platform === "win32") {
|
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
let onExit;
|
|
const exited = new Promise((resolve) => {
|
|
onExit = resolve;
|
|
child.once("exit", onExit);
|
|
});
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
killer.once("error", reject);
|
|
killer.once("close", (code) => {
|
|
if (code === 0 || child.exitCode !== null || child.signalCode !== null) resolve();
|
|
else reject(new Error(`taskkill failed with exit code ${code}`));
|
|
});
|
|
});
|
|
await exited;
|
|
} finally {
|
|
child.removeListener("exit", onExit);
|
|
}
|
|
return;
|
|
}
|
|
const signal = (value) => {
|
|
try {
|
|
process.kill(-child.pid, value);
|
|
return true;
|
|
} catch (error) {
|
|
// macOS can return EPERM for a group containing only reparented zombies.
|
|
if (error.code === "ESRCH" || (value === 0 && error.code === "EPERM")) return false;
|
|
throw error;
|
|
}
|
|
};
|
|
if (!signal("SIGTERM")) return;
|
|
// A leader exiting does not imply that its descendants have exited.
|
|
for (let i = 0; i < 40; i++) {
|
|
await delay(50);
|
|
if (!signal(0)) return;
|
|
}
|
|
signal("SIGKILL");
|
|
}
|