mirror of
https://github.com/toddbluhm/env-cmd.git
synced 2025-12-08 18:23:33 +00:00
117 lines
4.2 KiB
JavaScript
117 lines
4.2 KiB
JavaScript
const SIGNALS_TO_HANDLE = [
|
|
'SIGINT', 'SIGTERM', 'SIGHUP',
|
|
];
|
|
export class TermSignals {
|
|
terminateSpawnedProcessFuncHandlers = {};
|
|
terminateSpawnedProcessFuncExitHandler;
|
|
verbose = false;
|
|
_exitCalled = false;
|
|
constructor(options = {}) {
|
|
this.verbose = options.verbose === true;
|
|
}
|
|
handleTermSignals(proc) {
|
|
// Terminate child process if parent process receives termination events
|
|
const terminationFunc = (signal) => {
|
|
this._removeProcessListeners();
|
|
if (!this._exitCalled) {
|
|
if (this.verbose) {
|
|
console.info('Parent process exited with signal: '
|
|
+ signal.toString()
|
|
+ '. Terminating child process...');
|
|
}
|
|
// Mark shared state so we do not run into a signal/exit loop
|
|
this._exitCalled = true;
|
|
// Use the signal code if it is an error code
|
|
// let correctSignal: NodeJS.Signals | undefined
|
|
if (typeof signal === 'number') {
|
|
if (signal > 0) {
|
|
// code = signal
|
|
signal = 'SIGINT';
|
|
}
|
|
}
|
|
// else {
|
|
// correctSignal = signal
|
|
// }
|
|
// Kill the child process
|
|
proc.kill(signal);
|
|
// Terminate the parent process
|
|
this._terminateProcess(signal);
|
|
}
|
|
};
|
|
for (const signal of SIGNALS_TO_HANDLE) {
|
|
this.terminateSpawnedProcessFuncHandlers[signal] = terminationFunc;
|
|
process.once(signal, this.terminateSpawnedProcessFuncHandlers[signal]);
|
|
}
|
|
this.terminateSpawnedProcessFuncExitHandler = terminationFunc;
|
|
process.once('exit', this.terminateSpawnedProcessFuncExitHandler);
|
|
// Terminate parent process if child process receives termination events
|
|
proc.on('exit', (code, signal) => {
|
|
this._removeProcessListeners();
|
|
if (!this._exitCalled) {
|
|
if (this.verbose) {
|
|
console.info(`Child process exited with code: ${(code ?? '').toString()} and signal:`
|
|
+ (signal ?? '').toString()
|
|
+ '. Terminating parent process...');
|
|
}
|
|
// Mark shared state so we do not run into a signal/exit loop
|
|
this._exitCalled = true;
|
|
// Use the signal code if it is an error code
|
|
let correctSignal;
|
|
if (typeof signal === 'number') {
|
|
if (signal > (code ?? 0)) {
|
|
code = signal;
|
|
correctSignal = 'SIGINT';
|
|
}
|
|
}
|
|
else {
|
|
correctSignal = signal ?? undefined;
|
|
}
|
|
// Terminate the parent process
|
|
this._terminateProcess(correctSignal ?? code);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Enables catching of unhandled exceptions
|
|
*/
|
|
handleUncaughtExceptions() {
|
|
process.on('uncaughtException', (e) => {
|
|
this._uncaughtExceptionHandler(e);
|
|
});
|
|
}
|
|
/**
|
|
* Terminate parent process helper
|
|
*/
|
|
_terminateProcess(signal) {
|
|
if (signal != null) {
|
|
if (typeof signal === 'string') {
|
|
process.kill(process.pid, signal);
|
|
return;
|
|
}
|
|
if (typeof signal === 'number') {
|
|
process.exit(signal);
|
|
return;
|
|
}
|
|
}
|
|
throw new Error('Unable to terminate parent process successfully');
|
|
}
|
|
/**
|
|
* Exit event listener clean up helper
|
|
*/
|
|
_removeProcessListeners() {
|
|
SIGNALS_TO_HANDLE.forEach((signal) => {
|
|
process.removeListener(signal, this.terminateSpawnedProcessFuncHandlers[signal]);
|
|
});
|
|
if (this.terminateSpawnedProcessFuncExitHandler != null) {
|
|
process.removeListener('exit', this.terminateSpawnedProcessFuncExitHandler);
|
|
}
|
|
}
|
|
/**
|
|
* General exception handler
|
|
*/
|
|
_uncaughtExceptionHandler(e) {
|
|
console.error(e.message);
|
|
process.exit(1);
|
|
}
|
|
}
|