diff --git a/index.js b/index.js index 0610a581cc..3777e0c9f4 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,10 @@ function handleArgs(command, args, options) { encoding: 'utf8', reject: true, cleanup: true - }, parsed.options, {windowsHide: true}); + }, parsed.options, { + windowsHide: true, + killByPid: false + }); // TODO: Remove in the next major release if (options.stripEof === false) { @@ -68,6 +71,14 @@ function handleArgs(command, args, options) { options.cleanup = false; } + if (process.platform !== 'win32') { + // #96 + // Windows automatically kills every descendents of the child process + // On Linux (MacOS too?) we need to detach the process and kill by `sid` + options.detached = true; + options.killByPid = true; + } + if (process.platform === 'win32' && path.basename(parsed.command) === 'cmd.exe') { // #116 parsed.args.unshift('/q'); @@ -212,11 +223,24 @@ module.exports = (command, args, options) => { return Promise.reject(error); } + let killed = false; + + const killSpawned = signal => { + if (parsed.options.killByPid && spawned) { + // Kills the spawned process and its descendents using the pid range hack + // Source: https://azimi.me/2014/12/31/kill-child_process-node-js.html + process.kill(-spawned.pid, signal); + killed = true; + } else { + spawned.kill(signal); + } + }; + + spawned.kill = killSpawned; + let removeExitHandler; if (parsed.options.cleanup) { - removeExitHandler = onExit(() => { - spawned.kill(); - }); + removeExitHandler = onExit(killSpawned); } let timeoutId = null; @@ -237,7 +261,7 @@ module.exports = (command, args, options) => { timeoutId = setTimeout(() => { timeoutId = null; timedOut = true; - spawned.kill(parsed.options.killSignal); + killSpawned(parsed.options.killSignal); }, parsed.options.timeout); } @@ -289,7 +313,7 @@ module.exports = (command, args, options) => { // TODO: missing some timeout logic for killed // https://github.com/nodejs/node/blob/master/lib/child_process.js#L203 // error.killed = spawned.killed || killed; - error.killed = error.killed || spawned.killed; + error.killed = error.killed || spawned.killed || killed; if (!parsed.options.reject) { return error;