daemonize3 v1.0.2
daemonize3
daemonize3 is a NodeJS module, which lets one turn a normal node application (app) into a daemon easily, so that it detaches from the controlling terminal and continues to in the background. Furthermore if needed, one may even run it in the foreground by just setting the corresponding option, which makes troubleshooting much easier (e.g. if the app doesn't start properly).
The 'Runner' does this by spawning a wrapper, which in turn just runs the given app (more or less like require("app.js")) and redirects per default its output to /dev/null, however, it can be configured to redirect its stdout and/or stderr to a stream of your choice by just setting the appropriate option(s). Also the wrapper feeds information about the clients state back to its 'Runner', so that it knows, that the client is actually running (or not) and can exit with the exit code obtained via wrapper's feedback and thus e.g. follows the simple fork-exec-model prefered by the Solaris service management facilities (SMF).
Dueto the async nature of most apps the wrapper can't really tell from its perspective, whether the app is finally runnning or still in its "starting/configure" phase. The app may help it by just emmitting a 'daemon_running
' event (optional with a short info message attached), e.g. when the app's http listener actually started listening for http requests. If it doesn't do it, a timer fires this event after the configured period of time. On this event the wrapper sends the corresponding notification incl. the process.exitCode
currently set to its runner and closes all additional communication channels, removes related listeners.
To customize the wrappers start as needed, the following options are supported:
option | type | decription |
---|---|---|
main | string | The name of the JS file, which contains the app to run as daemon. Needs to be in the ${CWD} or resolvable via node's module lookup algorithm. |
name | string | The short name of the application, which can be used as title as well. Default: daemon |
pidfile | string | Pidfile to use. Only required, if you wanna use the runner for stopping it as well. Default: autogenerated if started on tty, none otherwise. |
umask | number | umask to set for the daemon. Default: inherit |
timeout | number | timeout to use to wait for a feedback of the app after it has been started (seconds). Default: 30 |
foreground | boolean | Don't start the app as daemon but in foreground. Offen useful for troubleshooting, to see why the daemon does not start properly. Default: false |
execArgv | array | Same as for ChildProcess.spawn(...), i.e. options to the node binary itself. Default: inherit |
args | array | Same as for ChildProcess.spawn(...), i.e. options and operands for the main app itself. Default: inherit |
cwd | string | Same as for ChildProcess.spawn(...). Default: inherit |
env | array | Same as for ChildProcess.spawn(...). Default: inherit |
stdout | string,stream | The stdin parameter for the stdio vector of the options object for ChildProcess.spawn(...). Should be 'ignore' aka /dev/null or a stream, otherwise the runner may notwork as expected. Default: 'ignore' |
stderr | string,stream | The stdout parameter for the stdio vector of the options object for ChildProcess.spawn(...). Should be 'ignore' aka /dev/null or a stream, otherwise the runner may notwork as expected. Default: 'ignore' |
uid | number | The uid the daemon should use. Note that this requires add. privileges - see Process.setuid(). Default: inherit |
gid | number | The gid the daemon should use. Note that this requires add. privileges - see Process.setgid(). Default: inherit |
detached | boolean | If true, make the daemon a process leader w/o a controlling terminal, i.e. call setsid(2). Otherwise honored on Windows only: makes sure, that the daemon gets killed with its parent aka runner. Ignored. Default: !foreground |
Example
application.js
(the main application as is):
var port = process.argv[2] || 3000;
var server = require('http').createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('okay');
});
server.on('error', function(e) {
if (e.code == 'EADDRINUSE') {
console.error('Unable to listen on port %d (port already in use)',port);
process.exitCode = 95;
} else if (e.code == 'EACCES') {
console.error('Unable to listen on port %d (permission denied)',port);
process.exitCode = 100;
} else {
console.error('An unexpected error occured (%s)', e.code);
process.exitCode = 94;
}
});
server.on('listening', function() {
console.log('Listening on port ' + port);
process.emit('daemon_running', 'Listening on port ' + port);
});
server.on('close', function() {
console.log('Stopping service on port %d ...', port);
});
server.listen(port);
run.js
(the little, trivial helper to run the daemon):
#!/usr/bin/env node
var arg = process.argv[2];
runner = new require('daemonize3').Runner({
name: 'My http service',
main: 'application.js',
args: [ (!!arg) && arg.search(/^[0-9]+$/) != -1 ? arg : 52768 ],
foreground: (!!arg) && typeof arg !== 'number'
});
if (arg == 'stop') {
runner.stop();
} else {
runner.startDaemon();
}
So you may try out the following commands to explore the example further:
test start/stop run.js run.js stop
force an error on the 2nd invocation run.js run.js * run.js stop
force a permission denied error * run.js 80
run the app not as a daemon * run.js fg
last but not least, comment out the
process.emit(....)
line inapplication.js
and watch out, what happens now (the timeout is used to draw the assumption, that the app is running, if no error occures).