1.0.2 • Published 8 years ago

daemonize3 v1.0.2

Weekly downloads
-
License
CDDL-1.0
Repository
-
Last release
8 years ago

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:

optiontypedecription
mainstringThe 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.
namestringThe short name of the application, which can be used as title as well. Default: daemon
pidfilestringPidfile to use. Only required, if you wanna use the runner for stopping it as well. Default: autogenerated if started on tty, none otherwise.
umasknumberumask to set for the daemon. Default: inherit
timeoutnumbertimeout to use to wait for a feedback of the app after it has been started (seconds). Default: 30
foregroundbooleanDon't start the app as daemon but in foreground. Offen useful for troubleshooting, to see why the daemon does not start properly. Default: false
execArgvarraySame as for ChildProcess.spawn(...), i.e. options to the node binary itself. Default: inherit
argsarraySame as for ChildProcess.spawn(...), i.e. options and operands for the main app itself. Default: inherit
cwdstringSame as for ChildProcess.spawn(...). Default: inherit
envarraySame as for ChildProcess.spawn(...). Default: inherit
stdoutstring,streamThe 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'
stderrstring,streamThe 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'
uidnumberThe uid the daemon should use. Note that this requires add. privileges - see Process.setuid(). Default: inherit
gidnumberThe gid the daemon should use. Note that this requires add. privileges - see Process.setgid(). Default: inherit
detachedbooleanIf 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 in application.js and watch out, what happens now (the timeout is used to draw the assumption, that the app is running, if no error occures).