diff --git a/docs/forever.html b/docs/forever.html index d54e1f87..7abd47a4 100644 --- a/docs/forever.html +++ b/docs/forever.html @@ -6,64 +6,78 @@ * */ -require.paths.unshift(__dirname); - var fs = require('fs'), - colors = require('colors'), - async = require('async'), path = require('path'), events = require('events'), exec = require('child_process').exec, - timespan = require('timespan'), + async = require('async'), + colors = require('colors'), + cliff = require('cliff'), + daemon = require('daemon'), nconf = require('nconf'), - daemon = require('daemon'); + timespan = require('timespan'), + winston = require('winston'); -var forever = exports;
Setup forever.log
to be a custom winston
logger.
forever.log = new (winston.Logger)({
+ transports: [
+ new (winston.transports.Console)()
+ ]
+});
-Export version
and important Prototypes from lib/forever/*
forever.version = [0, 4, 0];
-forever.initialized = false;
+forever.log.cli();
Export version
and important Prototypes from lib/forever/*
forever.initialized = false;
forever.root = path.join(process.env.HOME, '.forever');
forever.config = new nconf.stores.File({ file: path.join(forever.root, 'config.json') });
-forever.cli = require('forever/cli');
-forever.Forever = forever.Monitor = require('forever/monitor').Monitor;
Expose version through pkginfo
require('pkginfo')(module, 'version');
Initializes configuration for forever module
forever.load = function (options) {
Setup the incoming options with default options.
options = options || {};
- options.root = options.root || forever.root,
+Initializes configuration for forever module
forever.load = function (options) {
Setup the incoming options with default options.
options = options || {};
+ options.root = options.root || forever.root;
options.pidPath = options.pidPath || path.join(options.root, 'pids');
-
If forever is initalized and the config directories are identical +
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.stores.File({ file: path.join(options.root, 'config.json') });
-
Try to load the forever config.json
from
-the specified location.
try {
- forever.config.loadSync();
- }
+
Try to load the forever config.json
from
+the specified location.
try { forever.config.loadSync() }
catch (ex) { }
forever.config.set('root', options.root);
forever.config.set('pidPath', options.pidPath);
-
Syncronously create the root
directory
+
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); }
+ try { fs.mkdirSync(dir, 0755) }
catch (ex) { }
}
tryCreate(forever.config.get('root'));
tryCreate(forever.config.get('pidPath'));
-
Attempt to save the new config.json
for forever
try {
- forever.config.saveSync();
- }
+
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();
Sets up debugging for this forever process
forever._debug = function () {
+ forever.config.set('debug', true);
+ forever.log.add(winston.transports.File, {
+ filename: path.join(forever.config.get('root'), 'forever.debug.log')
+ });
+}
Ensure forever will always be loaded the first time it is required.
forever.load();
Ensures that the logFile doesn't exist and that the target script does exist before executing callback.
forever.stat = function (logFile, script, callback) {
- var logAppend,
- realCallback = callback;
+ var logAppend;
if (arguments.length === 4) {
logAppend = callback;
- realCallback = arguments[3];
+ callback = arguments[3];
}
fs.stat(script, function (err, stats) {
- if (err) return realCallback(new Error('script ' + script + ' does not exist.'));
-
- if (logAppend) {
- realCallback(null);
- return;
+ if (err) {
+ return callback(new Error('script ' + script + ' does not exist.'));
}
- fs.stat(logFile, function (err, stats) {
- if (!err) return realCallback(new Error('log file ' + logFile + ' exists.'));
- realCallback(null);
+ return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) {
+ return !err
+ ? callback(new Error('log file ' + logFile + ' exists.'))
+ : callback(null);
});
});
-};
Starts a script with forever
forever.start = function (script, options) {
return new forever.Monitor(script, options).start();
-};
Starts a script with forever as a daemon
forever.startDaemon = function (script, options) {
options.logFile = forever.logFilePath(options.logFile);
options.pidFile = forever.pidFilePath(options.pidFile);
+
var runner = new forever.Monitor(script, options);
fs.open(options.logFile, options.appendLog ? 'a+' : 'w+', function (err, fd) {
- if (err) return runner.emit('error', err);
+ if (err) {
+ return runner.emit('error', err);
+ }
var pid = daemon.start(fd);
- daemon.lock(options.pidFile);
Remark: This should work, but the fd gets screwed up + daemon.lock(options.pidFile);
Remark: This should work, but the fd gets screwed up with the daemon process.
process.on('exit', function () { @@ -129,7 +143,7 @@
We need to spawn a new process running the forever CLI + async.forEach(procs, function (proc, next) {
We need to spawn a new process running the forever CLI
here because we want each process to daemonize separately
without the main process running forever restart myscript.js
daemonizing itself.
var restartCommand = [
@@ -188,6 +202,10 @@ @format {boolean} Indicated if we should CLI format the returned output.
'--append'
];
+ if (proc.silent) {
+ restartCommand.push('--silent');
+ }
+
if (proc.outFile) {
restartCommand.push('-o', path.join(proc.sourceDir, proc.outFile));
}
@@ -208,28 +226,31 @@ @format {boolean} Indicated if we should CLI format the returned output.
emitter.emit('error', new Error('Cannot find forever process: ' + target));
}
});
-
Bubble up the error to the appropriate EventEmitter instance.
runner.on('error', function (err) {
+
Bubble up the error to the appropriate EventEmitter instance.
runner.on('error', function (err) {
emitter.emit('error', err);
});
return emitter;
-};
Finds the process with the specified index.
forever.findByIndex = function (index, processes) {
- return processes && [processes[parseInt(index)]];
-};
Finds the process with the specified script name.
forever.findByScript = function (script, processes) {
- return processes.filter(function (p) { return p.file === script });
-};
Returns the list of all process data managed by forever.
forever.list = function (format, procs) {
- var formatted = [];
+ var formatted;
procs = procs || getAllProcesses();
- if (!procs) return null;
+ if (!procs) {
+ return null;
+ }
if (format) {
- var index = 0, maxLen = 0;
Iterate over the procs to see which has the longest options string
procs.forEach(function (proc) {
- proc.length = [proc.file].concat(proc.options).join(' ').length;
- if (proc.length > maxLen) maxLen = proc.length;
- });
-
- procs.forEach(function (proc) {
Create padding string to keep output aligned
var padding = new Array(maxLen - proc.length + 1).join(' ');
- formatted.push(formatProcess(proc, index, padding));
+ var index = 0, rows = [
+ [' ', 'command ', 'script', 'forever ', 'pid', 'logfile', 'uptime']
+ ];
+
Iterate over the procs to see which has the +longest options string
procs.forEach(function (proc) {
+ rows.push([
+ '[' + index + ']',
+ (proc.command || 'node').grey,
+ [proc.file].concat(proc.options).join(' ').grey,
+ proc.foreverPid,
+ proc.pid,
+ proc.logFile ? proc.logFile.magenta : '',
+ timespan.fromDates(new Date(proc.ctime), new Date()).toString().yellow
+ ]);
+
index++;
});
+
+ formatted = cliff.stringifyRows(rows, [
+ 'white',
+ 'grey',
+ 'grey',
+ 'white',
+ 'white',
+ 'magenta',
+ 'yellow'
+ ]);
}
- return format ? formatted.join('\n') : procs;
-};
Utility function for removing excess pid and -config, and log files used by forever.
forever.cleanUp = function (cleanLogs) {
+config, and log files used by forever.
forever.cleanUp = function (cleanLogs, allowManager) {
var emitter = new events.EventEmitter(),
- processes = getAllProcesses(true);
+ processes = getAllProcesses(true),
+ pidPath = forever.config.get('pidPath');
- if (cleanLogs) forever.cleanLogsSync(processes);
+ if (cleanLogs) {
+ forever.cleanLogsSync(processes);
+ }
if (processes && processes.length > 0) {
- var checked = 0;
- processes.forEach(function (proc) {
- checkProcess(proc.pid, function (child) {
- checkProcess(proc.foreverPid, function (manager) {
- if (!child && !manager || proc.dead) {
- fs.unlink(path.join(forever.config.get('pidPath'), proc.uid + '.fvr'), function () {
- fs.unlink(path.join(forever.config.get('pidPath'), proc.uid + '.pid'), function () {
Ignore errors
});
- });
-
- if (cleanLogs && proc.logFile) {
- fs.unlink(proc.logFile, function () { /* Ignore Errors */ });
- }
- }
-
- checked++;
- if (checked === processes.length) {
- emitter.emit('cleanUp');
- }
+ function tryUnlink (file, next) {
+ fs.unlink(file, function () {
Ignore errors (in case the file doesnt exist).
next();
+ });
+ }
+
+ function unlinkProcess (proc, done) {
+ var files = [
+ path.join(pidPath, proc.uid + '.fvr'),
+ path.join(pidPath, proc.uid + '.pid')
+ ];
+
+ async.forEach(files, tryUnlink, function () {
+ if (cleanLogs && proc.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) {
+ forever.checkProcess(proc.pid, function (child) {
+ proc.child = child;
+ forever.checkProcess(proc.foreverPid, function (manager) {
+ proc.manager = manager;
+ cleanProcess(proc, next);
});
});
- });
+ }
+
+ (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 () {
@@ -328,95 +413,84 @@ @procs {Array} Set of processes to list format.
}
return emitter;
-};
Removes all log files from the root forever directory that do not belong to current running forever processes.
forever.cleanLogsSync = function (processes) {
- var files = fs.readdirSync(forever.config.get('root')),
+ var root = forever.config.get('root'),
+ files = fs.readdirSync(root),
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(forever.config.get('root'), file));
+ fs.unlinkSync(path.join(root, file));
}
});
-};
Returns a pseude-random ASCII string which contains at least the specified number of bits of entropy the return value is a string of length ⌈bits/6⌉ of characters from the base64 alphabet.
forever.randomString = function (bits) {
- var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+',
+ var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$',
rand, i, ret = '';
-
in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53)
while (bits > 0) {
- rand = Math.floor(Math.random()*0x100000000) // 32-bit integer
base 64 means 6 bits per character, so we use the top 30 bits from rand to give 30/6=5 characters.
for (i=26; i>0 && bits>0; i-=6, bits-=6) {
+
in v8, Math.random() yields 32 pseudo-random bits (in spidermonkey it gives 53)
while (bits > 0) {
+ rand = Math.floor(Math.random()*0x100000000) // 32-bit integer
base 64 means 6 bits per character, so we use the top 30 bits from rand to give 30/6=5 characters.
for (i=26; i>0 && bits>0; i-=6, bits-=6) {
ret+=chars[0x3F & rand >>> i];
}
}
+
return ret;
-};
Determines the full logfile path name
forever.logFilePath = function(logFile, uid) {
- if (logFile && logFile[0] === '/') {
- return logFile;
- } else {
- return path.join(forever.config.get('root'), logFile || (uid || 'forever') + '.log');
- }
-};
Determines the full pid file path name
forever.pidFilePath = function(pidFile) {
- if (pidFile && pidFile[0] === '/') {
- return pidFile;
- } else {
- return path.join(forever.config.get('pidPath'), pidFile);
- }
-};
Utility function to check to see if a pid is running
function checkProcess (pid, callback) {
+Utility function to check to see if a pid is running
forever.checkProcess = function (pid, callback) {
if (!pid) {
return callback(false);
}
exec('ps ' + pid + ' | grep -v PID', function (err, stdout, stderr) {
- if (err) return callback(false);
+ if (err) {
+ return callback(false);
+ }
+
callback(stdout.indexOf(pid) !== -1);
});
-};
Returns a formatted string for the process @proc at -the specified index.
function formatProcess (proc, index, padding) {
Create an array of the output we can later join
return [' [' + index + ']', proc.file.grey]
- .concat(proc.options.map(function (opt) { return opt.grey }))
- .concat([padding + '[' + proc.pid + ',', proc.foreverPid + ']'])
- .concat(proc.logFile ? proc.logFile.magenta : '')
- .concat(timespan.fromDates(new Date(proc.ctime), new Date()).toString().yellow)
- .join(' ');
-};
Returns all data for processes managed by forever.
function getAllProcesses (findDead) {
- var results = [], processes = {},
+ var results = [],
+ processes = {},
files = fs.readdirSync(forever.config.get('pidPath'));
- if (files.length === 0) return null;
+ if (files.length === 0) {
+ return null;
+ }
files.forEach(function (file) {
try {
@@ -428,7 +502,7 @@ @findDead {boolean} Optional parameter that indicates to return dead procs
switch (ext) {
case '.pid':
var pid = parseInt(data);
- if (!processes[uid]) processes[uid] = {
+ processes[uid] = processes[uid] || {
foreverPid: pid,
uid: uid
};
@@ -440,20 +514,25 @@ @findDead {boolean} Optional parameter that indicates to return dead procs
break;
}
}
- catch (ex) {
Ignore errors
processes[uid] = {
+ catch (ex) {
Ignore errors
processes[uid] = {
uid: uid
};
}
});
Object.keys(processes).forEach(function (key) {
- if (!processes[key].pid && !findDead) return;
- else if (!processes[key].pid) processes[key].dead = true;
+ if (!processes[key].pid && !findDead) {
+ return;
+ }
+ else if (!processes[key].pid) {
+ processes[key].dead = true;
+ }
+
results.push(processes[key]);
});
return results;
-};
Returns the set of all pids managed by forever. e.x. [{ pid: 12345, foreverPid: 12346 }, ...]
function getAllPids (processes) {
diff --git a/docs/forever/cli.html b/docs/forever/cli.html
index d58cc31e..dbba4835 100644
--- a/docs/forever/cli.html
+++ b/docs/forever/cli.html
@@ -8,27 +8,12 @@
var sys = require('sys'),
path = require('path'),
- eyes = require('eyes'),
- winston = require('winston'),
- forever = require('forever');
+ cliff = require('cliff'),
+ forever = require('../forever');
var cli = exports;
-var reserved = ['root', 'pidPath'];
-
-var inspect = eyes.inspector({ stream: null,
- styles: { // Styles applied to stdout
- all: null, // Overall style applied to everything
- label: 'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]`
- other: 'inverted', // Objects which don't have a literal representation, such as functions
- key: 'grey', // The keys in object literals, like 'a' in `{a: 1}`
- special: 'grey', // null, undefined...
- number: 'blue', // 1, 2, 3, etc
- bool: 'magenta', // true false
- regexp: 'green', // /\d+/
- string: 'yellow'
- }
-});
action
.Executes the action
in forever with the specified file
and options
.
cli.exec = function (action, file, options) {
- winston.info('Running action: ' + action.yellow);
+ if (action) {
+ forever.log.info('Running action: ' + action.yellow);
+ }
- winston.silly('Tidying ' + forever.config.get('root'));
+ forever.log.silly('Tidying ' + forever.config.get('root'));
var tidy = forever.cleanUp(action === 'cleanlogs');
tidy.on('cleanUp', function () {
- winston.silly(forever.config.get('root') + ' tidied.');
+ forever.log.silly(forever.config.get('root') + ' tidied.');
- if (file && action !== 'set' && action !== 'clear') {
- winston.info('Forever processing file: ' + file.grey);
+ if (file && action === 'start') {
+ forever.log.info('Forever processing file: ' + file.grey);
+ }
+
+ if (options.command) {
+ forever.log.info('Forever using command: ' + options.command.grey);
}
if (options && action !== 'set') {
- winston.silly('Forever using options', options);
+ forever.log.silly('Forever using options', options);
}
If there is no action then start in the current
process with the specified file
and options
.
if (!action) {
return cli.start(file, options);
@@ -58,8 +49,7 @@ @options {Object} Options to pass to forever for the action
.return;
}
- var daemon = true;
- cli[action](file, options, daemon);
+ cli[action](file, options, true);
});
};
Lists all currently running forever processes.
cli.list = function () {
var processes = forever.list(true);
if (processes) {
- winston.info('Forever processes running');
- sys.puts(processes);
+ forever.log.info('Forever processes running');
+ processes.split('\n').forEach(function (line) {
+ forever.log.data(line);
+ })
}
else {
- winston.info('No forever processes running');
+ forever.log.info('No forever processes running');
}
};
Lists all of the configuration in ~/.forever/config.json
.
cli.config = function () {
var keys = Object.keys(forever.config.store),
- conf = inspect(forever.config.store);
+ conf = cliff.inspect(forever.config.store);
if (keys.length <= 2) {
conf = conf.replace(/\{\s/, '{ \n')
@@ -148,7 +149,7 @@ @file {string} Target process to restart
}
conf.split('\n').forEach(function (line) {
- winston.info(line);
+ forever.log.data(line);
});
};
key
Sets the specified key
/ value
pair in the
forever user config.
cli.set = function (key, value) {
if (!key || !value) {
- return winston.error('Both <key> and <value> are required.');
+ return forever.log.error('Both <key> and <value> are required.');
}
updateConfig(function () {
- winston.info('Setting forever config: ' + key.grey);
+ forever.log.info('Setting forever config: ' + key.grey);
forever.config.set(key, value);
});
};
~/.forever/config.json
Removes the specified key
from the forever user config.
cli.clear = function (key) {
if (reserved.indexOf(key) !== -1) {
- winston.warn('Cannot clear reserved config: ' + key.grey);
- winston.warn('Use `forever set ' + key + '` instead');
+ forever.log.warn('Cannot clear reserved config: ' + key.grey);
+ forever.log.warn('Use `forever set ' + key + '` instead');
return;
}
updateConfig(function () {
- winston.info('Clearing forever config: ' + key.grey);
+ forever.log.info('Clearing forever config: ' + key.grey);
forever.config.clear(key);
});
-};
Helper function that sets up the pathing for the specified file
then stats the appropriate files and responds.
function tryStart (file, options, callback) {
- var fullLog, fullScript
+ var fullLog, fullScript;
fullLog = forever.logFilePath(options.logFile, options.uid);
fullScript = path.join(options.sourceDir, file);
forever.stat(fullLog, fullScript, options.appendLog, function (err) {
if (err) {
- winston.error('Cannot start forever: ' + err.message);
+ forever.log.error('Cannot start forever');
+ forever.log.error(err.message);
process.exit(-1);
}
callback();
});
-}
Creates a new instance of forever with specified params.
var Monitor = exports.Monitor = function (script, options) {
events.EventEmitter.call(this);
-
- options = options || {};
- this.silent = options.silent || false;
- this.forever = options.forever || false;
- this.command = options.command || 'node';
- this.sourceDir = options.sourceDir;
- this.minUptime = typeof options.minUptime !== 'number' ? 2000 : options.minUptime;
- this.options = options.options || [];
- this.spawnWith = options.spawnWith || null;
- this.uid = options.uid || forever.randomString(24);
- this.max = options.max;
- this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
- this.pidFile = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
- this.outFile = options.outFile;
- this.errFile = options.errFile;
- this.logger = options.logger || new (winston.Logger)({
+
Setup basic configuration options
options = options || {};
+ this.silent = options.silent || false;
+ this.forever = options.forever || false;
+ this.uid = options.uid || forever.randomString(24);
+ this.pidFile = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
+ this.fvrFile = path.join(forever.config.get('pidPath'), this.uid + '.fvr');
+ this.max = options.max;
+ this.childExists = false;
+ this.times = 0;
+
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' ? 2000 : options.minUptime;
+ this.spinSleepTime = options.spinSleepTime || null;
+
Setup the command to spawn and the options to pass +to that command.
this.command = options.command || 'node';
+ this.options = options.options || [];
+ this.spawnWith = options.spawnWith || {};
+ this.sourceDir = options.sourceDir;
+ this.cwd = options.cwd || null;
+ this.env = options.env || {};
+
Setup log files and logger for this instance.
this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
+ this.outFile = options.outFile;
+ this.errFile = options.errFile;
+ this.logger = options.logger || new (winston.Logger)({
transports: [new winston.transports.Console({ silent: this.silent })]
});
-
Extend from the winston logger.
this.logger.extend(this);
-
- this.childExists = false;
+
Extend from the winston logger.
this.logger.extend(this);
if (Array.isArray(script)) {
this.command = script[0];
@@ -53,15 +58,13 @@ @options {Object} Configuration for this instance.
if (this.sourceDir) {
this.options[0] = path.join(this.sourceDir, this.options[0]);
}
-
If we should log stdout, open a file buffer
if (this.outFile) {
+
If we should log stdout, open a file buffer
if (this.outFile) {
this.stdout = fs.createWriteStream(this.outFile, { flags: 'a+', encoding: 'utf8', mode: 0666 });
}
-
If we should log stderr, open a file buffer
if (this.errFile) {
+
If we should log stderr, open a file buffer
if (this.errFile) {
this.stderr = fs.createWriteStream(this.errFile, { flags: 'a+', encoding: 'utf8', mode: 0666 });
}
-
- this.times = 0;
-};
Inherit from events.EventEmitter
sys.inherits(Monitor, events.EventEmitter);
Inherit from events.EventEmitter
sys.inherits(Monitor, events.EventEmitter);
Hook all stream data and process it
function listenTo (stream) {
+
Hook all stream data and process it
function listenTo (stream) {
function ldata (data) {
- if (!self.silent && !self[stream]) {
If we haven't been silenced, and we don't have a file stream + if (!self.silent && !self[stream]) {
If we haven't been silenced, and we don't have a file stream to output to write to the process stdout stream
process.stdout.write(data);
}
- else if (self[stream]) {
If we have been given an output file for the stream, write to it
self[stream].write(data);
+ else if (self[stream]) {
If we have been given an output file for the stream, write to it
self[stream].write(data);
}
self.emit(stream, data);
@@ -108,30 +111,49 @@ @restart {boolean} Value indicating whether this is a restart.
child[stream].removeListener('data', ldata);
});
}
-
Listen to stdout and stderr
listenTo('stdout');
+
Listen to stdout and stderr
listenTo('stdout');
listenTo('stderr');
child.on('exit', function (code) {
var spinning = Date.now() - self.ctime < self.minUptime;
self.warn('Forever detected script exited with code: ' + code);
-
- if ((self.forever || self.times < self.max) && !self.forceStop && !spinning) {
- self.times++;
+
+ function letChildDie() {
+ self.running = false;
If had to write to an stdout file, close it
if (self.stdout) {
+ self.stdout.end();
+ }
+
If had to write to an stderr file, close it
if (self.stderr) {
+ self.stderr.end();
+ }
+
+ fs.unlink(self.fvrFile, function () {
+ self.emit('exit', self, spinning);
+ });
+ }
+
+ function restartChild() {
process.nextTick(function () {
self.warn('Forever restarting script for ' + self.times + ' time');
self.start(true);
});
}
+
+ self.times++;
+
+ if (self.forceStop || (!self.forever && self.times >= self.max)
+ || (spinning && typeof self.spinSleepTime !== 'number')) {
+ letChildDie();
+ }
+ else if (spinning) {
+ setTimeout(restartChild, self.spinSleepTime);
+ }
else {
- this.running = false;
-
If had to write to an stdout file, close it
if (self.stdout) self.stdout.end();
If had to write to an stderr file, close it
if (self.stderr) self.stderr.end();
-
- self.emit('exit', self, spinning);
+ restartChild();
}
});
return this;
-};
Tries to spawn the target Forever child process. Depending on configuration, it checks the first argument of the options @@ -147,11 +169,15 @@
Persists this instance of forever to disk.
Monitor.prototype.save = function () {
var self = this;
+
if (!this.running) {
process.nextTick(function () {
self.emit('error', new Error('Cannot save Forever instance that is not running'));
@@ -159,30 +185,34 @@ @restart {boolean} Value indicating whether this is a restart.
}
var childData = {
- uid: this.uid,
ctime: this.ctime,
- pid: this.child.pid,
+ command: this.command,
+ file: this.options[0],
foreverPid: process.pid,
logFile: this.logFile,
options: this.options.slice(1),
- file: this.options[0]
+ pid: this.child.pid,
+ silent: this.silent,
+ uid: this.uid
};
- this.childData = childData;
- if (this.pidFile) childData.pidFile = this.pidFile;
- if (this.outFile) childData.outFile = this.outFile;
- if (this.errFile) childData.errFile = this.errFile;
+ ['pidFile', 'outFile', 'errFile', 'env', 'cwd'].forEach(function (key) {
+ if (self[key]) {
+ childData[key] = self[key];
+ }
+ });
+
if (this.sourceDir) {
childData.sourceDir = this.sourceDir;
childData.file = childData.file.replace(this.sourceDir + '/', '');
}
- var childPath = path.join(forever.config.get('pidPath'), this.uid + '.fvr');
- fs.writeFile(childPath, JSON.stringify(childData, null, 2), function (err) {
- if (err) self.emit('error', err);
- self.emit('save', childPath, childData);
+ this.childData = childData;
+
+ fs.writeFile(this.fvrFile, JSON.stringify(childData, null, 2), function (err) {
+ return err ? self.emit('error', err) : self.emit('save', self.fvrFile, childData);
});
-
Setup the forever process to listen to +
Setup the forever process to listen to SIGINT and SIGTERM events so that we can clean up the *.pid file
@@ -200,15 +230,15 @@
return this;
-};
Restarts the target script associated with this instance.
Monitor.prototype.restart = function () {
return this.kill(false);
-};
Stops the target script associated with this instance. Prevents it from auto-respawning
Monitor.prototype.stop = function () {
return this.kill(true);
-};
Set an instance variable here to indicate this + 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) {
@@ -232,6 +262,26 @@ @forceStop {boolean} Value indicating whether short circuit forever auto-res
}
return this;
+};
Returns the environment variables that should be passed along +to the target process spawned by this instance.
Monitor.prototype._getEnv = function () {
+ var self = this,
+ extra = Object.keys(this.env),
+ merged = {};
+
+ if (extra.length === 0) {
+ return process.env;
+ }
+
+ 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 (k) { addKey(k, process.env) });
+ extra.forEach(function (k) { addKey(k, self.env) });
+
+ return merged;
};