chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
// Cross-platform "kill the whole process tree" helper (#3347).
|
||||
//
|
||||
// The embedded server is spawned via process.execPath (= omniroute.exe) with
|
||||
// ELECTRON_RUN_AS_NODE=1, and it in turn spawns grandchildren (embedded services,
|
||||
// MITM proxy, tunnels — several also omniroute.exe-as-node). On Windows, Node's
|
||||
// ChildProcess.kill()/SIGTERM/SIGKILL only terminate the DIRECT child via
|
||||
// TerminateProcess — they do NOT walk the tree. Surviving grandchildren keep a lock
|
||||
// on omniroute.exe, so the process "hangs in memory" after Exit and updates fail with
|
||||
// "file in use". Windows needs `taskkill /PID <pid> /T /F` (the /T flag terminates the
|
||||
// process AND its descendants). POSIX keeps signal-based termination, which propagates.
|
||||
|
||||
const { spawn } = require("child_process");
|
||||
|
||||
/**
|
||||
* Terminate a child process and all of its descendants.
|
||||
* @param {{ pid?: number, kill?: (signal?: string) => void } | null | undefined} proc
|
||||
* @param {{ platform?: string, signal?: string, spawnFn?: typeof spawn }} [options]
|
||||
*/
|
||||
function killProcessTree(proc, options = {}) {
|
||||
if (!proc || proc.pid == null) return;
|
||||
const platform = options.platform || process.platform;
|
||||
const signal = options.signal || "SIGTERM";
|
||||
|
||||
if (platform === "win32") {
|
||||
const spawnFn = options.spawnFn || spawn;
|
||||
try {
|
||||
// Array args + no shell → the pid (an integer we own) is never interpolated into a
|
||||
// shell command string (Hard Rule #13). /T walks the tree, /F forces termination.
|
||||
const killer = spawnFn("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
|
||||
windowsHide: true,
|
||||
});
|
||||
if (killer && typeof killer.on === "function") {
|
||||
killer.on("error", () => {
|
||||
try {
|
||||
proc.kill(signal);
|
||||
} catch {
|
||||
/* already dead */
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// taskkill unavailable (rare) — fall back to the direct kill.
|
||||
try {
|
||||
proc.kill(signal);
|
||||
} catch {
|
||||
/* already dead */
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// POSIX: signals propagate to the process group of a normally-spawned child.
|
||||
try {
|
||||
proc.kill(signal);
|
||||
} catch {
|
||||
/* already dead */
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { killProcessTree };
|
||||
Reference in New Issue
Block a user