Forever = function (script, options) { // // Simple bootstrapper for attaching logger // and watch plugins by default. Other plugins // can be attached through `monitor.use(plugin, options)`. // function bootstrap(monitor) { plugins.logger.attach.call(monitor, options); if (options.watch) { plugins.watch.attach.call(monitor, options); } } var execPath = process.execPath, self = this; // // Setup basic configuration options // options = options || {}; this.silent = options.silent || false; this.killTree = options.killTree !== false; this.uid = options.uid || utile.randomString(4); this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; this.checkFile = options.checkFile !== false; this.times = 0; this.warn = console.error; this.logFile = options.logFile; this.outFile = options.outFile; this.errFile = options.errFile; this.append = options.append; this.usePolling = options.usePolling; this.pollingInterval = options.pollingInterval; // // Define some safety checks for commands with spaces // this.parser = options.parser || Monitor.parseCommand; // // Setup restart timing. These options control how quickly forever restarts // a child process as well as when to kill a "spinning" process // this.minUptime = typeof options.minUptime !== 'number' ? 0 : options.minUptime; this.spinSleepTime = options.spinSleepTime || null; // // Special case Windows separately to decouple any // future changes // if (process.platform === 'win32') { execPath = '"' + execPath + '"'; } if (options.options) { console.warn('options.options is deprecated. Use options.args instead.'); } // // Setup the command to spawn and the options to pass // to that command. // this.command = options.command || execPath; this.args = options.args || options.options || []; this.spawnWith = options.spawnWith || {}; this.sourceDir = options.sourceDir; this.fork = options.fork || false; this.cwd = options.cwd || process.cwd(); this.hideEnv = options.hideEnv || []; this._env = options.env || {}; this._hideEnv = {}; // // Allow for custom stdio configuration of forked processes // this.stdio = options.stdio || null; // // Setup watch configuration options // this.watchIgnoreDotFiles = options.watchIgnoreDotFiles !== false; this.watchIgnorePatterns = options.watchIgnorePatterns || []; this.watchDirectory = options.watchDirectory || this.sourceDir; // // Create a simple mapping of `this.hideEnv` to an easily indexable // object // this.hideEnv.forEach(function (key) { self._hideEnv[key] = true; }); if (Array.isArray(script)) { this.command = script[0]; this.args = script.slice(1); } else { this.args.unshift(script); } if (this.sourceDir) { this.args[0] = path.join(this.sourceDir, this.args[0]); } // // Bootstrap this instance now that options // have been set // broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } }); }
n/a
Monitor = function (script, options) { // // Simple bootstrapper for attaching logger // and watch plugins by default. Other plugins // can be attached through `monitor.use(plugin, options)`. // function bootstrap(monitor) { plugins.logger.attach.call(monitor, options); if (options.watch) { plugins.watch.attach.call(monitor, options); } } var execPath = process.execPath, self = this; // // Setup basic configuration options // options = options || {}; this.silent = options.silent || false; this.killTree = options.killTree !== false; this.uid = options.uid || utile.randomString(4); this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; this.checkFile = options.checkFile !== false; this.times = 0; this.warn = console.error; this.logFile = options.logFile; this.outFile = options.outFile; this.errFile = options.errFile; this.append = options.append; this.usePolling = options.usePolling; this.pollingInterval = options.pollingInterval; // // Define some safety checks for commands with spaces // this.parser = options.parser || Monitor.parseCommand; // // Setup restart timing. These options control how quickly forever restarts // a child process as well as when to kill a "spinning" process // this.minUptime = typeof options.minUptime !== 'number' ? 0 : options.minUptime; this.spinSleepTime = options.spinSleepTime || null; // // Special case Windows separately to decouple any // future changes // if (process.platform === 'win32') { execPath = '"' + execPath + '"'; } if (options.options) { console.warn('options.options is deprecated. Use options.args instead.'); } // // Setup the command to spawn and the options to pass // to that command. // this.command = options.command || execPath; this.args = options.args || options.options || []; this.spawnWith = options.spawnWith || {}; this.sourceDir = options.sourceDir; this.fork = options.fork || false; this.cwd = options.cwd || process.cwd(); this.hideEnv = options.hideEnv || []; this._env = options.env || {}; this._hideEnv = {}; // // Allow for custom stdio configuration of forked processes // this.stdio = options.stdio || null; // // Setup watch configuration options // this.watchIgnoreDotFiles = options.watchIgnoreDotFiles !== false; this.watchIgnorePatterns = options.watchIgnorePatterns || []; this.watchDirectory = options.watchDirectory || this.sourceDir; // // Create a simple mapping of `this.hideEnv` to an easily indexable // object // this.hideEnv.forEach(function (key) { self._hideEnv[key] = true; }); if (Array.isArray(script)) { this.command = script[0]; this.args = script.slice(1); } else { this.args.unshift(script); } if (this.sourceDir) { this.args[0] = path.join(this.sourceDir, this.args[0]); } // // Bootstrap this instance now that options // have been set // broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } }); }
...
if (!options.logFile) {
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
...
Monitor.super_ = function (options) { // // Setup options and `App` constants. // options = options || {}; this.root = options.root; this.delimiter = options.delimiter || '::'; // // Inherit from `EventEmitter2` // events.EventEmitter2.call(this, { delimiter: this.delimiter, wildcard: true }); // // Setup other relevant options such as the plugins // for this instance. // this.options = options; this.env = options.env || process.env['NODE_ENV'] || 'development' this.plugins = options.plugins || {}; this.initialized = false; this.bootstrapper = options.bootstrapper || bootstrapper; this.initializers = {}; this.initlist = []; // // Bootstrap this instance // this.bootstrapper.bootstrap(this); }
n/a
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; }
...
}
else if (typeof a === 'function') {
callback = a;
}
});
async.map(monitors, function (monitor, next) {
var worker = new forever.Worker({
monitor: monitor,
sockPath: forever.config.get('sockPath'),
exitOnStop: true
});
worker.start(function (err) {
return err ? next(err) : next(null, worker);
...
_debug = function () { var debug = forever.config.get('debug'); if (!debug) { forever.config.set('debug', true); forever.log.add(winston.transports.File, { level: 'silly', filename: path.join(forever.config.get('root'), 'forever.debug.log') }); } }
...
options.debug = options.debug || forever.config.get('debug') || false;
if (options.debug) {
//
// If we have been indicated to debug this forever process
// then setup `forever._debug` to be an instance of `winston.Logger`.
//
forever._debug();
}
//
// Syncronously create the `root` directory
// and the `pid` directory for forever. Although there is
// an additional overhead here of the sync action. It simplifies
// the setup of forever dramatically.
...
checkProcess = function (pid) { if (!pid) { return false; } try { // // Trying to kill non-existent process here raises a ESRCH - no such // process exception. Also, signal 0 doesn't do no harm to a process - it // only checks if sending a signal to a given process is possible. // process.kill(pid, 0); return true; } catch (err) { return false; } }
...
}, 500);
}
done();
}
function checkProcess(proc, next) {
proc.child = forever.checkProcess(proc.pid);
proc.manager = forever.checkProcess(proc.foreverPid);
cleanProcess(proc, next);
}
if (processes && processes.length > 0) {
(function cleanBatch(batch) {
async.forEach(batch, checkProcess, function () {
...
cleanLogsSync = function (processes) { var root = forever.config.get('root'), files = fs.readdirSync(root), running, runningLogs; running = processes && processes.filter(function (p) { return p && p.logFile; }); runningLogs = running && running.map(function (p) { return p.logFile.split('/').pop(); }); files.forEach(function (file) { if (/\.log$/.test(file) && (!runningLogs || runningLogs.indexOf(file) === -1)) { fs.unlinkSync(path.join(root, file)); } }); }
...
getAllProcesses(function (err, processes) {
if (err) {
return process.nextTick(function () {
emitter.emit('error', err);
});
}
else if (cleanLogs) {
forever.cleanLogsSync(processes);
}
function unlinkProcess(proc, done) {
fs.unlink(path.join(pidPath, proc.uid + '.pid'), function () {
//
// Ignore errors (in case the file doesnt exist).
//
...
cleanUp = function (cleanLogs, allowManager) { var emitter = new events.EventEmitter(), pidPath = forever.config.get('pidPath'); getAllProcesses(function (err, processes) { if (err) { return process.nextTick(function () { emitter.emit('error', err); }); } else if (cleanLogs) { forever.cleanLogsSync(processes); } function unlinkProcess(proc, done) { fs.unlink(path.join(pidPath, proc.uid + '.pid'), function () { // // Ignore errors (in case the file doesnt exist). // if (cleanLogs && proc.logFile) { // // If we are cleaning logs then do so if the process // has a logfile. // return fs.unlink(proc.logFile, function () { done(); }); } done(); }); } function cleanProcess(proc, done) { if (proc.child && proc.manager) { return done(); } else if (!proc.child && !proc.manager || (!proc.child && proc.manager && allowManager) || proc.dead) { return unlinkProcess(proc, done); } // // If we have a manager but no child, wait a moment // in-case the child is currently restarting, but **only** // if we have not already waited for this process // if (!proc.waited) { proc.waited = true; return setTimeout(function () { checkProcess(proc, done); }, 500); } done(); } function checkProcess(proc, next) { proc.child = forever.checkProcess(proc.pid); proc.manager = forever.checkProcess(proc.foreverPid); cleanProcess(proc, next); } if (processes && processes.length > 0) { (function cleanBatch(batch) { async.forEach(batch, checkProcess, function () { return processes.length > 0 ? cleanBatch(processes.splice(0, 10)) : emitter.emit('cleanUp'); }); })(processes.splice(0, 10)); } else { process.nextTick(function () { emitter.emit('cleanUp'); }); } }); return emitter; }
n/a
findById = function (id, processes) { if (!processes) { return null; } var procs = processes.filter(function (p) { return p.id === id; }); if (procs.length === 0) { procs = null; } return procs; }
...
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
...
findByIndex = function (index, processes) { var indexAsNum = parseInt(index, 10), proc; if (indexAsNum == index) { proc = processes && processes[indexAsNum]; } return proc ? [proc] : null; }
...
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
...
findByPid = function (pid, processes) { pid = typeof pid === 'string' ? parseInt(pid, 10) : pid; var procs = processes && processes.filter(function (p) { return p.pid === pid; }); if (procs && procs.length === 0) { procs = null; } return procs || null; }
...
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
if (procs && procs.length > 0) {
async.map(procs, sendAction, function (err, results) {
...
findByScript = function (script, processes) { if (!processes) { return null; } // make script absolute. if (script.indexOf('/') != 0) { script = path.resolve(process.cwd(), script); } var procs = processes.filter(function (p) { return p.file === script || path.join(p.spawnWith.cwd, p.file) === script; }); if (procs.length === 0) { procs = null; } return procs; }
...
emitter.emit('error', err);
});
}
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
...
findByUid = function (script, processes) { var procs = !processes ? null : processes.filter(function (p) { return p.uid === script; }); if (procs && procs.length === 0) { procs = null; } return procs; }
...
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
if (procs && procs.length > 0) {
...
format = function (format, procs) { if (!procs || procs.length === 0) { return null; } var index = 0, columns = forever.config.get('columns'), rows = [[' '].concat(columns)], formatted; function mapColumns(prefix, mapFn) { return [prefix].concat(columns.map(mapFn)); } if (format) { // // Iterate over the procs to see which has the // longest options string // procs.forEach(function (proc) { rows.push(mapColumns('[' + index + ']', function (column) { return forever.columns[column] ? forever.columns[column].get(proc) : 'MISSING'; })); index++; }); formatted = cliff.stringifyRows(rows, mapColumns('white', function (column) { return forever.columns[column] ? forever.columns[column].color : 'white'; })); } return format ? formatted : procs; }
...
if (err) {
emitter.emit('error', err);
}
//
// Remark (indexzero): we should do something with the results.
//
emitter.emit(event, forever.format(format, procs));
});
}
else {
process.nextTick(function () {
emitter.emit('error', new Error('Cannot find forever process: ' + target));
});
}
...
kill = function (pid, killTree, signal, callback) { signal = signal || 'SIGKILL'; callback = callback || function () {}; if (killTree && process.platform !== 'win32') { psTree(pid, function (err, children) { [pid].concat( children.map(function (p) { return p.PID; }) ).forEach(function (tpid) { try { process.kill(tpid, signal) } catch (ex) { } }); callback(); }); } else { try { process.kill(pid, signal) } catch (ex) { } callback(); } }
n/a
list = function (format, callback) { getAllProcesses(function (err, processes) { callback(err, forever.format(format, processes)); }); }
...
}
]
```
### Using In Your Code
The forever module exposes some useful methods to use in your code. Each method returns an instance of an EventEmitter which emits
when complete. See the [forever cli commands][2] for sample usage.
**Remark:** As of `forever@0.6.0` processes will not automatically be available in `forever.
list()`. In order to get your processes into `forever.list()` or `forever list` you must instantiate the `forever` socket
server:
``` js
forever.startServer(child);
```
This method takes multiple `forever.Monitor` instances which are defined in the `forever-monitor` dependency.
...
load = function (options) { // memorize current options. this._loadedOptions = options; // // Setup the incoming options with default options. // options = options || {}; options.loglength = options.loglength || 100; options.logstream = options.logstream || false; options.root = options.root || forever.root; options.pidPath = options.pidPath || path.join(options.root, 'pids'); options.sockPath = options.sockPath || path.join(options.root, 'sock'); // // If forever is initalized and the config directories are identical // simply return without creating directories // if (forever.initialized && forever.config.get('root') === options.root && forever.config.get('pidPath') === options.pidPath) { return; } forever.config = new nconf.File({ file: path.join(options.root, 'config.json') }); // // Try to load the forever `config.json` from // the specified location. // try { forever.config.loadSync(); } catch (ex) { } // // Setup the columns for `forever list`. // options.columns = options.columns || forever.config.get('columns'); if (!options.columns) { options.columns = [ 'uid', 'command', 'script', 'forever', 'pid', 'id', 'logfile', 'uptime' ]; } forever.config.set('root', options.root); forever.config.set('pidPath', options.pidPath); forever.config.set('sockPath', options.sockPath); forever.config.set('loglength', options.loglength); forever.config.set('logstream', options.logstream); forever.config.set('columns', options.columns); // // Setup timestamp to event logger // forever.out.transports.console.timestamp = forever.config.get('timestamp') === 'true'; // // Attempt to see if `forever` has been configured to // run in debug mode. // options.debug = options.debug || forever.config.get('debug') || false; if (options.debug) { // // If we have been indicated to debug this forever process // then setup `forever._debug` to be an instance of `winston.Logger`. // forever._debug(); } // // Syncronously create the `root` directory // and the `pid` directory for forever. Although there is // an additional overhead here of the sync action. It simplifies // the setup of forever dramatically. // function tryCreate(dir) { try { fs.mkdirSync(dir, '0755'); } catch (ex) { } } tryCreate(forever.config.get('root')); tryCreate(forever.config.get('pidPath')); tryCreate(forever.config.get('sockPath')); // // Attempt to save the new `config.json` for forever // try { forever.config.saveSync(); } catch (ex) { } forever.initialized = true; }
...
});
}
};
//
// Ensure forever will always be loaded the first time it is required.
//
forever.load();
//
// ### function stat (logFile, script, callback)
// #### @logFile {string} Path to the log file for this script
// #### @logAppend {boolean} Optional. True Prevent failure if the log file exists.
// #### @script {string} Path to the target script.
// #### @callback {function} Continuation to pass control back to
...
logEvents = function (monitor) { monitor.on('watch:error', function (info) { forever.out.error(info.message); forever.out.error(info.error); }); monitor.on('watch:restart', function (info) { forever.out.error('restarting script because ' + info.file + ' changed'); }); monitor.on('restart', function () { forever.out.error('Script restart attempt #' + monitor.times); }); monitor.on('exit:code', function (code, signal) { forever.out.error((code !== null && code !== undefined) ? 'Forever detected script exited with code: ' + code : 'Forever detected script was killed by signal: ' + signal); }); }
...
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
...
logFilePath = function (logFile, uid) { return logFile && (logFile[0] === '/' || logFile[1] === ':') ? logFile : path.join(forever.config.get('root'), logFile || (uid || 'forever') + '.log'); }
...
//
forever.start = function (script, options) {
if (!options.uid) {
options.uid = utile.randomString(4).replace(/^\-/, '_');
}
if (!options.logFile) {
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
...
pidFilePath = function (pidFile) { return pidFile && (pidFile[0] === '/' || pidFile[1] === ':') ? pidFile : path.join(forever.config.get('pidPath'), pidFile); }
...
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
//
forever.startDaemon = function (script, options) {
options = options || {};
options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_');
options.logFile = forever.logFilePath(options.logFile || forever.config.get('logFile') || options.uid + '.log'
;);
options.pidFile = forever.pidFilePath(options.pidFile || forever.config.get('pidFile
') || options.uid + '.pid');
var monitor, outFD, errFD, monitorPath;
//
// This log file is forever's log file - the user's outFile and errFile
// options are not taken into account here. This will be an aggregate of all
// the app's output, as well as messages from the monitor process, where
...
restart = function (target, format) { return stopOrRestart('restart', 'restart', format, target); }
...
});
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 () {
//
...
restartAll = function (format) { return stopOrRestart('restart', 'restartAll', format); }
n/a
start = function (script, options) { if (!options.uid) { options.uid = utile.randomString(4).replace(/^\-/, '_'); } if (!options.logFile) { options.logFile = forever.logFilePath(options.uid + '.log'); } // // Create the monitor, log events, and start. // var monitor = new forever.Monitor(script, options); forever.logEvents(monitor); return monitor.start(); }
...
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
...
startDaemon = function (script, options) { options = options || {}; options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_'); options.logFile = forever.logFilePath(options.logFile || forever.config.get('logFile') || options.uid + '.log'); options.pidFile = forever.pidFilePath(options.pidFile || forever.config.get('pidFile') || options.uid + '.pid'); var monitor, outFD, errFD, monitorPath; // // This log file is forever's log file - the user's outFile and errFile // options are not taken into account here. This will be an aggregate of all // the app's output, as well as messages from the monitor process, where // applicable. // outFD = fs.openSync(options.logFile, 'a'); errFD = fs.openSync(options.logFile, 'a'); monitorPath = path.resolve(__dirname, '..', 'bin', 'monitor'); monitor = spawn(process.execPath, [monitorPath, script], { stdio: ['ipc', outFD, errFD], detached: true }); monitor.on('exit', function (code) { console.error('Monitor died unexpectedly with exit code %d', code); }); // transmit options to daemonic(child) process, keep configuration lineage. options._loadedOptions = this._loadedOptions; monitor.send(JSON.stringify(options)); // close the ipc communication channel with the monitor // otherwise the corresponding events listeners will prevent // the exit of the current process (observed with node 0.11.9) monitor.disconnect(); // make sure the monitor is unref() and does not prevent the // exit of the current process monitor.unref(); return monitor; }
n/a
startServer = function () { var args = Array.prototype.slice.call(arguments), monitors = [], callback; args.forEach(function (a) { if (Array.isArray(a)) { monitors = monitors.concat(a.filter(function (m) { return m instanceof forever.Monitor; })); } else if (a instanceof forever.Monitor) { monitors.push(a); } else if (typeof a === 'function') { callback = a; } }); async.map(monitors, function (monitor, next) { var worker = new forever.Worker({ monitor: monitor, sockPath: forever.config.get('sockPath'), exitOnStop: true }); worker.start(function (err) { return err ? next(err) : next(null, worker); }); }, callback || function () {}); }
...
### Using In Your Code
The forever module exposes some useful methods to use in your code. Each method returns an instance of an EventEmitter which emits
when complete. See the [forever cli commands][2] for sample usage.
**Remark:** As of `forever@0.6.0` processes will not automatically be available in `forever.list()`. In order to get your processes
into `forever.list()` or `forever list` you must instantiate the `forever` socket server:
``` js
forever.startServer(child);
```
This method takes multiple `forever.Monitor` instances which are defined in the `forever-monitor` dependency.
#### forever.load (config)
_Synchronously_ sets the specified configuration (config) for the forever module. There are two important options:
...
stat = function (logFile, script, callback) { var logAppend; if (arguments.length === 4) { logAppend = callback; callback = arguments[3]; } fs.stat(script, function (err, stats) { if (err) { return callback(new Error('script ' + script + ' does not exist.')); } return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) { return !err ? callback(new Error('log file ' + logFile + ' exists. Use the -a or --append option to append log.')) : callback(null); }); }); }
...
var logAppend;
if (arguments.length === 4) {
logAppend = callback;
callback = arguments[3];
}
fs.stat(script, function (err, stats) {
if (err) {
return callback(new Error('script ' + script + ' does not exist.'));
}
return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) {
return !err
? callback(new Error('log file ' + logFile + ' exists. Use the -a or --append option to append log.'))
...
stop = function (target, format) { return stopOrRestart('stop', 'stop', format, target); }
...
// file is used for exploration during `forever list`
// as a mapping to the `\\.pipe\\*` "files" that can't
// be enumerated because ... Windows.
//
fs.unlink(self._sockFile);
}
self.monitor.stop();
});
socket.data(['restart'], function () {
self.monitor.once('restart', function () {
socket.send(['restart', 'ok']);
});
...
stopAll = function (format) { return stopOrRestart('stop', 'stopAll', format); }
n/a
stopbypid = function (pid, format) { // stopByPid only capable of stopping, but can't restart return stopOrRestart('stop', 'stopByPid', format, pid); }
n/a
tail = function (target, options, callback) { if (!callback && typeof options === 'function') { callback = options; options.length = 0; options.stream = false; } var that = this, length = options.length || forever.config.get('loglength'), stream = options.stream || forever.config.get('logstream'), blanks = function (e, i, a) { return e !== ''; }, title = function (e, i, a) { return e.match(/^==>/); }, args = ['-n', length], logs; if (stream) { args.unshift('-f'); } function tailProcess(procs, next) { var count = 0, map = {}, tail; procs.forEach(function (proc) { args.push(proc.logFile); map[proc.logFile] = { pid: proc.pid, file: proc.file }; count++; }); tail = spawn('tail', args, { stdio: [null, 'pipe', 'pipe'], }); tail.stdio[1].setEncoding('utf8'); tail.stdio[2].setEncoding('utf8'); tail.stdio[1].on('data', function (data) { var chunk = data.split('\n\n'); chunk.forEach(function (logs) { var logs = logs.split('\n').filter(blanks), file = logs.filter(title), lines, proc; proc = file.length ? map[file[0].split(' ')[1]] : map[procs[0].logFile]; lines = count !== 1 ? logs.slice(1) : logs; lines.forEach(function (line) { callback(null, { file: proc.file, pid: proc.pid, line: line }); }); }); }); tail.stdio[2].on('data', function (err) { return callback(err); }); } getAllProcesses(function (err, processes) { if (err) { return callback(err); } else if (!processes) { return callback(new Error('Cannot find forever process: ' + target)); } var procs = forever.findByIndex(target, processes) || forever.findByScript(target, processes); if (!procs) { return callback(new Error('No logs available for process: ' + target)); } tailProcess(procs, callback); }); }
n/a
Monitor = function (script, options) { // // Simple bootstrapper for attaching logger // and watch plugins by default. Other plugins // can be attached through `monitor.use(plugin, options)`. // function bootstrap(monitor) { plugins.logger.attach.call(monitor, options); if (options.watch) { plugins.watch.attach.call(monitor, options); } } var execPath = process.execPath, self = this; // // Setup basic configuration options // options = options || {}; this.silent = options.silent || false; this.killTree = options.killTree !== false; this.uid = options.uid || utile.randomString(4); this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; this.checkFile = options.checkFile !== false; this.times = 0; this.warn = console.error; this.logFile = options.logFile; this.outFile = options.outFile; this.errFile = options.errFile; this.append = options.append; this.usePolling = options.usePolling; this.pollingInterval = options.pollingInterval; // // Define some safety checks for commands with spaces // this.parser = options.parser || Monitor.parseCommand; // // Setup restart timing. These options control how quickly forever restarts // a child process as well as when to kill a "spinning" process // this.minUptime = typeof options.minUptime !== 'number' ? 0 : options.minUptime; this.spinSleepTime = options.spinSleepTime || null; // // Special case Windows separately to decouple any // future changes // if (process.platform === 'win32') { execPath = '"' + execPath + '"'; } if (options.options) { console.warn('options.options is deprecated. Use options.args instead.'); } // // Setup the command to spawn and the options to pass // to that command. // this.command = options.command || execPath; this.args = options.args || options.options || []; this.spawnWith = options.spawnWith || {}; this.sourceDir = options.sourceDir; this.fork = options.fork || false; this.cwd = options.cwd || process.cwd(); this.hideEnv = options.hideEnv || []; this._env = options.env || {}; this._hideEnv = {}; // // Allow for custom stdio configuration of forked processes // this.stdio = options.stdio || null; // // Setup watch configuration options // this.watchIgnoreDotFiles = options.watchIgnoreDotFiles !== false; this.watchIgnorePatterns = options.watchIgnorePatterns || []; this.watchDirectory = options.watchDirectory || this.sourceDir; // // Create a simple mapping of `this.hideEnv` to an easily indexable // object // this.hideEnv.forEach(function (key) { self._hideEnv[key] = true; }); if (Array.isArray(script)) { this.command = script[0]; this.args = script.slice(1); } else { this.args.unshift(script); } if (this.sourceDir) { this.args[0] = path.join(this.sourceDir, this.args[0]); } // // Bootstrap this instance now that options // have been set // broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } }); }
...
if (!options.logFile) {
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
...
parseCommand = function (command, args) { var match = command.match( process.platform === 'win32' ? safetyChecks.windows : safetyChecks.linux ); // // No match means it's a bad command. This is configurable // by passing a custom `parser` function into the `Monitor` // constructor function. // if (!match) { return false; } if (process.platform == 'win32') { command = match[1] || match[2]; if (match[3]) { args = match[3].split(' ').concat(args); } } else { command = match[1]; if (match[2]) { args = match[2].split(' ').concat(this.args); } } return { command: command, args: args }; }
n/a
super_ = function (options) { // // Setup options and `App` constants. // options = options || {}; this.root = options.root; this.delimiter = options.delimiter || '::'; // // Inherit from `EventEmitter2` // events.EventEmitter2.call(this, { delimiter: this.delimiter, wildcard: true }); // // Setup other relevant options such as the plugins // for this instance. // this.options = options; this.env = options.env || process.env['NODE_ENV'] || 'development' this.plugins = options.plugins || {}; this.initialized = false; this.bootstrapper = options.bootstrapper || bootstrapper; this.initializers = {}; this.initlist = []; // // Bootstrap this instance // this.bootstrapper.bootstrap(this); }
n/a
_getEnv = function () { var self = this, merged = {}; function addKey(key, source) { merged[key] = source[key]; } // // Mixin the key:value pairs from `process.env` and the custom // environment variables in `this._env`. // Object.keys(process.env).forEach(function (key) { if (!self._hideEnv[key]) { addKey(key, process.env); } }); Object.keys(this._env).forEach(function (key) { addKey(key, self._env); }); return merged; }
n/a
kill = function (forceStop) { var child = this.child, self = this, timer; if (!child || (!this.running && !this.forceRestart)) { process.nextTick(function () { self.emit('error', new Error('Cannot stop process that is not running.')); }); } else { // // Set an instance variable here to indicate this // stoppage is forced so that when `child.on('exit', ..)` // fires in `Monitor.prototype.start` we can short circuit // and prevent auto-restart // if (forceStop) { this.forceStop = true; // // If we have a time before we truly kill forcefully, set up a timer // if (this.killTTL) { timer = setTimeout(function () { common.kill(self.child.pid, self.killTree, self.killSignal || 'SIGKILL'); }, this.killTTL); child.once('exit', function () { clearTimeout(timer); }); } } child.once('exit', function () { self.emit('stop', self.childData); if (self.forceRestart && !self.running) { self.start(true); } }); common.kill(this.child.pid, this.killTree, this.killSignal); } return this; }
n/a
restart = function () { this.times = this.times || 0; this.forceRestart = true; return !this.running ? this.start(true) : this.kill(false); }
...
});
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 () {
//
...
send = function (msg) { var child = this.child, self = this; if (!child || !this.running) { process.nextTick(function () { self.emit('error', new Error('Cannot send to process that is not running.')); }); } if (child.send) { child.send(msg) } }
...
socket.dataOnce(['data'], function (data) {
data.socket = fullPath;
next(null, data);
socket.end();
});
socket.send(['data']);
});
socket.on('error', function (err) {
if (err.code === 'ECONNREFUSED') {
fs.unlink(fullPath, function () {
next();
});
...
start = function (restart) { var self = this, child; if (this.running && !restart) { process.nextTick(function () { self.emit('error', new Error('Cannot start process that is already running.')); }); return this; } child = this.trySpawn(); if (!child) { process.nextTick(function () { self.emit('error', new Error('Target script does not exist: ' + self.args[0])); }); return this; } this.ctime = Date.now(); this.child = child; this.running = true; this.isMaster = cluster.isMaster; process.nextTick(function () { self.emit(restart ? 'restart' : 'start', self, self.data); }); function onMessage(msg) { self.emit('message', msg); } // Re-emit messages from the child process this.child.on('message', onMessage); child.on('exit', function (code, signal) { var spinning = Date.now() - self.ctime < self.minUptime; child.removeListener('message', onMessage); self.emit('exit:code', code, signal); function letChildDie() { self.running = false; self.forceStop = false; self.emit('exit', self, spinning); } function restartChild() { self.forceRestart = false; process.nextTick(function () { self.start(true); }); } self.times++; if (self.forceStop || (self.times >= self.max && !self.forceRestart) || (spinning && typeof self.spinSleepTime !== 'number') && !self.forceRestart) { letChildDie(); } else if (spinning) { setTimeout(restartChild, self.spinSleepTime); } else { restartChild(); } }); return this; }
...
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
...
stop = function () { return this.kill(true); }
...
// file is used for exploration during `forever list`
// as a mapping to the `\\.pipe\\*` "files" that can't
// be enumerated because ... Windows.
//
fs.unlink(self._sockFile);
}
self.monitor.stop();
});
socket.data(['restart'], function () {
self.monitor.once('restart', function () {
socket.send(['restart', 'ok']);
});
...
toString = function () { return JSON.stringify(this); }
...
get: function (proc) {
return proc.sourceDir.grey;
}
},
uptime: {
color: 'yellow',
get: function (proc) {
return proc.running ? timespan.fromDates(new Date(proc.ctime), new Date()).toString().yellow : "STOPPED".red;
}
}
};
...
trySpawn = function () { var run = this.parser(this.command, this.args.slice()), stats; if (/[^\w]node$/.test(this.command) && this.checkFile && !this.childExists) { try { stats = fs.statSync(this.args[0]); this.childExists = true; } catch (ex) { return false; } } this.spawnWith.cwd = this.spawnWith.cwd || this.cwd; this.spawnWith.env = this._getEnv(); if (process.platform === 'win32') { this.spawnWith.detached = true; } if (this.stdio) { this.spawnWith.stdio = this.stdio; } if (this.fork) { if (!this.stdio) { this.spawnWith.stdio = [ 'pipe', 'pipe', 'pipe', 'ipc' ]; } return spawn(run.command, run.args, this.spawnWith); } return spawn(run.command, run.args, this.spawnWith); }
n/a
function EventEmitter(conf) { this._events = {}; this.newListener = false; configure.call(this, conf); }
n/a
init = function (options, callback) { if (!callback && typeof options === 'function') { callback = options; options = {}; } if (this.initialized) { return callback(); } var self = this; options = options || {}; callback = callback || function () {}; this.env = options.env || this.env; this.options = common.mixin({}, this.options, options); function onComplete() { self.initialized = true; self.emit('init'); callback(); } function ensureFeatures (err) { return err ? onError(err) : features.ensure(this, onComplete); } function initPlugin(plugin, next) { if (typeof self.initializers[plugin] === 'function') { return self.initializers[plugin].call(self, function (err) { if (err) { return next(err); } self.emit(['plugin', plugin, 'init']); self.initializers[plugin] = true; next(); }); } next(); } function initPlugins() { async.forEach(self.initlist, initPlugin, ensureFeatures); } // // Emit and respond with any errors that may short // circuit the process. // function onError(err) { self.emit(['error', 'init'], err); callback(err); } // // Run the bootstrapper, initialize plugins, and // ensure features for this instance. // this.bootstrapper.init(this, initPlugins); }
n/a
inspect = function () { }
n/a
remove = function (name) { // if this is a plugin object set the name to the plugins name if (name.name) { name = name.name; } if (this.plugins[name] && this.plugins[name].detach) { this.plugins[name].detach.call(this); } delete this.plugins[name]; delete this.options[name]; delete this.initializers[name]; var init = this.initlist.indexOf(name); if (init !== -1) { this.initlist.splice(1, init); } }
n/a
use = function (plugin, options, callback) { options = options || {}; if (typeof plugin === 'undefined') { console.log('Cannot load invalid plugin!'); return callback && callback(new Error('Invalid plugin')); } var name = plugin.name, self = this; // If the plugin doesn't have a name, use itself as an identifier for the plugins hash. if (!name) { name = common.uuid(); } if (this.plugins[name]) { return callback && callback(); } // // Setup state on this instance for the specified plugin // this.plugins[name] = plugin; this.options[name] = common.mixin({}, options, this.options[name] || {}); // // Attach the specified plugin to this instance, extending // the `App` with new functionality. // if (this.plugins[name].attach && options.attach !== false) { this.plugins[name].attach.call(this, options); } // // Setup the initializer only if `options.init` is // not false. This allows for some plugins to be lazy-loaded // if (options.init === false) { return callback && callback(); } if (!this.initialized) { this.initializers[name] = plugin.init || true; this.initlist.push(name); return callback && callback(); } else if (plugin.init) { plugin.init.call(this, function (err) { var args = err ? [['plugin', name, 'error'], err] : [['plugin', name, 'init']]; self.emit.apply(self, args); return callback && (err ? callback(err) : callback()); }); } }
n/a
addListener = function (type, listener) { if (typeof type === 'function') { this.onAny(type); return this; } if (typeof listener !== 'function') { throw new Error('on only accepts instances of Function'); } this._events || init.call(this); // To avoid recursion in the case that type == "newListeners"! Before // adding it to the listeners, first emit "newListeners". this.emit('newListener', type, listener); if(this.wildcard) { growListenerTree.call(this, type, listener); return this; } if (!this._events[type]) { // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; } else if(typeof this._events[type] === 'function') { // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; } else if (isArray(this._events[type])) { // If we've already got an array, just append. this._events[type].push(listener); // Check for listener leak if (!this._events[type].warned) { var m = defaultMaxListeners; if (typeof this._events.maxListeners !== 'undefined') { m = this._events.maxListeners; } if (m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); console.trace(); } } } return this; }
n/a
emit = function () { this._events || init.call(this); var type = arguments[0]; if (type === 'newListener' && !this.newListener) { if (!this._events.newListener) { return false; } } // Loop through the *_all* functions and invoke them. if (this._all) { var l = arguments.length; var args = new Array(l - 1); for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; for (i = 0, l = this._all.length; i < l; i++) { this.event = type; this._all[i].apply(this, args); } } // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._all && !this._events.error && !(this.wildcard && this.listenerTree.error)) { if (arguments[1] instanceof Error) { throw arguments[1]; // Unhandled 'error' event } else { throw new Error("Uncaught, unspecified 'error' event."); } return false; } } var handler; if(this.wildcard) { handler = []; var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); searchListenerTree.call(this, handler, ns, this.listenerTree, 0); } else { handler = this._events[type]; } if (typeof handler === 'function') { this.event = type; if (arguments.length === 1) { handler.call(this); } else if (arguments.length > 1) switch (arguments.length) { case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: var l = arguments.length; var args = new Array(l - 1); for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; handler.apply(this, args); } return true; } else if (handler) { var l = arguments.length; var args = new Array(l - 1); for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; var listeners = handler.slice(); for (var i = 0, l = listeners.length; i < l; i++) { this.event = type; listeners[i].apply(this, args); } return (listeners.length > 0) || !!this._all; } else { return !!this._all; } }
...
});
}
getAllProcesses(function (err, processes) {
if (err) {
return process.nextTick(function () {
emitter.emit('error', err);
});
}
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
...
listeners = function (type) { if(this.wildcard) { var handlers = []; var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); searchListenerTree.call(this, handlers, ns, this.listenerTree, 0); return handlers; } this._events || init.call(this); if (!this._events[type]) this._events[type] = []; if (!isArray(this._events[type])) { this._events[type] = [this._events[type]]; } return this._events[type]; }
n/a
listenersAny = function () { if(this._all) { return this._all; } else { return []; } }
n/a
many = function (event, ttl, fn) { var self = this; if (typeof fn !== 'function') { throw new Error('many only accepts instances of Function'); } function listener() { if (--ttl === 0) { self.off(event, listener); } fn.apply(this, arguments); } listener._origin = fn; this.on(event, listener); return self; }
n/a
off = function (type, listener) { if (typeof listener !== 'function') { throw new Error('removeListener only takes instances of Function'); } var handlers,leafs=[]; if(this.wildcard) { var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); } else { // does not use listeners(), so no side effect of creating _events[type] if (!this._events[type]) return this; handlers = this._events[type]; leafs.push({_listeners:handlers}); } for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { var leaf = leafs[iLeaf]; handlers = leaf._listeners; if (isArray(handlers)) { var position = -1; for (var i = 0, length = handlers.length; i < length; i++) { if (handlers[i] === listener || (handlers[i].listener && handlers[i].listener === listener) || (handlers[i]._origin && handlers[i]._origin === listener)) { position = i; break; } } if (position < 0) { continue; } if(this.wildcard) { leaf._listeners.splice(position, 1); } else { this._events[type].splice(position, 1); } if (handlers.length === 0) { if(this.wildcard) { delete leaf._listeners; } else { delete this._events[type]; } } return this; } else if (handlers === listener || (handlers.listener && handlers.listener === listener) || (handlers._origin && handlers._origin === listener)) { if(this.wildcard) { delete leaf._listeners; } else { delete this._events[type]; } } } return this; }
n/a
offAny = function (fn) { var i = 0, l = 0, fns; if (fn && this._all && this._all.length > 0) { fns = this._all; for(i = 0, l = fns.length; i < l; i++) { if(fn === fns[i]) { fns.splice(i, 1); return this; } } } else { this._all = []; } return this; }
n/a
on = function (type, listener) { if (typeof type === 'function') { this.onAny(type); return this; } if (typeof listener !== 'function') { throw new Error('on only accepts instances of Function'); } this._events || init.call(this); // To avoid recursion in the case that type == "newListeners"! Before // adding it to the listeners, first emit "newListeners". this.emit('newListener', type, listener); if(this.wildcard) { growListenerTree.call(this, type, listener); return this; } if (!this._events[type]) { // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; } else if(typeof this._events[type] === 'function') { // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; } else if (isArray(this._events[type])) { // If we've already got an array, just append. this._events[type].push(listener); // Check for listener leak if (!this._events[type].warned) { var m = defaultMaxListeners; if (typeof this._events.maxListeners !== 'undefined') { m = this._events.maxListeners; } if (m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); console.trace(); } } } return this; }
...
next(null, data);
socket.end();
});
socket.send(['data']);
});
socket.on('error', function (err) {
if (err.code === 'ECONNREFUSED') {
fs.unlink(fullPath, function () {
next();
});
}
else {
next();
...
onAny = function (fn) { if (typeof fn !== 'function') { throw new Error('onAny only accepts instances of Function'); } if(!this._all) { this._all = []; } // Add the function to the event listener collection. this._all.push(fn); return this; }
n/a
once = function (event, fn) { this.many(event, 1, fn); return this; }
...
socket.send.apply(socket, args);
if (self.exitOnStop) {
process.exit();
}
}
self.monitor.once('stop', onStop);
self.monitor.once('error', onStop);
if (process.platform === 'win32') {
//
// On Windows, delete the 'symbolic' sock file. This
// file is used for exploration during `forever list`
// as a mapping to the `\\.pipe\\*` "files" that can't
...
removeAllListeners = function (type) { if (arguments.length === 0) { !this._events || init.call(this); return this; } if(this.wildcard) { var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { var leaf = leafs[iLeaf]; leaf._listeners = null; } } else { if (!this._events[type]) return this; this._events[type] = null; } return this; }
n/a
removeListener = function (type, listener) { if (typeof listener !== 'function') { throw new Error('removeListener only takes instances of Function'); } var handlers,leafs=[]; if(this.wildcard) { var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); } else { // does not use listeners(), so no side effect of creating _events[type] if (!this._events[type]) return this; handlers = this._events[type]; leafs.push({_listeners:handlers}); } for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { var leaf = leafs[iLeaf]; handlers = leaf._listeners; if (isArray(handlers)) { var position = -1; for (var i = 0, length = handlers.length; i < length; i++) { if (handlers[i] === listener || (handlers[i].listener && handlers[i].listener === listener) || (handlers[i]._origin && handlers[i]._origin === listener)) { position = i; break; } } if (position < 0) { continue; } if(this.wildcard) { leaf._listeners.splice(position, 1); } else { this._events[type].splice(position, 1); } if (handlers.length === 0) { if(this.wildcard) { delete leaf._listeners; } else { delete this._events[type]; } } return this; } else if (handlers === listener || (handlers.listener && handlers.listener === listener) || (handlers._origin && handlers._origin === listener)) { if(this.wildcard) { delete leaf._listeners; } else { delete this._events[type]; } } } return this; }
...
});
socket.data(['stop'], function () {
function onStop(err) {
var args = [];
if (err && err instanceof Error) {
args.push(['stop', 'error'], { message: err.message, stack: err.stack });
self.monitor.removeListener('stop', onStop);
}
else {
args.push(['stop', 'ok']);
self.monitor.removeListener('error', onStop);
}
socket.send.apply(socket, args);
...
setMaxListeners = function (n) { this._events || init.call(this); this._events.maxListeners = n; if (!this._conf) this._conf = {}; this._conf.maxListeners = n; }
n/a
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; }
...
}
else if (typeof a === 'function') {
callback = a;
}
});
async.map(monitors, function (monitor, next) {
var worker = new forever.Worker({
monitor: monitor,
sockPath: forever.config.get('sockPath'),
exitOnStop: true
});
worker.start(function (err) {
return err ? next(err) : next(null, worker);
...
function EventEmitter() { EventEmitter.init.call(this); }
n/a
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.args); monitor.start(); monitor.on('start', function () { socket.send(['spawn', 'start'], monitor.data); }); }); socket.data(['stop'], function () { function onStop(err) { var args = []; if (err && err instanceof Error) { args.push(['stop', 'error'], { message: err.message, stack: err.stack }); self.monitor.removeListener('stop', onStop); } else { args.push(['stop', 'ok']); self.monitor.removeListener('error', onStop); } socket.send.apply(socket, args); if (self.exitOnStop) { process.exit(); } } self.monitor.once('stop', onStop); self.monitor.once('error', onStop); if (process.platform === 'win32') { // // On Windows, delete the 'symbolic' sock file. This // file is used for exploration during `forever list` // as a mapping to the `\\.pipe\\*` "files" that can't // be enumerated because ... Windows. // fs.unlink(self._sockFile); } 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'); if (callback) { callback(null, self._sockFile); } }); self._socket.on('error', function (err) { if (err.code === 'EADDRINUSE') { return findAndStart(); } else if (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('.')); if (process.platform === 'win32') { // // Create 'symbolic' file on the system, so it can be later // found via "forever list" since the `\\.pipe\\*` "files" can't // be enumerated because ... Windows. // fs.openSync(sock, 'w'); // // It needs the prefix, otherwise EACCESS error happens on Windows // (no .sock extension, only named pipes with .pipe prefixes) // sock = '\\\\.\\pipe\\' + sock; } self._socket.listen(sock); } // // Attempt to start the server the first time // findAndStart(); return this; }
...
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
...
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(); } }
n/a
cleanLogs = function () { forever.log.silly('Tidying ' + forever.config.get('root')); forever.cleanUp(true).on('cleanUp', function () { forever.log.silly(forever.config.get('root') + ' tidied.'); }); }
n/a
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); }); }
n/a
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); }); }
n/a
getOptions = function (file) { var options = {}, absFile = isAbsolute(file) ? file : path.resolve(process.cwd(), file), configKeys = [ 'pidFile', 'logFile', 'errFile', 'watch', 'minUptime', 'append', 'silent', 'outFile', 'max', 'command', 'path', 'spinSleepTime', 'sourceDir', 'workingDir', 'uid', 'watchDirectory', 'watchIgnore', 'killTree', 'killSignal', 'id' ], specialKeys = ['script', 'args'], configs; // // Load JSON configuration values // if (path.extname(file) === '.json') { configs = shush(absFile); configs = !Array.isArray(configs) ? [configs] : configs; configs = configs.map(function (conf) { var mut = Object.keys(conf) .reduce(function (acc, key) { if (~configKeys.indexOf(key) || ~specialKeys.indexOf(key)) { acc[key] = conf[key]; } return acc; }, {}); if (!mut.script) { forever.log.error('"script" option required in JSON configuration files'); console.log(prettyjson.render(mut)); process.exit(1); } return mut; }); } else { options.script = file; } // // First isolate options which should be passed to file // options.args = 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); configKeys.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(' ')); } function assignSpawnWith(options) { options.sourceDir = options.sourceDir || (file && file[0] !== '/' ? process.cwd() : '/'); options.workingDir = options.workingDir || options.sourceDir; options.spawnWith = { cwd: options.workingDir }; return options; } if (configs && configs.length) { return configs.map(function (conf) { return assignSpawnWith(objectAssign(clone(options), conf)); }); } return [assignSpawnWith(options)]; }
n/a
help = function () { util.puts(help.join('\n')); }
n/a
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'); } }); }
...
}
]
```
### Using In Your Code
The forever module exposes some useful methods to use in your code. Each method returns an instance of an EventEmitter which emits
when complete. See the [forever cli commands][2] for sample usage.
**Remark:** As of `forever@0.6.0` processes will not automatically be available in `forever.
list()`. In order to get your processes into `forever.list()` or `forever list` you must instantiate the `forever` socket
server:
``` js
forever.startServer(child);
```
This method takes multiple `forever.Monitor` instances which are defined in the `forever-monitor` dependency.
...
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']); }); }
n/a
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); }); }
n/a
resetColumns = function () { var columns = 'uid command script forever pid logfile uptime'; forever.log.info('Setting columns: ' + columns.magenta); forever.config.set('columns', columns.split(' ')); forever.config.saveSync(); }
n/a
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); }); }
...
});
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 () {
//
...
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'); }); }
n/a
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(); } }
n/a
run = function () { var file = app.argv._[0], options = getOptions(file); options.forEach(function (o) { tryStart(o.script, o, function () { var monitor = forever.start(o.script, o); monitor.on('start', function () { forever.startServer(monitor); }); }); }); }
n/a
set = function (key, value) { updateConfig(function () { forever.log.info('Setting forever config: ' + key.grey); forever.config.set(key, value); }); }
...
#### forever.stop (index)
Stops the forever daemon script at the specified index. These indices are the same as those returned by forever.list(). This method
returns an EventEmitter that raises the 'stop' event when complete.
#### forever.stopAll (format)
Stops all forever scripts currently running. This method returns an EventEmitter that raises the 'stopAll' event when
complete.
The `format` parameter is a boolean value indicating whether the returned values should be formatted according to the configured
columns which can set with `forever columns` or programmatically `forever.config.set(
x27;columns')`.
#### forever.list (format, callback)
Returns a list of metadata objects about each process that is being run using forever. This method will return the list of metadata
as such. Only processes which have invoked `forever.startServer()` will be available from `forever.list()`
The `format` parameter is a boolean value indicating whether the returned values should be formatted according to the configured
columns which can set with `forever columns` or programmatically `forever.config.set('columns')`.
#### forever.tail (target, options, callback)
...
setColumns = function (columns) { forever.log.info('Setting columns: ' + columns.magenta); forever.config.set('columns', columns.split(' ')); forever.config.saveSync(); }
n/a
start = function () { if (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(); }); }
...
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
...
startDaemon = function () { var file = app.argv._[1], options = getOptions(file); options.forEach(function (o) { forever.log.info('Forever processing file: ' + o.script.grey); tryStart(o.script, o, function () { forever.startDaemon(o.script, o); }); }); }
n/a
stop = function (file) { var runner = forever.stop(file, true); runner.on('stop', function (process) { forever.log.info('Forever stopped process:' + '\n' + process); }); runner.on('error', function (err) { forever.log.error('Forever cannot find process with id: ' + file); process.exit(1); }); }
...
// file is used for exploration during `forever list`
// as a mapping to the `\\.pipe\\*` "files" that can't
// be enumerated because ... Windows.
//
fs.unlink(self._sockFile);
}
self.monitor.stop();
});
socket.data(['restart'], function () {
self.monitor.once('restart', function () {
socket.send(['restart', 'ok']);
});
...
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'); }); }
n/a
stopbypid = function (pid) { forever.log.warn('Deprecated, try `forever stop ' + pid + '` instead.'); cli.stop(pid); }
n/a
Forever = function (script, options) { // // Simple bootstrapper for attaching logger // and watch plugins by default. Other plugins // can be attached through `monitor.use(plugin, options)`. // function bootstrap(monitor) { plugins.logger.attach.call(monitor, options); if (options.watch) { plugins.watch.attach.call(monitor, options); } } var execPath = process.execPath, self = this; // // Setup basic configuration options // options = options || {}; this.silent = options.silent || false; this.killTree = options.killTree !== false; this.uid = options.uid || utile.randomString(4); this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; this.checkFile = options.checkFile !== false; this.times = 0; this.warn = console.error; this.logFile = options.logFile; this.outFile = options.outFile; this.errFile = options.errFile; this.append = options.append; this.usePolling = options.usePolling; this.pollingInterval = options.pollingInterval; // // Define some safety checks for commands with spaces // this.parser = options.parser || Monitor.parseCommand; // // Setup restart timing. These options control how quickly forever restarts // a child process as well as when to kill a "spinning" process // this.minUptime = typeof options.minUptime !== 'number' ? 0 : options.minUptime; this.spinSleepTime = options.spinSleepTime || null; // // Special case Windows separately to decouple any // future changes // if (process.platform === 'win32') { execPath = '"' + execPath + '"'; } if (options.options) { console.warn('options.options is deprecated. Use options.args instead.'); } // // Setup the command to spawn and the options to pass // to that command. // this.command = options.command || execPath; this.args = options.args || options.options || []; this.spawnWith = options.spawnWith || {}; this.sourceDir = options.sourceDir; this.fork = options.fork || false; this.cwd = options.cwd || process.cwd(); this.hideEnv = options.hideEnv || []; this._env = options.env || {}; this._hideEnv = {}; // // Allow for custom stdio configuration of forked processes // this.stdio = options.stdio || null; // // Setup watch configuration options // this.watchIgnoreDotFiles = options.watchIgnoreDotFiles !== false; this.watchIgnorePatterns = options.watchIgnorePatterns || []; this.watchDirectory = options.watchDirectory || this.sourceDir; // // Create a simple mapping of `this.hideEnv` to an easily indexable // object // this.hideEnv.forEach(function (key) { self._hideEnv[key] = true; }); if (Array.isArray(script)) { this.command = script[0]; this.args = script.slice(1); } else { this.args.unshift(script); } if (this.sourceDir) { this.args[0] = path.join(this.sourceDir, this.args[0]); } // // Bootstrap this instance now that options // have been set // broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } }); }
n/a
Monitor = function (script, options) { // // Simple bootstrapper for attaching logger // and watch plugins by default. Other plugins // can be attached through `monitor.use(plugin, options)`. // function bootstrap(monitor) { plugins.logger.attach.call(monitor, options); if (options.watch) { plugins.watch.attach.call(monitor, options); } } var execPath = process.execPath, self = this; // // Setup basic configuration options // options = options || {}; this.silent = options.silent || false; this.killTree = options.killTree !== false; this.uid = options.uid || utile.randomString(4); this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; this.checkFile = options.checkFile !== false; this.times = 0; this.warn = console.error; this.logFile = options.logFile; this.outFile = options.outFile; this.errFile = options.errFile; this.append = options.append; this.usePolling = options.usePolling; this.pollingInterval = options.pollingInterval; // // Define some safety checks for commands with spaces // this.parser = options.parser || Monitor.parseCommand; // // Setup restart timing. These options control how quickly forever restarts // a child process as well as when to kill a "spinning" process // this.minUptime = typeof options.minUptime !== 'number' ? 0 : options.minUptime; this.spinSleepTime = options.spinSleepTime || null; // // Special case Windows separately to decouple any // future changes // if (process.platform === 'win32') { execPath = '"' + execPath + '"'; } if (options.options) { console.warn('options.options is deprecated. Use options.args instead.'); } // // Setup the command to spawn and the options to pass // to that command. // this.command = options.command || execPath; this.args = options.args || options.options || []; this.spawnWith = options.spawnWith || {}; this.sourceDir = options.sourceDir; this.fork = options.fork || false; this.cwd = options.cwd || process.cwd(); this.hideEnv = options.hideEnv || []; this._env = options.env || {}; this._hideEnv = {}; // // Allow for custom stdio configuration of forked processes // this.stdio = options.stdio || null; // // Setup watch configuration options // this.watchIgnoreDotFiles = options.watchIgnoreDotFiles !== false; this.watchIgnorePatterns = options.watchIgnorePatterns || []; this.watchDirectory = options.watchDirectory || this.sourceDir; // // Create a simple mapping of `this.hideEnv` to an easily indexable // object // this.hideEnv.forEach(function (key) { self._hideEnv[key] = true; }); if (Array.isArray(script)) { this.command = script[0]; this.args = script.slice(1); } else { this.args.unshift(script); } if (this.sourceDir) { this.args[0] = path.join(this.sourceDir, this.args[0]); } // // Bootstrap this instance now that options // have been set // broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } }); }
...
if (!options.logFile) {
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
...
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; }
...
}
else if (typeof a === 'function') {
callback = a;
}
});
async.map(monitors, function (monitor, next) {
var worker = new forever.Worker({
monitor: monitor,
sockPath: forever.config.get('sockPath'),
exitOnStop: true
});
worker.start(function (err) {
return err ? next(err) : next(null, worker);
...
_debug = function () { var debug = forever.config.get('debug'); if (!debug) { forever.config.set('debug', true); forever.log.add(winston.transports.File, { level: 'silly', filename: path.join(forever.config.get('root'), 'forever.debug.log') }); } }
...
options.debug = options.debug || forever.config.get('debug') || false;
if (options.debug) {
//
// If we have been indicated to debug this forever process
// then setup `forever._debug` to be an instance of `winston.Logger`.
//
forever._debug();
}
//
// Syncronously create the `root` directory
// and the `pid` directory for forever. Although there is
// an additional overhead here of the sync action. It simplifies
// the setup of forever dramatically.
...
checkProcess = function (pid) { if (!pid) { return false; } try { // // Trying to kill non-existent process here raises a ESRCH - no such // process exception. Also, signal 0 doesn't do no harm to a process - it // only checks if sending a signal to a given process is possible. // process.kill(pid, 0); return true; } catch (err) { return false; } }
...
}, 500);
}
done();
}
function checkProcess(proc, next) {
proc.child = forever.checkProcess(proc.pid);
proc.manager = forever.checkProcess(proc.foreverPid);
cleanProcess(proc, next);
}
if (processes && processes.length > 0) {
(function cleanBatch(batch) {
async.forEach(batch, checkProcess, function () {
...
cleanLogsSync = function (processes) { var root = forever.config.get('root'), files = fs.readdirSync(root), running, runningLogs; running = processes && processes.filter(function (p) { return p && p.logFile; }); runningLogs = running && running.map(function (p) { return p.logFile.split('/').pop(); }); files.forEach(function (file) { if (/\.log$/.test(file) && (!runningLogs || runningLogs.indexOf(file) === -1)) { fs.unlinkSync(path.join(root, file)); } }); }
...
getAllProcesses(function (err, processes) {
if (err) {
return process.nextTick(function () {
emitter.emit('error', err);
});
}
else if (cleanLogs) {
forever.cleanLogsSync(processes);
}
function unlinkProcess(proc, done) {
fs.unlink(path.join(pidPath, proc.uid + '.pid'), function () {
//
// Ignore errors (in case the file doesnt exist).
//
...
cleanUp = function (cleanLogs, allowManager) { var emitter = new events.EventEmitter(), pidPath = forever.config.get('pidPath'); getAllProcesses(function (err, processes) { if (err) { return process.nextTick(function () { emitter.emit('error', err); }); } else if (cleanLogs) { forever.cleanLogsSync(processes); } function unlinkProcess(proc, done) { fs.unlink(path.join(pidPath, proc.uid + '.pid'), function () { // // Ignore errors (in case the file doesnt exist). // if (cleanLogs && proc.logFile) { // // If we are cleaning logs then do so if the process // has a logfile. // return fs.unlink(proc.logFile, function () { done(); }); } done(); }); } function cleanProcess(proc, done) { if (proc.child && proc.manager) { return done(); } else if (!proc.child && !proc.manager || (!proc.child && proc.manager && allowManager) || proc.dead) { return unlinkProcess(proc, done); } // // If we have a manager but no child, wait a moment // in-case the child is currently restarting, but **only** // if we have not already waited for this process // if (!proc.waited) { proc.waited = true; return setTimeout(function () { checkProcess(proc, done); }, 500); } done(); } function checkProcess(proc, next) { proc.child = forever.checkProcess(proc.pid); proc.manager = forever.checkProcess(proc.foreverPid); cleanProcess(proc, next); } if (processes && processes.length > 0) { (function cleanBatch(batch) { async.forEach(batch, checkProcess, function () { return processes.length > 0 ? cleanBatch(processes.splice(0, 10)) : emitter.emit('cleanUp'); }); })(processes.splice(0, 10)); } else { process.nextTick(function () { emitter.emit('cleanUp'); }); } }); return emitter; }
n/a
findById = function (id, processes) { if (!processes) { return null; } var procs = processes.filter(function (p) { return p.id === id; }); if (procs.length === 0) { procs = null; } return procs; }
...
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
...
findByIndex = function (index, processes) { var indexAsNum = parseInt(index, 10), proc; if (indexAsNum == index) { proc = processes && processes[indexAsNum]; } return proc ? [proc] : null; }
...
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
...
findByPid = function (pid, processes) { pid = typeof pid === 'string' ? parseInt(pid, 10) : pid; var procs = processes && processes.filter(function (p) { return p.pid === pid; }); if (procs && procs.length === 0) { procs = null; } return procs || null; }
...
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
if (procs && procs.length > 0) {
async.map(procs, sendAction, function (err, results) {
...
findByScript = function (script, processes) { if (!processes) { return null; } // make script absolute. if (script.indexOf('/') != 0) { script = path.resolve(process.cwd(), script); } var procs = processes.filter(function (p) { return p.file === script || path.join(p.spawnWith.cwd, p.file) === script; }); if (procs.length === 0) { procs = null; } return procs; }
...
emitter.emit('error', err);
});
}
var procs;
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
...
findByUid = function (script, processes) { var procs = !processes ? null : processes.filter(function (p) { return p.uid === script; }); if (procs && procs.length === 0) { procs = null; } return procs; }
...
if (target !== undefined && target !== null) {
if (isNaN(target)) {
procs = forever.findByScript(target, processes);
}
procs = procs
|| forever.findById(target, processes)
|| forever.findByIndex(target, processes)
|| forever.findByUid(target, processes)
|| forever.findByPid(target, processes);
}
else {
procs = processes;
}
if (procs && procs.length > 0) {
...
format = function (format, procs) { if (!procs || procs.length === 0) { return null; } var index = 0, columns = forever.config.get('columns'), rows = [[' '].concat(columns)], formatted; function mapColumns(prefix, mapFn) { return [prefix].concat(columns.map(mapFn)); } if (format) { // // Iterate over the procs to see which has the // longest options string // procs.forEach(function (proc) { rows.push(mapColumns('[' + index + ']', function (column) { return forever.columns[column] ? forever.columns[column].get(proc) : 'MISSING'; })); index++; }); formatted = cliff.stringifyRows(rows, mapColumns('white', function (column) { return forever.columns[column] ? forever.columns[column].color : 'white'; })); } return format ? formatted : procs; }
...
if (err) {
emitter.emit('error', err);
}
//
// Remark (indexzero): we should do something with the results.
//
emitter.emit(event, forever.format(format, procs));
});
}
else {
process.nextTick(function () {
emitter.emit('error', new Error('Cannot find forever process: ' + target));
});
}
...
kill = function (pid, killTree, signal, callback) { signal = signal || 'SIGKILL'; callback = callback || function () {}; if (killTree && process.platform !== 'win32') { psTree(pid, function (err, children) { [pid].concat( children.map(function (p) { return p.PID; }) ).forEach(function (tpid) { try { process.kill(tpid, signal) } catch (ex) { } }); callback(); }); } else { try { process.kill(pid, signal) } catch (ex) { } callback(); } }
n/a
list = function (format, callback) { getAllProcesses(function (err, processes) { callback(err, forever.format(format, processes)); }); }
...
}
]
```
### Using In Your Code
The forever module exposes some useful methods to use in your code. Each method returns an instance of an EventEmitter which emits
when complete. See the [forever cli commands][2] for sample usage.
**Remark:** As of `forever@0.6.0` processes will not automatically be available in `forever.
list()`. In order to get your processes into `forever.list()` or `forever list` you must instantiate the `forever` socket
server:
``` js
forever.startServer(child);
```
This method takes multiple `forever.Monitor` instances which are defined in the `forever-monitor` dependency.
...
load = function (options) { // memorize current options. this._loadedOptions = options; // // Setup the incoming options with default options. // options = options || {}; options.loglength = options.loglength || 100; options.logstream = options.logstream || false; options.root = options.root || forever.root; options.pidPath = options.pidPath || path.join(options.root, 'pids'); options.sockPath = options.sockPath || path.join(options.root, 'sock'); // // If forever is initalized and the config directories are identical // simply return without creating directories // if (forever.initialized && forever.config.get('root') === options.root && forever.config.get('pidPath') === options.pidPath) { return; } forever.config = new nconf.File({ file: path.join(options.root, 'config.json') }); // // Try to load the forever `config.json` from // the specified location. // try { forever.config.loadSync(); } catch (ex) { } // // Setup the columns for `forever list`. // options.columns = options.columns || forever.config.get('columns'); if (!options.columns) { options.columns = [ 'uid', 'command', 'script', 'forever', 'pid', 'id', 'logfile', 'uptime' ]; } forever.config.set('root', options.root); forever.config.set('pidPath', options.pidPath); forever.config.set('sockPath', options.sockPath); forever.config.set('loglength', options.loglength); forever.config.set('logstream', options.logstream); forever.config.set('columns', options.columns); // // Setup timestamp to event logger // forever.out.transports.console.timestamp = forever.config.get('timestamp') === 'true'; // // Attempt to see if `forever` has been configured to // run in debug mode. // options.debug = options.debug || forever.config.get('debug') || false; if (options.debug) { // // If we have been indicated to debug this forever process // then setup `forever._debug` to be an instance of `winston.Logger`. // forever._debug(); } // // Syncronously create the `root` directory // and the `pid` directory for forever. Although there is // an additional overhead here of the sync action. It simplifies // the setup of forever dramatically. // function tryCreate(dir) { try { fs.mkdirSync(dir, '0755'); } catch (ex) { } } tryCreate(forever.config.get('root')); tryCreate(forever.config.get('pidPath')); tryCreate(forever.config.get('sockPath')); // // Attempt to save the new `config.json` for forever // try { forever.config.saveSync(); } catch (ex) { } forever.initialized = true; }
...
});
}
};
//
// Ensure forever will always be loaded the first time it is required.
//
forever.load();
//
// ### function stat (logFile, script, callback)
// #### @logFile {string} Path to the log file for this script
// #### @logAppend {boolean} Optional. True Prevent failure if the log file exists.
// #### @script {string} Path to the target script.
// #### @callback {function} Continuation to pass control back to
...
logEvents = function (monitor) { monitor.on('watch:error', function (info) { forever.out.error(info.message); forever.out.error(info.error); }); monitor.on('watch:restart', function (info) { forever.out.error('restarting script because ' + info.file + ' changed'); }); monitor.on('restart', function () { forever.out.error('Script restart attempt #' + monitor.times); }); monitor.on('exit:code', function (code, signal) { forever.out.error((code !== null && code !== undefined) ? 'Forever detected script exited with code: ' + code : 'Forever detected script was killed by signal: ' + signal); }); }
...
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
...
logFilePath = function (logFile, uid) { return logFile && (logFile[0] === '/' || logFile[1] === ':') ? logFile : path.join(forever.config.get('root'), logFile || (uid || 'forever') + '.log'); }
...
//
forever.start = function (script, options) {
if (!options.uid) {
options.uid = utile.randomString(4).replace(/^\-/, '_');
}
if (!options.logFile) {
options.logFile = forever.logFilePath(options.uid + '.log');
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
...
pidFilePath = function (pidFile) { return pidFile && (pidFile[0] === '/' || pidFile[1] === ':') ? pidFile : path.join(forever.config.get('pidPath'), pidFile); }
...
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
//
forever.startDaemon = function (script, options) {
options = options || {};
options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_');
options.logFile = forever.logFilePath(options.logFile || forever.config.get('logFile') || options.uid + '.log'
;);
options.pidFile = forever.pidFilePath(options.pidFile || forever.config.get('pidFile
') || options.uid + '.pid');
var monitor, outFD, errFD, monitorPath;
//
// This log file is forever's log file - the user's outFile and errFile
// options are not taken into account here. This will be an aggregate of all
// the app's output, as well as messages from the monitor process, where
...
restart = function (target, format) { return stopOrRestart('restart', 'restart', format, target); }
...
});
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 () {
//
...
restartAll = function (format) { return stopOrRestart('restart', 'restartAll', format); }
n/a
start = function (script, options) { if (!options.uid) { options.uid = utile.randomString(4).replace(/^\-/, '_'); } if (!options.logFile) { options.logFile = forever.logFilePath(options.uid + '.log'); } // // Create the monitor, log events, and start. // var monitor = new forever.Monitor(script, options); forever.logEvents(monitor); return monitor.start(); }
...
}
//
// Create the monitor, log events, and start.
//
var monitor = new forever.Monitor(script, options);
forever.logEvents(monitor);
return monitor.start();
};
//
// ### function startDaemon (script, options)
// #### @script {string} Location of the script to run.
// #### @options {Object} Configuration for forever instance.
// Starts a script with forever as a daemon
...
startDaemon = function (script, options) { options = options || {}; options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_'); options.logFile = forever.logFilePath(options.logFile || forever.config.get('logFile') || options.uid + '.log'); options.pidFile = forever.pidFilePath(options.pidFile || forever.config.get('pidFile') || options.uid + '.pid'); var monitor, outFD, errFD, monitorPath; // // This log file is forever's log file - the user's outFile and errFile // options are not taken into account here. This will be an aggregate of all // the app's output, as well as messages from the monitor process, where // applicable. // outFD = fs.openSync(options.logFile, 'a'); errFD = fs.openSync(options.logFile, 'a'); monitorPath = path.resolve(__dirname, '..', 'bin', 'monitor'); monitor = spawn(process.execPath, [monitorPath, script], { stdio: ['ipc', outFD, errFD], detached: true }); monitor.on('exit', function (code) { console.error('Monitor died unexpectedly with exit code %d', code); }); // transmit options to daemonic(child) process, keep configuration lineage. options._loadedOptions = this._loadedOptions; monitor.send(JSON.stringify(options)); // close the ipc communication channel with the monitor // otherwise the corresponding events listeners will prevent // the exit of the current process (observed with node 0.11.9) monitor.disconnect(); // make sure the monitor is unref() and does not prevent the // exit of the current process monitor.unref(); return monitor; }
n/a
startServer = function () { var args = Array.prototype.slice.call(arguments), monitors = [], callback; args.forEach(function (a) { if (Array.isArray(a)) { monitors = monitors.concat(a.filter(function (m) { return m instanceof forever.Monitor; })); } else if (a instanceof forever.Monitor) { monitors.push(a); } else if (typeof a === 'function') { callback = a; } }); async.map(monitors, function (monitor, next) { var worker = new forever.Worker({ monitor: monitor, sockPath: forever.config.get('sockPath'), exitOnStop: true }); worker.start(function (err) { return err ? next(err) : next(null, worker); }); }, callback || function () {}); }
...
### Using In Your Code
The forever module exposes some useful methods to use in your code. Each method returns an instance of an EventEmitter which emits
when complete. See the [forever cli commands][2] for sample usage.
**Remark:** As of `forever@0.6.0` processes will not automatically be available in `forever.list()`. In order to get your processes
into `forever.list()` or `forever list` you must instantiate the `forever` socket server:
``` js
forever.startServer(child);
```
This method takes multiple `forever.Monitor` instances which are defined in the `forever-monitor` dependency.
#### forever.load (config)
_Synchronously_ sets the specified configuration (config) for the forever module. There are two important options:
...
stat = function (logFile, script, callback) { var logAppend; if (arguments.length === 4) { logAppend = callback; callback = arguments[3]; } fs.stat(script, function (err, stats) { if (err) { return callback(new Error('script ' + script + ' does not exist.')); } return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) { return !err ? callback(new Error('log file ' + logFile + ' exists. Use the -a or --append option to append log.')) : callback(null); }); }); }
...
var logAppend;
if (arguments.length === 4) {
logAppend = callback;
callback = arguments[3];
}
fs.stat(script, function (err, stats) {
if (err) {
return callback(new Error('script ' + script + ' does not exist.'));
}
return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) {
return !err
? callback(new Error('log file ' + logFile + ' exists. Use the -a or --append option to append log.'))
...
stop = function (target, format) { return stopOrRestart('stop', 'stop', format, target); }
...
// file is used for exploration during `forever list`
// as a mapping to the `\\.pipe\\*` "files" that can't
// be enumerated because ... Windows.
//
fs.unlink(self._sockFile);
}
self.monitor.stop();
});
socket.data(['restart'], function () {
self.monitor.once('restart', function () {
socket.send(['restart', 'ok']);
});
...
stopAll = function (format) { return stopOrRestart('stop', 'stopAll', format); }
n/a
stopbypid = function (pid, format) { // stopByPid only capable of stopping, but can't restart return stopOrRestart('stop', 'stopByPid', format, pid); }
n/a
tail = function (target, options, callback) { if (!callback && typeof options === 'function') { callback = options; options.length = 0; options.stream = false; } var that = this, length = options.length || forever.config.get('loglength'), stream = options.stream || forever.config.get('logstream'), blanks = function (e, i, a) { return e !== ''; }, title = function (e, i, a) { return e.match(/^==>/); }, args = ['-n', length], logs; if (stream) { args.unshift('-f'); } function tailProcess(procs, next) { var count = 0, map = {}, tail; procs.forEach(function (proc) { args.push(proc.logFile); map[proc.logFile] = { pid: proc.pid, file: proc.file }; count++; }); tail = spawn('tail', args, { stdio: [null, 'pipe', 'pipe'], }); tail.stdio[1].setEncoding('utf8'); tail.stdio[2].setEncoding('utf8'); tail.stdio[1].on('data', function (data) { var chunk = data.split('\n\n'); chunk.forEach(function (logs) { var logs = logs.split('\n').filter(blanks), file = logs.filter(title), lines, proc; proc = file.length ? map[file[0].split(' ')[1]] : map[procs[0].logFile]; lines = count !== 1 ? logs.slice(1) : logs; lines.forEach(function (line) { callback(null, { file: proc.file, pid: proc.pid, line: line }); }); }); }); tail.stdio[2].on('data', function (err) { return callback(err); }); } getAllProcesses(function (err, processes) { if (err) { return callback(err); } else if (!processes) { return callback(new Error('Cannot find forever process: ' + target)); } var procs = forever.findByIndex(target, processes) || forever.findByScript(target, processes); if (!procs) { return callback(new Error('No logs available for process: ' + target)); } tailProcess(procs, callback); }); }
n/a
data = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
...
// 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);
});
...
debug = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
error = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
...
monitor = spawn(process.execPath, [monitorPath, script], {
stdio: ['ipc', outFD, errFD],
detached: true
});
monitor.on('exit', function (code) {
console.error('Monitor died unexpectedly with exit code %d', code);
});
// transmit options to daemonic(child) process, keep configuration lineage.
options._loadedOptions = this._loadedOptions;
monitor.send(JSON.stringify(options));
...
help = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
info = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
input = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
prompt = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
silly = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
verbose = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
warn = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
debug = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
error = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
...
monitor = spawn(process.execPath, [monitorPath, script], {
stdio: ['ipc', outFD, errFD],
detached: true
});
monitor.on('exit', function (code) {
console.error('Monitor died unexpectedly with exit code %d', code);
});
// transmit options to daemonic(child) process, keep configuration lineage.
options._loadedOptions = this._loadedOptions;
monitor.send(JSON.stringify(options));
...
info = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
silly = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
verbose = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
warn = function (msg) { // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) var args = [level].concat(Array.prototype.slice.call(arguments)); target.log.apply(target, args); }
n/a
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; }
...
}
else if (typeof a === 'function') {
callback = a;
}
});
async.map(monitors, function (monitor, next) {
var worker = new forever.Worker({
monitor: monitor,
sockPath: forever.config.get('sockPath'),
exitOnStop: true
});
worker.start(function (err) {
return err ? next(err) : next(null, worker);
...