sazze/node-pm

View on GitHub
bin/node-pm

Summary

Maintainability
Test Coverage
#!/usr/bin/env node

/**
 * The command line entry point
 *
 * @author Kevin Smithson <ksmithson@sazze.com>
 * @author Craig Thayer <cthayer@sazze.com>
 */

var pm = require('..');
var optimist = require('optimist');
var config = require('../lib/config');
var cp = require('child_process');
var path = require('path');
var tty = require('tty');

var CWD = process.cwd();

var options = optimist
  .usage('Run a node app in style\n\nUsage: $0 [app start script]\n\nVersion: ' + require('../package.json').version + '\nGit repo: ' + require('../package.json').repository.url)
  .boolean('s').alias('s', 'silent').default('s', false).describe('s', 'silence workers')
  .boolean('v').default('v', []).describe('v', 'show more verbose output.  most verbose output: -vvv')
  .boolean('sticky').default('sticky', false).describe('sticky', 'Have sticky connections, uses the same worker')
  .alias('n', 'numProc').default('n', 0).describe('n', 'number of workers to spawn')
  .default('tStart', 30000).describe('tStart', 'max number of milliseconds to wait for worker to start')
  .default('tStop', 30000).describe('tStop', 'max number of milliseconds to wait for worker to stop')
  .default('tMaxAge', 1800000).describe('tMaxAge', 'max number of milliseconds a worker will live')
  .default('workerMessageHandler', '').describe('workerMessageHandler', 'the path to the module to load to handle inter-process communication between workers.  The module should export a function with signature: function(message, handle, worker)')
  .default('pidFile', path.resolve(path.join(CWD, 'daemon.pid'))).describe('pidFile', 'the file to write the process id to when running as a daemon')
  .boolean('d').alias('d', 'daemon').default('d', false).describe('d', 'run the node app as a daemon')
  .alias('u', 'user').default('u', '').describe('u', 'the user to run as')
  .alias('g', 'group').default('g', '').describe('g', 'the group to run as')
  .demand(1).argv;

//format timeout options properly
options.timeouts = {
  start: parseInt(options.tStart) > 0 ? options.tStart : undefined,
  stop: parseInt(options.tStop) > 0 ? options.tStop : undefined,
  maxAge: parseInt(options.tMaxAge) > 0 ? options.tMaxAge : undefined
};

delete options.tStart;
delete options.tStop;
delete options.tMaxAge;

options.CWD = CWD;

if (options.d) {
  // run as a daemon
  var childSTDOUT = tty.isatty(1) ? 'ignore' : process.stdout;
  var childSTDERR = tty.isatty(2) ? 'ignore' : process.stderr;

  var childOptions = {
    cwd: CWD,
    env: process.env,
    detached: true,
    stdio: ['ignore', childSTDOUT, childSTDERR, 'ipc'],
    uid: process.getuid(),
    gid: process.getgid()
  };

  process.umask(0);

  var childArgs = [].concat(process.execArgv);

  if (process.env.NODE_EXEC_ARGV) {
    childArgs = childArgs.concat(process.env.NODE_EXEC_ARGV.split(','));
  }

  childArgs.push(path.resolve(path.join(__dirname, '..', 'lib', 'node-pm')));

  var child = cp.spawn('node', childArgs, childOptions);

  child.on('message', function (message) {
    if (message == 'options') {
      child.send(JSON.stringify(options));
    }

    if (message == 'pid') {
      child.send(process.pid);
    }
  });

  child.on('error', function (err) {
    console.log('Failed to daemonize');
  });

  child.on('close', function (code) {
    console.log('daemon exited with code: ' + code);
  });
} else {
  if (process.env.NODE_EXEC_ARGV) {
    // attempt to honor the V8 options specified in NODE_EXEC_ARGV environment variable
    // since we're not running in a daemon, it would be better to pass these directly to node:
    //
    // node --harmony node-pm -n 1 app.js

    var v8 = require('v8');

    var nodeArgs = process.env.NODE_EXEC_ARGV.split(',');

    nodeArgs.map(function (arg) {
      v8.setFlagsFromString(arg);
      process.execArgv.push(arg);
    });
  }

  pm.start(options);
}