import { promisify } from 'util' import { spawn, exec } from 'child_process' const execPromise = promisify(exec) export default function kill(pid, signal) { let pids = new Set([pid]) let getSpawn = null let getPids = null switch (process.platform) { case 'win32': return execPromise('taskkill /pid ' + pid + ' /T /F').then(() => pids) case 'darwin': getSpawn = function(parentPid) { return spawn('pgrep', ['-P', parentPid]) } getPids = function(data) { return data.match(/\d+/g).map(Number) } break default: getSpawn = function (parentPid) { return exec('ps -opid="" -oppid="" | grep ' + parentPid) } getPids = function(data, parentPid) { let output = data.trim().split('\n') return output.map(line => { let [child, parent] = line.trim().split(/ +/) if (Number(parent) === parentPid) { return Number(child) } return 0 }).filter(x => x) } break } return buildTree(pids, getSpawn, getPids, pid) .then(function() { for (let pid of pids) { try { process.kill(pid, signal) } catch (err) { if (err.code !== 'ESRCH') throw err; } } return pids }) } function buildTree(allPids, spawnGetChildren, spawnGetPids, parentPid) { allPids.add(parentPid) let ps = spawnGetChildren(parentPid) let data = '' let err = '' ps.stdout.on('data', function(buf) { data += buf.toString('ascii') }) ps.stderr.on('data', function(buf) { err += buf.toString('ascii') }) return new Promise(function(res, rej) { ps.on('close', function(code) { // Check if ps errored out if (code !== 0 && err.trim()) { return rej(new Error('Error running ps to kill processes:\n\t' + err)) } // Check if we otherwise got an error code (usually means empty results) if (code !== 0 || !data.trim()) return res() let pids = spawnGetPids(data, parentPid) res(Promise.all( pids.filter(pid => pid && !allPids.has(pid)) .map(buildTree.bind(this, allPids, spawnGetChildren, spawnGetPids)) )) }) }) }