-
Notifications
You must be signed in to change notification settings - Fork 10
/
node-cleanup.js
148 lines (114 loc) · 5.7 KB
/
node-cleanup.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/******************************************************************************
nodeCleanup() installs functions -- cleanup handlers -- that perform cleanup activities just before the node process exits, except on SIGKILL, which can't be intercepted. nodeCleanup() can also install messages to be written to stderr on either SIGINT or an uncaught exception.
Each cleanup handler has the following (FlowType) signature:
cleanupHandler(exitCode: number|null, signal: string|null): boolean?
If the process is terminating for a reason other than a signal, exitCode is an integer that provides the reason for termination, and signal is null. If the process received a POSIX signal, signal is the signal's string name, and exitCode is null. These are also the arguments passed to a process' `exit` event handler, mirrored in node-cleanup for consistency.
The process terminates after cleanup, except possibly on signals. If any cleanup handler returns a boolean false for a signal, the process will not exit; otherwise the process exits. SIGKILL cannot be intercepted.
Install a cleanup handler as follows:
var nodeCleanup = require('node-cleanup');
nodeCleanup(cleanupHandler, stderrMessages);
Or to only install stderr messages:
nodeCleanup(stderrMessages);
Or to install the default stderr messages:
nodeCleanup();
nodeCleanup() may be called multiple times to install multiple cleanup handlers. However, only the most recently installed stderr messages get used. The messages available are ctrl_C and uncaughtException.
The following uninstalls all cleanup handlers and may be called multiple times in succession:
nodeCleanup.uninstall();
This module has its origin in code by @CanyonCasa at http://stackoverflow.com/a/21947851/650894, but the module was significantly rewritten to resolve issues raised by @Banjocat at http://stackoverflow.com/questions/14031763/doing-a-cleanup-action-just-before-node-js-exits#comment68567869_21947851. It has also been extended for greater configurability.
******************************************************************************/
//// CONSTANTS ////////////////////////////////////////////////////////////////
var DEFAULT_MESSAGES = {
ctrl_C: '[ctrl-C]',
uncaughtException: 'Uncaught exception...'
};
//// CONFIGURATION ////////////////////////////////////////////////////////////
var cleanupHandlers = null; // array of cleanup handlers to call
var messages = null; // messages to write to stderr
var sigintHandler; // POSIX signal handlers
var sighupHandler;
var sigquitHandler;
var sigtermHandler;
//// HANDLERS /////////////////////////////////////////////////////////////////
function signalHandler(signal)
{
var exit = true;
cleanupHandlers.forEach(function (cleanup) {
if (cleanup(null, signal) === false)
exit = false;
});
if (exit) {
if (signal === 'SIGINT' && messages && messages.ctrl_C !== '')
process.stderr.write(messages.ctrl_C + "\n");
uninstall(); // don't cleanup again
// necessary to communicate the signal to the parent process
process.kill(process.pid, signal);
}
}
function exceptionHandler(e)
{
if (messages && messages.uncaughtException !== '')
process.stderr.write(messages.uncaughtException + "\n");
process.stderr.write(e.stack + "\n");
process.exit(1); // will call exitHandler() for cleanup
}
function exitHandler(exitCode, signal)
{
cleanupHandlers.forEach(function (cleanup) {
cleanup(exitCode, signal);
});
}
//// MAIN /////////////////////////////////////////////////////////////////////
function install(cleanupHandler, stderrMessages)
{
if (cleanupHandler) {
if (typeof cleanupHandler === 'object') {
stderrMessages = cleanupHandler;
cleanupHandler = null;
}
}
else if (!stderrMessages)
stderrMessages = DEFAULT_MESSAGES;
if (stderrMessages) {
if (messages === null)
messages = { ctrl_C: '', uncaughtException: '' };
if (typeof stderrMessages.ctrl_C === 'string')
messages.ctrl_C = stderrMessages.ctrl_C;
if (typeof stderrMessages.uncaughtException === 'string')
messages.uncaughtException = stderrMessages.uncaughtException;
}
if (cleanupHandlers === null) {
cleanupHandlers = []; // establish before installing handlers
sigintHandler = signalHandler.bind(this, 'SIGINT');
sighupHandler = signalHandler.bind(this, 'SIGHUP');
sigquitHandler = signalHandler.bind(this, 'SIGQUIT');
sigtermHandler = signalHandler.bind(this, 'SIGTERM');
process.on('SIGINT', sigintHandler);
process.on('SIGHUP', sighupHandler);
process.on('SIGQUIT', sigquitHandler);
process.on('SIGTERM', sigtermHandler);
process.on('uncaughtException', exceptionHandler);
process.on('exit', exitHandler);
cleanupHandlers.push(cleanupHandler || noCleanup);
}
else if (cleanupHandler)
cleanupHandlers.push(cleanupHandler);
}
function uninstall()
{
if (cleanupHandlers !== null) {
process.removeListener('SIGINT', sigintHandler);
process.removeListener('SIGHUP', sighupHandler);
process.removeListener('SIGQUIT', sigquitHandler);
process.removeListener('SIGTERM', sigtermHandler);
process.removeListener('uncaughtException', exceptionHandler);
process.removeListener('exit', exitHandler);
cleanupHandlers = null; // null only after uninstalling
}
}
function noCleanup()
{
return true; // signals will always terminate process
}
//// EXPORTS //////////////////////////////////////////////////////////////////
module.exports = install;
install.uninstall = uninstall;