mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-03 19:54:44 -07:00
86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
import { spawn } from 'child_process';
|
|
import type { ChildProcess } from 'child_process';
|
|
|
|
export interface ProcessTerminationOptions {
|
|
timeoutMs?: number;
|
|
logError?: (message: string, error: Error) => void;
|
|
}
|
|
|
|
async function killWindowsProcessTree(
|
|
pid: number,
|
|
logError?: (message: string, error: Error) => void
|
|
): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
const taskkill = spawn('taskkill', ['/pid', pid.toString(), '/t', '/f'], {
|
|
stdio: 'pipe',
|
|
});
|
|
|
|
taskkill.on('exit', (code) => {
|
|
if (code !== 0 && code !== 128) {
|
|
logError?.(
|
|
`taskkill exited with code ${code} for PID ${pid}`,
|
|
new Error(`taskkill failed with exit code ${code}`)
|
|
);
|
|
}
|
|
resolve();
|
|
});
|
|
|
|
taskkill.on('error', (error) => {
|
|
logError?.(`Failed to execute taskkill for PID ${pid}:`, error);
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function terminateProcess(
|
|
childProcess: ChildProcess,
|
|
options: ProcessTerminationOptions = {}
|
|
): Promise<void> {
|
|
const { timeoutMs = 3000, logError } = options;
|
|
|
|
if (!childProcess?.pid) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (process.platform === 'win32') {
|
|
await killWindowsProcessTree(childProcess.pid, logError);
|
|
|
|
await new Promise<void>((resolve) => {
|
|
const timeout = setTimeout(
|
|
() => {
|
|
resolve();
|
|
},
|
|
Math.min(timeoutMs, 2000)
|
|
);
|
|
|
|
childProcess.once('exit', () => {
|
|
clearTimeout(timeout);
|
|
resolve();
|
|
});
|
|
});
|
|
} else {
|
|
childProcess.kill('SIGTERM');
|
|
|
|
await new Promise<void>((resolve) => {
|
|
const timeout = setTimeout(() => {
|
|
if (childProcess && !childProcess.killed) {
|
|
try {
|
|
childProcess.kill('SIGKILL');
|
|
} catch (error) {
|
|
logError?.('Error force-killing process:', error as Error);
|
|
}
|
|
}
|
|
resolve();
|
|
}, timeoutMs);
|
|
|
|
childProcess.once('exit', () => {
|
|
clearTimeout(timeout);
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
} catch (error) {
|
|
logError?.('Error terminating process:', error as Error);
|
|
}
|
|
}
|