var util = require('util') var cp = require('child_process') var Promise = require('bluebird') var log = require('./logger').createLogger('util:procutil') function ExitError(code) { Error.call(this) this.name = 'ExitError' this.code = code this.message = util.format('Exit code "%d"', code) Error.captureStackTrace(this, ExitError) } util.inherits(ExitError, Error) // Export module.exports.ExitError = ExitError // Export module.exports.fork = function(filename, args) { log.info('Forking "%s %s"', filename, args.join(' ')) var resolver = Promise.defer() var proc = cp.fork.apply(cp, arguments) function sigintListener() { proc.kill('SIGINT') } function sigtermListener() { proc.kill('SIGTERM') } process.on('SIGINT', sigintListener) process.on('SIGTERM', sigtermListener) proc.on('error', function(err) { resolver.reject(err) proc.kill() }) proc.on('exit', function(code, signal) { if (signal) { resolver.resolve(code) } else if (code > 0 && code !== 130 && code !== 143) { resolver.reject(new ExitError(code)) } else { resolver.resolve(code) } }) return resolver.promise.cancellable() .finally(function() { process.removeListener('SIGINT', sigintListener) process.removeListener('SIGTERM', sigtermListener) }) .catch(Promise.CancellationError, function() { return new Promise(function(resolve) { proc.on('exit', function() { resolve() }) proc.kill() }) }) } // Export module.exports.gracefullyKill = function(proc, timeout) { function killer(signal) { var deferred = Promise.defer() function onExit() { deferred.resolve() } proc.once('exit', onExit) proc.kill(signal) return deferred.promise.finally(function() { proc.removeListener('exit', onExit) }) } return killer('SIGTERM') .timeout(timeout) .catch(function() { return killer('SIGKILL') .timeout(timeout) }) }