Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

signal-exit

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

signal-exit - npm Package Compare versions

Comparing version 1.3.1 to 2.0.0

signals.js

206

index.js

@@ -0,112 +1,148 @@

// Note: since nyc uses this module to output coverage, any lines
// that are in the direct sync flow of nyc's outputCoverage are
// ignored, since we can never get coverage for them.
var assert = require('assert')
var signals = require('./signals.js')
var EE = require('events')
/* istanbul ignore if */
if (typeof EE !== 'function') {
EE = EE.EventEmitter
}
var emitter
if (process.__signal_exit_emitter__) {
emitter = process.__signal_exit_emitter__
} else {
emitter = process.__signal_exit_emitter__ = new EE()
emitter.count = 0
emitter.emitted = {}
}
module.exports = function (cb, opts) {
assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler')
var emittedExit = false,
listenerMap = {},
listeners = []
if (loaded === false) {
load()
}
opts = opts || {}
opts.minimumListeners = opts.maxListeners || 1
var ev = 'exit'
if (opts && opts.alwaysLast) {
ev = 'afterexit'
}
Object.keys(signals).forEach(function (sig) {
var listener = function () {
// If there are no other listeners, do the default action.
if (process.listeners(sig).length <= opts.maxListeners) {
process.removeListener(sig, listener)
cb(process.exitCode || signals[sig], sig)
process.kill(process.pid, sig)
}
var remove = function () {
emitter.removeListener(ev, cb)
if (emitter.listeners('exit').length === 0 &&
emitter.listeners('afterexit').length === 0) {
unload()
}
}
emitter.on(ev, cb)
listenerMap[sig] = listener
listeners.push(listener)
return remove
}
module.exports.unload = unload
function unload () {
if (!loaded) {
return
}
loaded = false
signals.forEach(function (sig) {
try {
process.on(sig, listener)
process.removeListener(sig, sigListeners[sig])
} catch (er) {}
})
process.emit = originalProcessEmit
process.reallyExit = originalProcessReallyExit
emitter.count -= 1
}
var listener = function (code) {
if (emittedExit) return
emittedExit = true
return cb(code, undefined)
function emit (event, code, signal) {
if (emitter.emitted[event]) {
return
}
emitter.emitted[event] = true
emitter.emit(event, code, signal)
}
listenerMap['exit'] = listener
listeners.push(listener)
process.on('exit', listener)
// { <signal>: <listener fn>, ... }
var sigListeners = {}
signals.forEach(function (sig) {
sigListeners[sig] = function listener () {
// If there are no other listeners, an exit is coming!
// Simplest way: remove us and then re-send the signal.
// We know that this will kill the process, so we can
// safely emit now.
var listeners = process.listeners(sig)
if (listeners.length === emitter.count) {
unload()
emit('exit', null, sig)
/* istanbul ignore next */
emit('afterexit', null, sig)
/* istanbul ignore next */
process.kill(process.pid, sig)
}
}
})
if (opts.alwaysLast) monkeyPatchAddListener(listenerMap, listeners)
module.exports.signals = function () {
return signals
}
// in some cases we always want to ensure that the
// onExit handler registered with signal-exit is
// the last event handler to fire, e.g, for code coverage.
function monkeyPatchAddListener (listenerMap, listeners) {
var events = Object.keys(listenerMap),
listener
module.exports.load = load
process.on = process.addListener = (function (on) {
return function (ev, fn) {
for (var i = 0, event; (event = events[i]) !== undefined; i++) {
listener = listenerMap[event]
var loaded = false
if (ev === event && listeners.indexOf(fn) === -1) {
process.removeListener(ev, listener)
var ret = on.call(process, ev, fn)
on.call(process, ev, listener)
return ret
}
}
function load () {
if (loaded) {
return
}
loaded = true
return on.apply(this, arguments)
// This is the number of onSignalExit's that are in play.
// It's important so that we can count the correct number of
// listeners on signals, and don't wait for the other one to
// handle it instead of us.
emitter.count += 1
signals = signals.filter(function (sig) {
try {
process.on(sig, sigListeners[sig])
return true
} catch (er) {
return false
}
})(process.on)
})
process.emit = processEmit
process.reallyExit = processReallyExit
}
var signals = {
'SIGALRM': 142,
'SIGBUS': 138,
'SIGCLD': undefined,
'SIGEMT': undefined,
'SIGFPE': 136,
'SIGHUP': 129,
'SIGILL': 132,
'SIGINFO': undefined,
'SIGINT': 130, // CTRL^C
'SIGIOT': 134,
'SIGKILL': 137, // can't be caught, but let's keep it here.
'SIGLOST': undefined,
'SIGPIPE': 141,
'SIGPOLL': undefined,
'SIGPROF': 155,
'SIGPWR': undefined,
'SIGQUIT': 131,
'SIGSEGV': 139,
'SIGSTKFLT': undefined,
'SIGSYS': 140,
'SIGTERM': 143, // polite exit code, can be caught.
'SIGTRAP': 133,
'SIGTSTP': 146,
'SIGTTIN': 149,
'SIGTTOU': 150,
'SIGUNUSED': undefined,
'SIGUSR2': 159,
'SIGVTALRM': 154,
'SIGXCPU': 152,
'SIGXFSZ': 153
var originalProcessReallyExit = process.reallyExit
function processReallyExit (code) {
process.exitCode = code || 0
emit('exit', process.exitCode, null)
/* istanbul ignore next */
emit('afterexit', process.exitCode, null)
/* istanbul ignore next */
originalProcessReallyExit.call(process, process.exitCode)
}
/* var nonFatalSignals = [
'SIGWINCH', // resize window.
'SIGUSR1', // debugger.
'SIGCHLD',
'SIGSTOP',
'SIGCONT',
'SIGIO',
'SIGURG',
'SIGABRT',
'SIGURG' // out of band data.
] */
var originalProcessEmit = process.emit
function processEmit (ev, arg) {
if (ev === 'exit') {
if (arg !== undefined) {
process.exitCode = arg
}
var ret = originalProcessEmit.apply(this, arguments)
emit('exit', process.exitCode, null)
/* istanbul ignore next */
emit('afterexit', process.exitCode, null)
return ret
} else {
return originalProcessEmit.apply(this, arguments)
}
}
{
"name": "signal-exit",
"version": "1.3.1",
"version": "2.0.0",
"description": "when you want to fire an event no matter how a process exits.",
"main": "index.js",
"scripts": {
"test": "standard && nyc tap ./test/*.js",
"test": "standard && nyc tap --timeout=240 ./test/*.js",
"coverage": "nyc report --reporter=text-lcov | coveralls"

@@ -27,6 +27,6 @@ },

"coveralls": "^2.11.2",
"nyc": "^2.0.5",
"nyc": "^2.1.2",
"standard": "^3.9.0",
"tap": "^1.0.4"
"tap": "1.0.4"
}
}

@@ -12,2 +12,3 @@ # signal-exit

* having `process.kill(pid, sig)` called.
* receiving a fatal signal from outside the process

@@ -23,1 +24,17 @@ Use `signal-exit`.

```
## API
`var remove = onExit(function (code, signal) {}, options)`
The return value of the function is a function that will remove the
handler.
Note that the function *only* fires for signals if the signal would
cause the proces to exit. That is, there are no other listeners, and
it is a fatal signal.
## Options
* `alwaysLast`: Run this handler after any other signal or exit
handlers. This causes `process.emit` to be monkeypatched.

@@ -5,4 +5,8 @@ var onSignalExit = require('../../')

console.log('exited with sigint, ' + code + ', ' + signal)
}, {maxListeners: 2})
})
// For some reason, signals appear to not always be fast enough
// to come in before the process exits. Just a few ticks needed.
setTimeout(function () {}, 1000)
process.kill(process.pid, 'SIGINT')

@@ -8,3 +8,3 @@ var onSignalExit = require('../../')

counter, code, signal)
}, {maxListeners: 2, alwaysLast: true})
}, {alwaysLast: true})

@@ -15,4 +15,5 @@ onSignalExit(function (code, signal) {

counter, code, signal)
}, {maxListeners: 3})
})
process.kill(process.pid, 'SIGHUP')
setTimeout(function () {}, 1000)
var onSignalExit = require('../../')
setTimeout(function () {})
var calledListener = 0

@@ -7,3 +9,3 @@ onSignalExit(function (code, signal) {

calledListener, code, signal)
}, {maxListeners: 2})
})

@@ -10,0 +12,0 @@ process.on('SIGHUP', listener)

@@ -5,4 +5,6 @@ var onSignalExit = require('../../')

console.log('exited with sigterm, ' + code + ', ' + signal)
}, {maxListeners: 2})
})
setTimeout(function () {}, 1000)
process.kill(process.pid, 'SIGTERM')

@@ -10,9 +10,8 @@ /* global describe, it */

process.env.NYC_TEST = 'yep'
describe('signal-exit', function () {
describe('signal-exit', function () {
it('receives an exit event when a process exits normally', function (done) {
exec(process.execPath + ' ./test/fixtures/end-of-execution.js', function (err, stdout, stderr) {
expect(err).to.equal(null)
stdout.should.match(/reached end of execution, 0, undefined/)
stdout.should.match(/reached end of execution, 0, null/)
done()

@@ -25,3 +24,3 @@ })

assert(err)
stdout.should.match(/exited with sigint, 130, SIGINT/)
stdout.should.match(/exited with sigint, null, SIGINT/)
done()

@@ -34,3 +33,3 @@ })

assert(err)
stdout.should.match(/exited with sigterm, 143, SIGTERM/)
stdout.should.match(/exited with sigterm, null, SIGTERM/)
done()

@@ -43,3 +42,3 @@ })

err.code.should.equal(32)
stdout.should.match(/exited with process\.exit\(\), 32, undefined/)
stdout.should.match(/exited with process\.exit\(\), 32, null/)
done()

@@ -52,3 +51,3 @@ })

assert(err)
assert.equal(stdout, 'exited calledListener=4, code=129, signal="SIGHUP"\n')
assert.equal(stdout, 'exited calledListener=4, code=null, signal="SIGHUP"\n')
done()

@@ -58,3 +57,3 @@ })

it('ensures that if alwaysLast=true, the handler is run last', function (done) {
it('ensures that if alwaysLast=true, the handler is run last (signal)', function (done) {
exec(process.execPath + ' ./test/fixtures/signal-last.js', function (err, stdout, stderr) {

@@ -67,2 +66,50 @@ assert(err)

})
it('ensures that if alwaysLast=true, the handler is run last (normal exit)', function (done) {
exec(process.execPath + ' ./test/fixtures/exit-last.js', function (err, stdout, stderr) {
assert.ifError(err)
stdout.should.match(/first counter=1/)
stdout.should.match(/last counter=2/)
done()
})
})
it('works when loaded multiple times', function (done) {
exec(process.execPath + ' ./test/fixtures/multiple-load.js', function (err, stdout, stderr) {
assert(err)
stdout.should.match(/first counter=1, code=null, signal="SIGHUP"/)
stdout.should.match(/first counter=2, code=null, signal="SIGHUP"/)
stdout.should.match(/last counter=3, code=null, signal="SIGHUP"/)
stdout.should.match(/last counter=4, code=null, signal="SIGHUP"/)
done()
})
})
// TODO: test on a few non-OSX machines.
it('removes handlers when fully unwrapped', function (done) {
exec(process.execPath + ' ./test/fixtures/unwrap.js', function (err, stdout, stderr) {
// on Travis CI no err.signal is populated but
// err.code is 129 (which I think tends to be SIGHUP).
var expectedCode = process.env.TRAVIS ? 129 : null
assert(err)
if (!process.env.TRAVIS) err.signal.should.equal('SIGHUP')
expect(err.code).to.equal(expectedCode)
done()
})
})
it('does not load() or unload() more than once', function (done) {
exec(process.execPath + ' ./test/fixtures/load-unload.js', function (err, stdout, stderr) {
assert.ifError(err)
done()
})
})
it('handles uncatchable signals with grace and poise', function (done) {
exec(process.execPath + ' ./test/fixtures/sigkill.js', function (err, stdout, stderr) {
assert.ifError(err)
done()
})
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc