summaryrefslogtreecommitdiff
path: root/node_modules/forever/lib/forever
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/forever/lib/forever')
-rw-r--r--node_modules/forever/lib/forever/cli.js564
-rw-r--r--node_modules/forever/lib/forever/service/adapters/adapter.js96
-rw-r--r--node_modules/forever/lib/forever/service/adapters/initd/index.js9
-rw-r--r--node_modules/forever/lib/forever/service/adapters/systemv/foreverd82
-rw-r--r--node_modules/forever/lib/forever/service/adapters/systemv/index.js199
-rw-r--r--node_modules/forever/lib/forever/service/cli.js102
-rw-r--r--node_modules/forever/lib/forever/service/index.js22
-rw-r--r--node_modules/forever/lib/forever/service/service.js268
-rw-r--r--node_modules/forever/lib/forever/worker.js122
9 files changed, 1464 insertions, 0 deletions
diff --git a/node_modules/forever/lib/forever/cli.js b/node_modules/forever/lib/forever/cli.js
new file mode 100644
index 0000000..01107b2
--- /dev/null
+++ b/node_modules/forever/lib/forever/cli.js
@@ -0,0 +1,564 @@
+/*
+ * cli.js: Handlers for the forever CLI commands.
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var fs = require('fs'),
+ path = require('path'),
+ util = require('util'),
+ colors = require('colors'),
+ cliff = require('cliff'),
+ flatiron = require('flatiron'),
+ forever = require('../forever');
+
+var cli = exports;
+
+var help = [
+ 'usage: forever [action] [options] SCRIPT [script-options]',
+ '',
+ 'Monitors the script specified in the current process or as a daemon',
+ '',
+ 'actions:',
+ ' start Start SCRIPT as a daemon',
+ ' stop Stop the daemon SCRIPT',
+ ' stopall Stop all running forever scripts',
+ ' restart Restart the daemon SCRIPT',
+ ' restartall Restart all running forever scripts',
+ ' list List all running forever scripts',
+ ' config Lists all forever user configuration',
+ ' set <key> <val> Sets the specified forever config <key>',
+ ' clear <key> Clears the specified forever config <key>',
+ ' logs Lists log files for all forever processes',
+ ' logs <script|index> Tails the logs for <script|index>',
+ ' columns add <col> Adds the specified column to the output in `forever list`',
+ ' columns rm <col> Removed the specified column from the output in `forever list`',
+ ' columns set <cols> Set all columns for the output in `forever list`',
+ ' cleanlogs [CAREFUL] Deletes all historical forever log files',
+ '',
+ 'options:',
+ ' -m MAX Only run the specified script MAX times',
+ ' -l LOGFILE Logs the forever output to LOGFILE',
+ ' -o OUTFILE Logs stdout from child script to OUTFILE',
+ ' -e ERRFILE Logs stderr from child script to ERRFILE',
+ ' -p PATH Base path for all forever related filesĀ (pid files, etc.)',
+ ' -c COMMAND COMMAND to execute (defaults to node)',
+ ' -a, --append Append logs',
+ ' -f, --fifo Stream logs to stdout',
+ ' -n, --number Number of log lines to print',
+ ' --pidFile The pid file',
+ ' --sourceDir The source directory for which SCRIPT is relative to',
+ ' --minUptime Minimum uptime (millis) for a script to not be considered "spinning"',
+ ' --spinSleepTime Time to wait (millis) between launches of a spinning script.',
+ ' --colors --no-colors will disable output coloring',
+ ' --plain alias of --no-colors',
+ ' -d, --debug Forces forever to log debug output',
+ ' -v, --verbose Turns on the verbose messages from Forever',
+ ' -s, --silent Run the child script silencing stdout and stderr',
+ ' -w, --watch Watch for file changes',
+ ' --watchDirectory Top-level directory to watch from',
+ ' --watchIgnore To ignore pattern when watch is enabled (multiple option is allowed)',
+ ' -h, --help You\'re staring at it',
+ '',
+ '[Long Running Process]',
+ ' The forever process will continue to run outputting log messages to the console.',
+ ' ex. forever -o out.log -e err.log my-script.js',
+ '',
+ '[Daemon]',
+ ' The forever process will run as a daemon which will make the target process start',
+ ' in the background. This is extremely useful for remote starting simple node.js scripts',
+ ' without using nohup. It is recommended to run start with -o -l, & -e.',
+ ' ex. forever start -l forever.log -o out.log -e err.log my-daemon.js',
+ ' forever stop my-daemon.js',
+ ''
+];
+
+var app = flatiron.app;
+
+var actions = [
+ 'start',
+ 'stop',
+ 'stopall',
+ 'restart',
+ 'restartall',
+ 'list',
+ 'config',
+ 'set',
+ 'clear',
+ 'logs',
+ 'columns',
+ 'cleanlogs'
+];
+
+var argvOptions = cli.argvOptions = {
+ 'command': {alias: 'c'},
+ 'errFile': {alias: 'e'},
+ 'logFile': {alias: 'l'},
+ 'append': {alias: 'a', boolean: true},
+ 'fifo': {alias: 'f', boolean: false},
+ 'number': {alias: 'n'},
+ 'max': {alias: 'm'},
+ 'outFile': {alias: 'o'},
+ 'path': {alias: 'p'},
+ 'help': {alias: 'h'},
+ 'silent': {alias: 's', boolean: true},
+ 'verbose': {alias: 'v', boolean: true},
+ 'watch': {alias: 'w', boolean: true},
+ 'debug': {alias: 'd', boolean: true},
+ 'plain': {boolean: true}
+};
+
+app.use(flatiron.plugins.cli, {
+ argv: argvOptions,
+ usage: help
+});
+
+var reserved = ['root', 'pidPath'];
+
+//
+// ### @private function (file, options, callback)
+// #### @file {string} Target script to start
+// #### @options {Object} Options to start the script with
+// #### @callback {function} Continuation to respond to when complete.
+// Helper function that sets up the pathing for the specified `file`
+// then stats the appropriate files and responds.
+//
+function tryStart(file, options, callback) {
+ var fullLog, fullScript;
+
+ if (options.path) forever.config.set('root', options.path);
+ fullLog = forever.logFilePath(options.logFile, options.uid);
+ fullScript = path.join(options.sourceDir, file);
+
+ forever.stat(fullLog, fullScript, options.append, function (err) {
+ if (err) {
+ forever.log.error('Cannot start forever');
+ forever.log.error(err.message);
+ process.exit(-1);
+ }
+
+ callback();
+ });
+}
+
+//
+// ### @private function updateConfig (updater)
+// #### @updater {function} Function which updates the forever config
+// Helper which runs the specified `updater` and then saves the forever
+// config to `forever.config.get('root')`.
+//
+function updateConfig(updater) {
+ updater();
+ forever.config.save(function (err) {
+ if (err) {
+ return forever.log.error('Error saving config: ' + err.message);
+ }
+
+ cli.config();
+ var configFile = path.join(forever.config.get('root'), 'config.json');
+ forever.log.info('Forever config saved: ' + configFile.yellow);
+ });
+}
+
+//
+// ### @private function checkColumn (name)
+// #### @name {string} Column to check
+// Checks if column `name` exists
+//
+function checkColumn(name) {
+ if (!forever.columns[name]) {
+ forever.log.error('Unknown column: ' + name.magenta);
+ return false;
+ }
+ return true;
+}
+
+//
+// ### function getOptions (file)
+// #### @file {string} File to run. **Optional**
+// Returns `options` object for use with `forever.start` and
+// `forever.startDaemon`
+//
+var getOptions = cli.getOptions = function (file) {
+ var options = {};
+ //
+ // First isolate options which should be passed to file
+ //
+ options.options = process.argv.splice(process.argv.indexOf(file) + 1);
+
+ //
+ // Now we have to force optimist to reparse command line options because
+ // we've removed some before.
+ //
+ app.config.stores.argv.store = {};
+ app.config.use('argv', argvOptions);
+
+ [
+ 'pidFile', 'logFile', 'errFile', 'watch', 'minUptime', 'append',
+ 'silent', 'outFile', 'max', 'command', 'path', 'spinSleepTime',
+ 'sourceDir', 'uid', 'watchDirectory', 'watchIgnore', 'killTree', 'killSignal'
+ ].forEach(function (key) {
+ options[key] = app.config.get(key);
+ });
+
+ options.watchIgnore = options.watchIgnore || [];
+ options.watchIgnorePatterns = !Array.isArray(options.watchIgnore)
+ ? options.watchIgnore
+ : [options.watchIgnore];
+
+ if (!options.minUptime) {
+ forever.log.warn('--minUptime not set. Defaulting to: 1000ms');
+ options.minUptime = 1000;
+ }
+
+ if (!options.spinSleepTime) {
+ forever.log.warn([
+ '--spinSleepTime not set. Your script',
+ 'will exit if it does not stay up for',
+ 'at least ' + options.minUptime + 'ms'
+ ].join(' '));
+ }
+
+ options.sourceDir = options.sourceDir || (file && file[0] !== '/' ? process.cwd() : '/');
+ if (options.sourceDir) {
+ options.spawnWith = {cwd: options.sourceDir};
+ }
+
+ return options;
+}
+
+//
+// ### function cleanLogs
+// Deletes all historical forever log files
+//
+app.cmd('cleanlogs', cli.cleanLogs = function () {
+ forever.log.silly('Tidying ' + forever.config.get('root'));
+ forever.cleanUp(true).on('cleanUp', function () {
+ forever.log.silly(forever.config.get('root') + ' tidied.');
+ });
+});
+
+//
+// ### function start (file)
+// #### @file {string} Location of the script to spawn with forever
+// Starts a forever process for the script located at `file` as daemon
+// process.
+//
+app.cmd(/start (.+)/, cli.startDaemon = function () {
+ var file = app.argv._[1],
+ options = getOptions(file);
+
+ forever.log.info('Forever processing file: ' + file.grey);
+ tryStart(file, options, function () {
+ forever.startDaemon(file, options);
+ });
+});
+
+//
+// ### function stop (file)
+// #### @file {string} Target forever process to stop
+// Stops the forever process specified by `file`.
+//
+app.cmd(/stop (.+)/, cli.stop = function (file) {
+ var runner = forever.stop(file, true);
+
+ runner.on('stop', function (process) {
+ forever.log.info('Forever stopped process:');
+ forever.log.data(process);
+ });
+
+ runner.on('error', function (err) {
+ forever.log.error('Forever cannot find process with index: ' + file);
+ process.exit(1);
+ });
+});
+
+//
+// ### function stopall ()
+// Stops all currently running forever processes.
+//
+app.cmd('stopall', cli.stopall = function () {
+ var runner = forever.stopAll(true);
+ runner.on('stopAll', function (processes) {
+ if (processes) {
+ forever.log.info('Forever stopped processes:');
+ processes.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ });
+ }
+ else {
+ forever.log.info('No forever processes running');
+ }
+ });
+
+ runner.on('error', function () {
+ forever.log.info('No forever processes running');
+ process.exit(1);
+ });
+});
+
+//
+// ### function restartall ()
+// Restarts all currently running forever processes.
+//
+app.cmd('restartall', cli.restartAll = function () {
+ var runner = forever.restartAll(true);
+ runner.on('restartAll', function (processes) {
+ if (processes) {
+ forever.log.info('Forever restarted processes:');
+ processes.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ });
+ }
+ else {
+ forever.log.info('No forever processes running');
+ }
+ });
+
+ runner.on('error', function () {
+ forever.log.info('No forever processes running');
+ });
+});
+
+//
+// ### function restart (file)
+// #### @file {string} Target process to restart
+// Restarts the forever process specified by `file`.
+//
+app.cmd(/restart (.+)/, cli.restart = function (file) {
+ var runner = forever.restart(file, true);
+ runner.on('restart', function (processes) {
+ if (processes) {
+ forever.log.info('Forever restarted process(es):');
+ processes.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ });
+ }
+ else {
+ forever.log.info('No forever processes running');
+ }
+ });
+
+ runner.on('error', function (err) {
+ forever.log.error('Error restarting process: ' + file.grey);
+ forever.log.error(err.message);
+ process.exit(1);
+ });
+});
+
+//
+// ### function list ()
+// Lists all currently running forever processes.
+//
+app.cmd('list', cli.list = function () {
+ forever.list(true, function (err, processes) {
+ if (processes) {
+ forever.log.info('Forever processes running');
+ processes.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ });
+ }
+ else {
+ forever.log.info('No forever processes running');
+ }
+ });
+});
+
+//
+// ### function config ()
+// Lists all of the configuration in `~/.forever/config.json`.
+//
+app.cmd('config', cli.config = function () {
+ var keys = Object.keys(forever.config.store),
+ conf = cliff.inspect(forever.config.store);
+
+ if (keys.length <= 2) {
+ conf = conf.replace(/\{\s/, '{ \n')
+ .replace(/\}/, '\n}')
+ .replace('\\033[90m', ' \\033[90m')
+ .replace(/, /ig, ',\n ');
+ }
+ else {
+ conf = conf.replace(/\n\s{4}/ig, '\n ');
+ }
+
+ conf.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ });
+});
+
+//
+// ### function set (key, value)
+// #### @key {string} Key to set in forever config
+// #### @value {string} Value to set for `key`
+// Sets the specified `key` / `value` pair in the
+// forever user config.
+//
+app.cmd(/set ([\w-_]+) (.+)/, cli.set = function (key, value) {
+ updateConfig(function () {
+ forever.log.info('Setting forever config: ' + key.grey);
+ forever.config.set(key, value);
+ });
+});
+
+//
+// ### function clear (key)
+// #### @key {string} Key to remove from `~/.forever/config.json`
+// Removes the specified `key` from the forever user config.
+//
+app.cmd('clear :key', cli.clear = function (key) {
+ if (reserved.indexOf(key) !== -1) {
+ forever.log.warn('Cannot clear reserved config: ' + key.grey);
+ forever.log.warn('Use `forever set ' + key + '` instead');
+ return;
+ }
+
+ updateConfig(function () {
+ forever.log.info('Clearing forever config: ' + key.grey);
+ forever.config.clear(key);
+ });
+});
+
+//
+// ### function logs (target)
+// #### @target {string} Target script or index to list logs for
+// Displays the logs using `tail` for the specified `target`.
+//
+app.cmd('logs :index', cli.logs = function (index) {
+ var options = {
+ stream: app.argv.fifo,
+ length: app.argv.number
+ };
+
+ forever.tail(index, options, function (err, log) {
+ if (err) {
+ return forever.log.error(err.message);
+ }
+
+ forever.log.data(log.file.magenta + ':' + log.pid + ' - ' + log.line);
+
+ });
+});
+
+//
+// ### function logFiles ()
+// Display log files for all running forever processes.
+//
+app.cmd('logs', cli.logFiles = function (index) {
+ if (typeof index !== 'undefined') {
+ return;
+ }
+
+ var rows = [[' ', 'script', 'logfile']];
+ index = 0;
+
+ forever.list(false, function (err, processes) {
+ if (!processes) {
+ return forever.log.warn('No forever logfiles in ' + forever.config.get('root').magenta);
+ }
+
+ forever.log.info('Logs for running Forever processes');
+ rows = rows.concat(processes.map(function (proc) {
+ return ['[' + index++ + ']', proc.file.grey, proc.logFile.magenta];
+ }));
+
+ cliff.putRows('data', rows, ['white', 'grey', 'magenta']);
+ });
+});
+
+
+app.cmd('columns add :name', cli.addColumn = function (name) {
+ if (checkColumn(name)) {
+ var columns = forever.config.get('columns');
+
+ if (~columns.indexOf(name)) {
+ return forever.log.warn(name.magenta + ' already exists in forever');
+ }
+
+ forever.log.info('Adding column: ' + name.magenta);
+ columns.push(name);
+
+ forever.config.set('columns', columns);
+ forever.config.saveSync();
+ }
+});
+
+app.cmd('columns rm :name', cli.rmColumn = function (name) {
+ if (checkColumn(name)) {
+ var columns = forever.config.get('columns');
+
+ if (!~columns.indexOf(name)) {
+ return forever.log.warn(name.magenta + ' doesn\'t exist in forever');
+ }
+
+ forever.log.info('Removing column: ' + name.magenta);
+ columns.splice(columns.indexOf(name), 1);
+
+ forever.config.set('columns', columns);
+ forever.config.saveSync();
+ }
+});
+
+app.cmd(/columns set (.*)/, cli.setColumns = function (columns) {
+ forever.log.info('Setting columns: ' + columns.magenta);
+
+ forever.config.set('columns', columns.split(' '));
+ forever.config.saveSync();
+});
+
+//
+// ### function help ()
+// Shows help
+//
+app.cmd('help', cli.help = function () {
+ util.puts(help.join('\n'));
+});
+
+//
+// ### function start (file)
+// #### @file {string} Location of the script to spawn with forever
+// Starts a forever process for the script located at `file` as non-daemon
+// process.
+//
+// Remark: this regex matches everything. It has to be added at the end to
+// make executing other commands possible.
+//
+cli.run = function () {
+ var file = app.argv._[0],
+ options = getOptions(file);
+
+ tryStart(file, options, function () {
+ var monitor = forever.start(file, options);
+ monitor.on('start', function () {
+ forever.startServer(monitor);
+ });
+ });
+};
+
+cli.start = function () {
+ if (app.argv.v || app.argv.version) {
+ return console.log('v' + forever.version);
+ }
+
+ //
+ // Check for --no-colors/--colors and --plain option
+ //
+ if ((typeof app.argv.colors !== 'undefined' && !app.argv.colors) || app.argv.plain) {
+ colors.mode = 'none';
+ }
+
+ if (app.config.get('help')) {
+ return util.puts(help.join('\n'));
+ }
+
+ app.init(function () {
+ if (app.argv._.length && actions.indexOf(app.argv._[0]) === -1) {
+ return cli.run();
+ }
+
+ app.start();
+ });
+};
+
diff --git a/node_modules/forever/lib/forever/service/adapters/adapter.js b/node_modules/forever/lib/forever/service/adapters/adapter.js
new file mode 100644
index 0000000..bf23411
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/adapters/adapter.js
@@ -0,0 +1,96 @@
+/*
+ * adapter.js: Abstract base class used by foreverd service adapters
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var Adapter = module.exports = function Adapter(service) {
+ this.service = service;
+};
+
+//
+// This should install assets to appropriate places for initialization,
+// configuration, and storage
+//
+// The script will be used on startup to load Service
+//
+// Service should listen on something that the management events
+// can respond to in full duplex
+//
+// The installed adapter should send the following events in nssocket protocol
+// to the Service and invoke methods as appropriate
+//
+Adapter.prototype.install = function install() {
+ throw new Error('not implemented');
+};
+
+//
+// This should do a rollback of install completely except for logs
+//
+Adapter.prototype.uninstall = function uninstall() {
+ throw new Error('not implemented');
+};
+
+//
+// This should call back with an array of [{file:...,options:...},] to pass to Monitors
+// this will be invoked when foreverd is created (not started)
+//
+Adapter.prototype.load = function load(callback) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to start the service
+// this will not start any applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.start = function start(monitors) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to start the service
+// this will not stop any applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.stop = function stop(monitors) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to reply with info about applications in the service
+// this will not change any applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.status = function status(monitors) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to restart the service
+// this will not restart any applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.restart = function restart(monitors) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to pause the service
+// this will prevent any addition or removal of applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.pause = function pause(monitors) {
+ throw new Error('not implemented');
+};
+
+//
+// This should tell the OS to resume the service
+// this will enable any addition or removal of applications
+// make sure the adapter is installed and sending events to foreverd's listener
+//
+Adapter.prototype.resume = function resume(monitors) {
+ throw new Error('not implemented');
+}; \ No newline at end of file
diff --git a/node_modules/forever/lib/forever/service/adapters/initd/index.js b/node_modules/forever/lib/forever/service/adapters/initd/index.js
new file mode 100644
index 0000000..1162d37
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/adapters/initd/index.js
@@ -0,0 +1,9 @@
+/*
+ * index.js: Top-level include for the init.d foreverd service adapter.
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+module.exports = require('../systemv'); \ No newline at end of file
diff --git a/node_modules/forever/lib/forever/service/adapters/systemv/foreverd b/node_modules/forever/lib/forever/service/adapters/systemv/foreverd
new file mode 100644
index 0000000..bdd9a5c
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/adapters/systemv/foreverd
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# initd-example Node init.d
+#
+# chkconfig: 345 80 20
+# description: Node init.d example
+# processname: node
+# pidfile: /var/run/initd-example.pid
+# logfile: /var/log/initd-example.log
+#
+
+# Source function library.
+. /lib/lsb/init-functions
+PATH=$PATH:/usr/local/bin
+
+NAME=foreverd
+pidfile=/var/run/$NAME.pid
+logfile=/var/log/$NAME.log
+forever_dir=/var/local/forever # Forever root directory.
+
+node=node
+foreverd=`which foreverd`
+awk=awk
+sed=sed
+
+start() {
+ echo "Starting $NAME: "
+
+ if [ "$id" = "" ]; then
+ # Launch the application
+ $foreverd -p $forever_dir run
+ else
+ echo "Instance already running"
+ fi
+ RETVAL=$?
+}
+
+restart() {
+ echo -n "Restarting $NAME: "
+ if [ "$id" != "" ]; then
+ $foreverd -p $forever_dir restart
+ RETVAL=$?
+ else
+ start
+ fi
+}
+
+stop() {
+ echo -n "Shutting down $NAME: "
+ if [ "$id" != "" ]; then
+ $foreverd -p $forever_dir stop
+ else
+ echo "Instance is not running";
+ fi
+ RETVAL=$?
+}
+
+getForeverId() {
+ local pid=$(pidofproc -p $pidfile)
+ $foreverd list -p $forever_dir | $sed -e 's/\x1b\[[0-9; ]*m//g' | $awk "\$4 == \"$pid]\" { gsub(/[\[\]]/, \"\", \$1); print \$1; }"
+}
+id=$(getForeverId)
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ $foreverd -p $forever_dir list
+ ;;
+ restart)
+ restart
+ ;;
+ *)
+ echo "Usage: {start|stop|status|restart}"
+ exit 1
+ ;;
+esac
+exit $RETVAL
diff --git a/node_modules/forever/lib/forever/service/adapters/systemv/index.js b/node_modules/forever/lib/forever/service/adapters/systemv/index.js
new file mode 100644
index 0000000..2877b77
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/adapters/systemv/index.js
@@ -0,0 +1,199 @@
+/*
+ * index.js: Top-level include for the systemv foreverd service adapter
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var fs = require('fs'),
+ util = require('util'),
+ path = require('path'),
+ spawn = require('child_process').spawn,
+ nssocket = require('nssocket'),
+ forever = require('../../../../forever'),
+ Adapter = require('../adapter');
+
+//
+// Classic init.d script adapter
+// Sometimes called inittab, but its origin is called systemv
+//
+var SystemVAdapter = module.exports = function SystemVAdapter(service) {
+ Adapter.call(this, service);
+ this.daemonized = false;
+};
+
+util.inherits(SystemVAdapter, Adapter);
+
+SystemVAdapter.prototype.install = function install(callback) {
+ //
+ // Copy the init.d script to the right location
+ // TODO Distribution fixes?
+ //
+ forever.config.set('root', path.join('/var', 'local', 'foreverd'));
+ var initdPath = path.join('/etc', 'init.d', 'foreverd'),
+ script,
+ target;
+
+ try {
+ fs.mkdirSync(forever.config.get('root'), '0777');
+ fs.mkdirSync(path.join(forever.config.get('root'), 'services'), '0777');
+ }
+ catch (e) {
+ if (e.code !== 'EEXIST') {
+ return callback && callback(e);
+ }
+ }
+
+ try {
+ script = fs.createReadStream(path.join(__dirname, 'foreverd'));
+ target = fs.createWriteStream(initdPath, { flags: 'w', mode: '0777' });
+
+ script.pipe(target);
+ script.on('end', function () {
+ var directories = fs.readdirSync('/etc');
+ directories.forEach(function (directory) {
+ var match = directory.match(/^rc(\d+)\.d$/),
+ killOrStart;
+
+ if (match) {
+ killOrStart = { 0: true, 1: true, 6: true }[match[1]] ? 'K' : 'S';
+
+ try {
+ fs.symlinkSync(initdPath, path.join('/etc', directory, killOrStart + '20foreverd'));
+ }
+ catch (e) {
+ if (e.code !== 'EEXIST') {
+ return callback && callback(e);
+ }
+ }
+ }
+ });
+
+ return callback && callback();
+ });
+ }
+ catch (e) {
+ if (e.code !== 'EEXIST') {
+ return callback && callback(e);
+ }
+ }
+};
+
+//
+//
+//
+SystemVAdapter.prototype.load = function load(callback) {
+ forever.config.set('root', path.join('/var', 'local', 'foreverd'));
+ var serviceFiles = fs.readdirSync(path.join(forever.config.get('root'), 'services')),
+ services = [];
+
+ if (serviceFiles.length !== 0) {
+ serviceFiles.forEach(function loadServiceFiles(serviceFile, index) {
+ var serviceFilePath = path.join(forever.config.get('root'), 'services', serviceFile),
+ service = JSON.parse(fs.readFileSync(serviceFilePath)),
+ file = service.file,
+ options = service.options;
+
+ options.minUptime = 200;
+ services.push({
+ file: service.file,
+ options: service.options
+ });
+ });
+ }
+
+ callback(services);
+};
+
+SystemVAdapter.prototype.start = function start(callback) {
+ spawn('/etc/init.d/foreverd', ['start']);
+ return callback && callback();
+};
+
+SystemVAdapter.prototype.run = function run(callback) {
+ if (this.daemonized) {
+ return callback();
+ }
+
+ var self = this,
+ pidFilePath = path.join('/var', 'run', 'foreverd.pid'),
+ logFilePath = path.join('/var', 'log', 'foreverd');
+
+ process.on('exit', function removePIDFile() {
+ try {
+ fs.unlinkSync(pidFilePath);
+ }
+ catch (err) {
+ // we are exiting anyway. this may have some noexist error already
+ }
+ });
+
+ fs.open(logFilePath, 'w+', function serviceLogOpened(err, logFile) {
+ if (err) {
+ throw err;
+ }
+
+ self.service.startServer(function () {
+ try {
+ //
+ // TODO: Create a pseudo-daemon to replace this.
+ //
+ // daemon.start(logFile);
+ // daemon.lock(pidFilePath);
+ self.daemonized = true;
+ return callback && callback();
+ }
+ catch (err) {
+ console.error(err);
+ return callback && callback(err);
+ }
+ });
+ });
+};
+
+SystemVAdapter.prototype.add = function add(file, options, callback) {
+ forever.config.set('root', path.join('/var', 'local', 'foreverd'));
+ //
+ // Add descriptor to our service list
+ // this is just a json file in $root/services/*.json
+ //
+ var filePath, service = {
+ file: file,
+ options: options || {}
+ };
+
+ options.appendLog = true;
+ filePath = path.join(forever.config.get('root'), 'services', options.uid + '.json');
+
+ fs.writeFile(filePath, JSON.stringify(service), function (err) {
+ return callback && callback(err);
+ });
+};
+
+SystemVAdapter.prototype.list = function list(callback) {
+ forever.config.set('root', path.join('/var', 'local', 'foreverd'));
+
+ var sockPath = path.join(forever.config.get('root'), 'foreverd.sock'),
+ client;
+
+ client = dnode.connect(sockPath, function onConnect(remote, client) {
+ if (callback) {
+ callback(false, remote.applications);
+ }
+
+ client.end();
+ });
+
+ client.on('error', function onError(err) {
+ if (err.code === 'ECONNREFUSED') {
+ try {
+ fs.unlinkSync(fullPath);
+ }
+ catch (ex) { }
+ return callback && callback(false, []);
+ }
+
+ return callback && callback(err);
+ });
+}; \ No newline at end of file
diff --git a/node_modules/forever/lib/forever/service/cli.js b/node_modules/forever/lib/forever/service/cli.js
new file mode 100644
index 0000000..674e15b
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/cli.js
@@ -0,0 +1,102 @@
+/*
+ * cli.js: Handlers for the foreverd CLI commands.
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var utile = require('utile'),
+ flatiron = require('flatiron'),
+ optimist = require('optimist'),
+ forever = require('../../forever'),
+ Service = require('./service'),
+ argv;
+
+var cli = exports;
+
+var app = flatiron.app;
+
+app.use(flatiron.plugins.cli, {
+ usage: forever.cli.usage
+});
+
+app.config.argv().env();
+
+var service = new Service({
+ adapter: optimist.argv.adapter
+});
+
+app.cmd('install', cli.install = function () {
+ service.install(function onInstall(err) {
+ if (err) {
+ forever.log.error(err);
+ }
+ else {
+ forever.log.info('foreverd installed');
+ }
+ });
+});
+
+//TODO
+app.cmd('run', cli.run = function () {
+ service.load(function () {
+ service.run();
+ });
+});
+
+app.cmd('uninstall', cli.uninstall = function () {
+ service.uninstall();
+});
+
+app.cmd(/add (.*)/, cli.add = function (file) {
+ service.add(file, forever.cli.getOptions(file));
+});
+
+//TODO
+app.cmd(/remove (.*)/, cli.remove = function (file) {
+ service.remove(file, forever.cli.getOptions(file));
+});
+
+app.cmd('start', cli.start = function () {
+ service.start();
+});
+
+//TODO
+app.cmd('stop', cli.stop = function () {
+ service.stop();
+});
+
+app.cmd('restart', cli.restart = function () {
+ service.restart();
+});
+
+app.cmd('list', cli.list = function () {
+ service.list(function (err, applications) {
+ if (err) {
+ app.log.error('Error running command: ' + 'list'.magenta);
+ app.log.error(err.message);
+ err.stack && err.stack.split('\n').forEach(function (line) {
+ app.log.error(line);
+ })
+ return;
+ }
+
+ applications.forEach(function printApplication(application) {
+ console.log(application.monitor.uid, application.monitor.command, application.file, application.monitor.child.pid, application.monitor.logFile, application.monitor.pidFile);
+ });
+ });
+});
+
+app.cmd('pause', cli.pause = function () {
+ service.pause();
+});
+
+app.cmd('resume', cli.resume = function () {
+ service.resume();
+});
+
+cli.startCLI = function () {
+ app.start();
+};
+
diff --git a/node_modules/forever/lib/forever/service/index.js b/node_modules/forever/lib/forever/service/index.js
new file mode 100644
index 0000000..5addced
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/index.js
@@ -0,0 +1,22 @@
+/*
+ * index.js: Top-level include for the `forever.service` module.
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var fs = require('fs'),
+ path = require('path');
+
+var service = exports;
+
+service.Service = require('./service');
+service.adapters = {};
+
+fs.readdirSync(path.join(__dirname, 'adapters')).forEach(function (file) {
+ file = file.replace(/\.js/, '');
+ service.adapters.__defineGetter__(file, function () {
+ return require(__dirname + '/adapters/' + file);
+ });
+});
diff --git a/node_modules/forever/lib/forever/service/service.js b/node_modules/forever/lib/forever/service/service.js
new file mode 100644
index 0000000..2cc878d
--- /dev/null
+++ b/node_modules/forever/lib/forever/service/service.js
@@ -0,0 +1,268 @@
+/*
+ * service.js: Object responsible for managing the foreverd daemon.
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var fs = require('fs'),
+ path = require('path'),
+ util = require('util'),
+ events = require('events'),
+ forever = require('../../forever'),
+ SystemVAdapter = require('./adapters/systemv');
+
+// options
+// directories {log, pid, conf, run, local}
+var Service = module.exports = function Service(options) {
+ events.EventEmitter.call(this);
+ options = options || {};
+
+ var self = this,
+ AdapterType;
+
+ this.applications = [
+ //{
+ //file:
+ //options:
+ //monitor:
+ //}
+ ];
+
+ this.servers = [];
+ if (typeof options.adapter === 'string') {
+ options.adapter = Service.adapter[options.adapter];
+ }
+
+ AdapterType = options.adapter || SystemVAdapter;
+ this.adapter = new AdapterType(this);
+};
+
+util.inherits(Service, events.EventEmitter);
+
+Service.prototype.startServer = function startServer(callback) {
+ var socket = path.join(forever.config.get('sockPath'), 'forever.sock'),
+ monitors = [],
+ self = this,
+ server;
+
+ server = dnode(this);
+ server.on('error', function onServerError() {
+ //
+ // TODO: This is really bad.
+ //
+ });
+
+ server.on('ready', function onServerReady(err) {
+ self.listen(server);
+ if (callback) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, server, socket);
+ }
+ }
+ });
+
+ server.listen(socket);
+
+ return this;
+};
+
+Service.prototype.listen = function listen(server) {
+ var dnodeServer = dnode(this);
+
+ this.servers.push(dnodeServer);
+ dnodeServer.listen(server);
+
+ return this;
+};
+
+Service.prototype.load = function load() {
+ var self = this;
+ this.adapter.load(function onLoaded(applications) {
+ applications.forEach(function startApplication(application, index) {
+ var monitor = application.monitor = new forever.Monitor(application.file, application.options);
+
+ monitor.start();
+ self.applications.push(application);
+
+ if (index === applications.length - 1) {
+ self.listen(path.join(forever.config.get('root'), 'foreverd.sock'));
+ }
+
+ self.emit('foreverd::loaded');
+ });
+ });
+ return this;
+};
+
+//
+// Function add(file, options)
+// add the application to the service manager
+// DOES NOT START THE APPLICATION
+// call's the service manager's add method
+//
+Service.prototype.add = function add(file, options, callback) {
+ if (this.paused) {
+ return callback && callback(new Error('foreverd is paused'));
+ }
+
+ this.adapter.add(file, options, callback);
+};
+
+//
+// Function remove(file, options)
+// remove the application from the service manager
+// call's the service manager's remove method
+//
+Service.prototype.remove = function remove(file, options, callback) {
+ if (this.paused) {
+ return callback(new Error('foreverd is paused'));
+ }
+
+ var applicationsToRemove = this.applications,
+ self = this,
+ optionStr,
+ fileStr;
+
+ if (file) {
+ fileStr = JSON.stringify(file);
+ applicationsToRemove = applicationsToRemove.filter(function compareFile(application) {
+ return fileStr !== JSON.stringify(application.file);
+ });
+ }
+
+ if (options) {
+ optionStr = JSON.stringify(options);
+ applicationsToRemove = applicationsToRemove.filter(function compareOptions(application) {
+ return optionStr !== JSON.stringify(application.options);
+ });
+ }
+
+ applicationsToRemove.forEach(function removeApplication(application) {
+ if (application.monitor) {
+ application.monitor.stop();
+ }
+
+ self.applications.splice(self.applications.indexOf(application), 1);
+ });
+
+ if (callback) {
+ callback();
+ }
+
+ return this;
+};
+
+//
+// Function install()
+// installs all the required to run foreverd
+// call's the service manager's install(options)
+//
+Service.prototype.install = function install(callback) {
+ this.adapter.install(callback);
+ return this;
+};
+
+//
+// Function uninstall(options)
+// uninstalls all the required to run foreverd
+// call's the service manager's uninstall(options)
+//
+Service.prototype.uninstall = function uninstall(callback) {
+ this.adapter.uninstall(callback);
+ return this;
+};
+
+//
+// Function start()
+// calls the appropriate OS functionality to start this service
+//
+Service.prototype.start = function start(callback) {
+ this.adapter.start(callback);
+ return this;
+};
+
+//
+// Function run()
+// creates monitors for all the services
+//
+Service.prototype.run = function run(callback) {
+ var self = this;
+ this.adapter.run(function adapterStarted() {
+ self.applications.forEach(function startApplication(application) {
+ application.monitor = new forever.Monitor(application.file, application.options);
+ application.monitor.start();
+ });
+
+ return callback && callback();
+ });
+
+ return this;
+};
+
+//
+// Function stop(monitors)
+//
+Service.prototype.stop = function stop(callback) {
+ var self = this;
+ this.adapter.start(function adapterStopped() {
+ self.applications.forEach(function stopApplication(application) {
+ application.monitor.stop();
+ });
+
+ return callback && callback();
+ });
+
+ return this;
+};
+
+//
+// Function restart()
+//
+Service.prototype.restart = function restart(callback) {
+ var self = this;
+ this.adapter.start(function adapterRestarted() {
+ self.applications.forEach(function restartApplication(application) {
+ application.monitor.restart();
+ });
+
+ return callback && callback();
+ });
+
+ return this;
+};
+
+//
+// Function pause()
+// disables adding / removing applications
+//
+Service.prototype.pause = function pause(callback) {
+ this.paused = true;
+ if (callback) {
+ callback();
+ }
+
+ return this;
+};
+
+//
+// Function resume()
+// reenables adding / removing applications
+//
+Service.prototype.resume = function resume(callback) {
+ this.paused = false;
+ if (callback) {
+ callback();
+ }
+
+ return this;
+};
+
+Service.prototype.list = function list(callback) {
+ this.adapter.list(callback);
+ return this;
+}; \ No newline at end of file
diff --git a/node_modules/forever/lib/forever/worker.js b/node_modules/forever/lib/forever/worker.js
new file mode 100644
index 0000000..895a6d5
--- /dev/null
+++ b/node_modules/forever/lib/forever/worker.js
@@ -0,0 +1,122 @@
+var events = require('events'),
+ fs = require('fs'),
+ path = require('path'),
+ nssocket = require('nssocket'),
+ utile = require('utile'),
+ forever = require(path.resolve(__dirname, '..', 'forever'));
+
+var Worker = exports.Worker = function (options) {
+ events.EventEmitter.call(this);
+ options || (options = {});
+
+ this.monitor = options.monitor;
+ this.sockPath = options.sockPath || forever.config.get('sockPath');
+ this.exitOnStop = options.exitOnStop === true;
+
+ this._socket = null;
+};
+
+utile.inherits(Worker, events.EventEmitter);
+
+Worker.prototype.start = function (callback) {
+ var self = this,
+ err;
+
+ if (this._socket) {
+ err = new Error("Can't start already started worker");
+ if (callback) {
+ return callback(err);
+ }
+
+ throw err;
+ }
+
+ //
+ // Defines a simple `nssocket` protocol for communication
+ // with a parent process.
+ //
+ function workerProtocol(socket) {
+ socket.on('error', function() {
+ socket.destroy();
+ })
+
+ socket.data(['ping'], function () {
+ socket.send(['pong']);
+ });
+
+ socket.data(['data'], function () {
+ socket.send(['data'], self.monitor.data);
+ });
+
+ socket.data(['spawn'], function (data) {
+ if (!data.script) {
+ return socket.send(['spawn', 'error'], { error: new Error('No script given') });
+ }
+
+ if (self.monitor) {
+ return socket.send(['spawn', 'error'], { error: new Error("Already running") });
+ }
+
+ var monitor = new (forever.Monitor)(data.script, data.options);
+ monitor.start();
+
+ monitor.on('start', function () {
+ socket.send(['spawn', 'start'], monitor.data);
+ });
+ });
+
+ socket.data(['stop'], function () {
+ self.monitor.once('stop', function () {
+ socket.send(['stop', 'ok']);
+ self.exitOnStop && process.exit();
+ });
+
+ self.monitor.stop();
+ });
+
+ socket.data(['restart'], function () {
+ self.monitor.once('restart', function () {
+ socket.send(['restart', 'ok']);
+ });
+
+ self.monitor.restart();
+ });
+ }
+
+ function findAndStart() {
+ self._socket = nssocket.createServer(workerProtocol);
+ self._socket.on('listening', function () {
+ //
+ // `listening` listener doesn't take error as the first parameter
+ //
+ self.emit('start');
+ callback && callback(null, self._sockFile);
+ });
+
+ self._socket.on('error', function (err) {
+ if (err.code === 'EADDRINUSE') {
+ return findAndStart();
+ }
+
+ callback && callback(err);
+ });
+
+ //
+ // Create a unique socket file based on the current microtime.
+ //
+ var sock = self._sockFile = path.join(self.sockPath, [
+ 'worker',
+ new Date().getTime() + utile.randomString(3),
+ 'sock'
+ ].join('.'));
+
+ self._socket.listen(sock);
+ }
+
+ //
+ // Attempt to start the server the first time
+ //
+ findAndStart();
+ return this;
+};
+