blob: eff1f6067a41d1e6b57bee25ab535546c8003e4d [file] [log] [blame]
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)
})
}