Commit b4a65be8 authored by Sruthi Chandran's avatar Sruthi Chandran

Import Upstream version 3.0.1

parents
node_modules
.DS_Store
nyc_output
coverage
.nyc_output
sudo: false
language: node_js
node_js:
- '0.12'
- '4'
- '5'
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="3.0.1"></a>
## [3.0.1](https://github.com/tapjs/signal-exit/compare/v3.0.0...v3.0.1) (2016-09-08)
### Bug Fixes
* do not listen on SIGBUS, SIGFPE, SIGSEGV and SIGILL ([#40](https://github.com/tapjs/signal-exit/issues/40)) ([5b105fb](https://github.com/tapjs/signal-exit/commit/5b105fb))
<a name="3.0.0"></a>
# [3.0.0](https://github.com/tapjs/signal-exit/compare/v2.1.2...v3.0.0) (2016-06-13)
### Bug Fixes
* get our test suite running on Windows ([#23](https://github.com/tapjs/signal-exit/issues/23)) ([6f3eda8](https://github.com/tapjs/signal-exit/commit/6f3eda8))
* hooking SIGPROF was interfering with profilers see [#21](https://github.com/tapjs/signal-exit/issues/21) ([#24](https://github.com/tapjs/signal-exit/issues/24)) ([1248a4c](https://github.com/tapjs/signal-exit/commit/1248a4c))
### BREAKING CHANGES
* signal-exit no longer wires into SIGPROF
Copyright (c) 2015, Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# signal-exit
[![Build Status](https://travis-ci.org/tapjs/signal-exit.png)](https://travis-ci.org/tapjs/signal-exit)
[![Coverage](https://coveralls.io/repos/tapjs/signal-exit/badge.svg?branch=master)](https://coveralls.io/r/tapjs/signal-exit?branch=master)
[![NPM version](https://img.shields.io/npm/v/signal-exit.svg)](https://www.npmjs.com/package/signal-exit)
[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/signal-exit/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/signal-exit)
[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version)
When you want to fire an event no matter how a process exits:
* reaching the end of execution.
* explicitly having `process.exit(code)` called.
* having `process.kill(pid, sig)` called.
* receiving a fatal signal from outside the process
Use `signal-exit`.
```js
var onExit = require('signal-exit')
onExit(function (code, signal) {
console.log('process exited!')
})
```
## 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.
environment:
matrix:
- nodejs_version: '5'
- nodejs_version: '4'
- nodejs_version: '0.12'
install:
- ps: Install-Product node $env:nodejs_version
- set CI=true
- npm -g install npm@latest
- set PATH=%APPDATA%\npm;%PATH%
- npm install
matrix:
fast_finish: true
build: off
version: '{build}'
shallow_clone: true
clone_depth: 1
test_script:
- npm test
// 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')
if (loaded === false) {
load()
}
var ev = 'exit'
if (opts && opts.alwaysLast) {
ev = 'afterexit'
}
var remove = function () {
emitter.removeListener(ev, cb)
if (emitter.listeners('exit').length === 0 &&
emitter.listeners('afterexit').length === 0) {
unload()
}
}
emitter.on(ev, cb)
return remove
}
module.exports.unload = unload
function unload () {
if (!loaded) {
return
}
loaded = false
signals.forEach(function (sig) {
try {
process.removeListener(sig, sigListeners[sig])
} catch (er) {}
})
process.emit = originalProcessEmit
process.reallyExit = originalProcessReallyExit
emitter.count -= 1
}
function emit (event, code, signal) {
if (emitter.emitted[event]) {
return
}
emitter.emitted[event] = true
emitter.emit(event, code, signal)
}
// { <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)
}
}
})
module.exports.signals = function () {
return signals
}
module.exports.load = load
var loaded = false
function load () {
if (loaded) {
return
}
loaded = true
// 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.emit = processEmit
process.reallyExit = processReallyExit
}
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 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": "3.0.1",
"description": "when you want to fire an event no matter how a process exits.",
"main": "index.js",
"scripts": {
"pretest": "standard",
"test": "tap --timeout=240 ./test/*.js --cov",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"release": "standard-version"
},
"files": [
"index.js",
"signals.js"
],
"repository": {
"type": "git",
"url": "https://github.com/tapjs/signal-exit.git"
},
"keywords": [
"signal",
"exit"
],
"author": "Ben Coe <ben@npmjs.com>",
"license": "ISC",
"bugs": {
"url": "https://github.com/tapjs/signal-exit/issues"
},
"homepage": "https://github.com/tapjs/signal-exit",
"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^2.11.10",
"nyc": "^7.0.0",
"standard": "^7.1.2",
"standard-version": "^2.3.0",
"tap": "^6.2.0"
}
}
// This is not the set of all possible signals.
//
// It IS, however, the set of all signals that trigger
// an exit on either Linux or BSD systems. Linux is a
// superset of the signal names supported on BSD, and
// the unknown signals just fail to register, so we can
// catch that easily enough.
//
// Don't bother with SIGKILL. It's uncatchable, which
// means that we can't fire any callbacks anyway.
//
// If a user does happen to register a handler on a non-
// fatal signal like SIGWINCH or something, and then
// exit, it'll end up firing `process.emit('exit')`, so
// the handler will be fired anyway.
//
// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
// artificially, inherently leave the process in a
// state from which it is not safe to try and enter JS
// listeners.
module.exports = [
'SIGABRT',
'SIGALRM',
'SIGHUP',
'SIGINT',
'SIGTERM'
]
if (process.platform !== 'win32') {
module.exports.push(
'SIGVTALRM',
'SIGXCPU',
'SIGXFSZ',
'SIGUSR2',
'SIGTRAP',
'SIGSYS',
'SIGQUIT',
'SIGIOT'
// should detect profiler and enable/disable accordingly.
// see #21
// 'SIGPROF'
)
}
if (process.platform === 'linux') {
module.exports.push(
'SIGIO',
'SIGPOLL',
'SIGPWR',
'SIGSTKFLT',
'SIGUNUSED'
)
}
/* global describe, it */
var exec = require('child_process').exec
var assert = require('assert')
var isWindows = process.platform === 'win32'
var shell = isWindows ? null : { shell: '/bin/bash' }
var node = isWindows ? '"' + process.execPath + '"' : process.execPath
require('chai').should()
require('tap').mochaGlobals()
var onSignalExit = require('../')
describe('all-signals-integration-test', function () {
// These are signals that are aliases for other signals, so
// the result will sometimes be one of the others. For these,
// we just verify that we GOT a signal, not what it is.
function weirdSignal (sig) {
return sig === 'SIGIOT' ||
sig === 'SIGIO' ||
sig === 'SIGSYS' ||
sig === 'SIGIOT' ||
sig === 'SIGABRT' ||
sig === 'SIGPOLL' ||
sig === 'SIGUNUSED'
}
// Exhaustively test every signal, and a few numbers.
// signal-exit does not currently support process.kill()
// on win32.
var signals = isWindows ? [] : onSignalExit.signals()
signals.concat('', 0, 1, 2, 3, 54).forEach(function (sig) {
var js = require.resolve('./fixtures/exiter.js')
it('exits properly: ' + sig, function (done) {
// issues with SIGUSR1 on Node 0.10.x
if (process.version.match(/^v0\.10\./) && sig === 'SIGUSR1') return done()
var cmd = node + ' ' + js + ' ' + sig
exec(cmd, shell, function (err, stdout, stderr) {
if (sig) {
if (!isWindows) assert(err)
if (!isNaN(sig)) {
if (!isWindows) assert.equal(err.code, sig)
} else if (!weirdSignal(sig)) {
if (!isWindows) err.signal.should.equal(sig)
} else if (sig) {
if (!isWindows) assert(err.signal)
}
} else {
assert.ifError(err)
}
try {
var data = JSON.parse(stdout)
} catch (er) {
console.error('invalid json: %j', stdout, stderr)
throw er
}
if (weirdSignal(sig)) {
data.wanted[1] = true
data.found[1] = !!data.found[1]
}
assert.deepEqual(data.found, data.wanted)
done()
})
})
})
signals.forEach(function (sig) {
var js = require.resolve('./fixtures/parent.js')
it('exits properly: (external sig) ' + sig, function (done) {
// issues with SIGUSR1 on Node 0.10.x
if (process.version.match(/^v0\.10\./) && sig === 'SIGUSR1') return done()
var cmd = node + ' ' + js + ' ' + sig
exec(cmd, shell, function (err, stdout, stderr) {
assert.ifError(err)
try {
var data = JSON.parse(stdout)
} catch (er) {
console.error('invalid json: %j', stdout, stderr)
throw er
}
if (weirdSignal(sig)) {
data.wanted[1] = true
data.found[1] = !!data.found[1]
data.external[1] = !!data.external[1]
}
assert.deepEqual(data.found, data.wanted)
assert.deepEqual(data.external, data.wanted)
done()
})
})
})
})
var expectSignal = process.argv[2]
if (!expectSignal || !isNaN(expectSignal)) {
throw new Error('signal not provided')
}
var onSignalExit = require('../../')
onSignalExit(function (code, signal) {
// some signals don't always get recognized properly, because
// they have the same numeric code.
if (wanted[1] === true) {
signal = !!signal
}
console.log('%j', {
found: [ code, signal ],
wanted: wanted
})
})
var wanted
switch (expectSignal) {
case 'SIGIOT':
case 'SIGUNUSED':
case 'SIGPOLL':
wanted = [ null, true ]
break
default:
wanted = [ null, expectSignal ]
break
}
console.error('want', wanted)
setTimeout(function () {}, 1000)
This diff is collapsed.
var join = require('path').join
if (process.argv.length === 2) {
var types = [ 'explicit', 'code', 'normal' ]
var codes = [ 0, 2, 'null' ]
var changes = [ 'nochange', 'change', 'code', 'twice', 'twicecode' ]
var handlers = [ 'sigexit', 'nosigexit' ]
var opts = []
types.forEach(function (type) {
var testCodes = type === 'normal' ? [ 0 ] : codes
testCodes.forEach(function (code) {
changes.forEach(function (change) {
handlers.forEach(function (handler) {
opts.push([type, code, change, handler].join(' '))
})
})
})
})
var results = {}
var exec = require('child_process').exec
run(opts.shift())
} else {
var type = process.argv[2]
var code = +process.argv[3]
var change = process.argv[4]
var sigexit = process.argv[5] !== 'nosigexit'
if (sigexit) {
var onSignalExit = require('../../')
onSignalExit(listener)
} else {
process.on('exit', listener)
}
process.on('exit', function (code) {
console.error('first code=%j', code)
})
if (change !== 'nochange') {
process.once('exit', function (code) {
console.error('set code from %j to %j', code, 5)
if (change === 'code' || change === 'twicecode') {
process.exitCode = 5
} else {
process.exit(5)
}
})
if (change === 'twicecode' || change === 'twice') {
process.once('exit', function (code) {
code = process.exitCode || code
console.error('set code from %j to %j', code, code + 1)
process.exit(code + 1)
})
}
}
process.on('exit', function (code) {
console.error('second code=%j', code)
})
if (type === 'explicit') {
if (code || code === 0) {
process.exit(code)
} else {
process.exit()
}
} else if (type === 'code') {
process.exitCode = +code || 0
}
}
function listener (code, signal) {
signal = signal || null
console.log('%j', { code: code, signal: signal, exitCode: process.exitCode || 0 })
}
function run (opt) {
console.error(opt)
var shell = process.platform === 'win32' ? null : { shell: '/bin/bash' }
exec(join(process.execPath, ' ', __filename, ' ' + opt), shell, function (err, stdout, stderr) {
var res = JSON.parse(stdout)
if (err) {
res.actualCode = err.code
res.actualSignal = err.signal
} else {
res.actualCode = 0
res.actualSignal = null
}
res.stderr = stderr.trim().split('\n')
results[opt] = res
if (opts.length) {
run(opts.shift())
} else {
console.log(JSON.stringify(results, null, 2))
}
})
}
var onSignalExit = require('../../')
onSignalExit(function (code, signal) {
console.log('reached end of execution, ' + code + ', ' + signal)
})
var onSignalExit = require('../../')
var counter = 0
onSignalExit(function (code, signal) {
counter++
console.log('last counter=%j, code=%j, signal=%j',
counter, code, signal)
}, {alwaysLast: true})
onSignalExit(function (code, signal) {
counter++
console.log('first counter=%j, code=%j, signal=%j',
counter, code, signal)
})
var onSignalExit = require('../../')
onSignalExit(function (code, signal) {
console.log('exited with process.exit(), ' + code + ', ' + signal)
})
process.exit(32)
var exit = process.argv[2] || 0
var onSignalExit = require('../../')
onSignalExit(function (code, signal) {
// some signals don't always get recognized properly, because
// they have the same numeric code.
if (wanted[1] === true) {
signal = !!signal
}
console.log('%j', {
found: [ code, signal ],
wanted: wanted
})
})
var wanted
if (isNaN(exit)) {
switch (exit) {
case 'SIGIOT':
case 'SIGUNUSED':
case 'SIGPOLL':
wanted = [ null, true ]
break
default:
wanted = [ null, exit ]
break
}
try {
process.kill(process.pid, exit)
setTimeout(function () {}, 1000)
} catch (er) {
wanted = [ 0, null ]
}
} else {
exit = +exit
wanted = [ exit, null ]
// If it's explicitly requested 0, then explicitly call it.
// "no arg" = "exit naturally"
if (exit || process.argv[2]) {
process.exit(exit)
}
}
// just be silly with calling these functions a bunch
// mostly just to get coverage of the guard branches
var onSignalExit = require('../../')
onSignalExit.load()
onSignalExit.load()
onSignalExit.unload()
onSignalExit.unload()
// simulate cases where the module could be loaded from multiple places
var onSignalExit = require('../../')
var counter = 0
onSignalExit(function (code, signal) {
counter++
console.log('last counter=%j, code=%j, signal=%j',
counter, code, signal)
}, {alwaysLast: true})
onSignalExit(function (code, signal) {
counter++
console.log('first counter=%j, code=%j, signal=%j',
counter, code, signal)
})
delete require('module')._cache[require.resolve('../../')]
onSignalExit = require('../../')
onSignalExit(function (code, signal) {
counter++
console.log('last counter=%j, code=%j, signal=%j',
counter, code, signal)
}, {alwaysLast: true})
onSignalExit(function (code, signal) {
counter++
console.log('first counter=%j, code=%j, signal=%j',
counter, code, signal)
})
// Lastly, some that should NOT be shown
delete require('module')._cache[require.resolve('../../')]
onSignalExit = require('../../')
var unwrap = onSignalExit(function (code, signal) {
counter++
console.log('last counter=%j, code=%j, signal=%j',
counter, code, signal)
}, {alwaysLast: true})
unwrap()
unwrap = onSignalExit(function (code, signal) {
counter++
console.log('