Skip to content

Commit

Permalink
[api] First pass at "restart" functionality, not 100% yet
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Feb 14, 2011
1 parent 7b9b4be commit c073c47
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 10 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ There are two distinct ways to use forever: through the command line interface,
You can use forever to run any kind of script continuously (whether it is written in node.js or not). The usage options are simple:

<pre>
usage: forever [start | stop | stopall | list | cleanlogs] [options] SCRIPT [script options]
usage: forever [start | stop | restart | stopall | list | cleanlogs] [options] SCRIPT [script options]

options:
start start SCRIPT as a daemon
stop stop the daemon SCRIPT
stopall stop all running forever scripts
restart restart the daemon SCRIPT
list list all running forever scripts
cleanlogs [CAREFUL] Deletes all historical forever log files


-m MAX Only run the specified script MAX times
-l LOGFILE Logs the forever output to LOGFILE
-o OUTFILE Logs stdout from child script to OUTFILE
Expand Down
18 changes: 16 additions & 2 deletions bin/forever
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var path = require('path'),
eyes = require('eyes'),
sys = require('sys');

var accepts = ['start', 'stop', 'stopall', 'list', 'cleanlogs'], action;
var accepts = ['start', 'stop', 'stopall', 'list', 'cleanlogs', 'restart'], action;
if (accepts.indexOf(process.argv[2]) !== -1) {
action = process.argv.splice(2,1)[0];
}
Expand All @@ -17,12 +17,13 @@ require.paths.unshift(path.join(__dirname, '..', 'lib'));
var forever = require('forever');

var help = [
"usage: forever [start | stop | stopall | list | cleanlogs] [options] SCRIPT [script options]",
"usage: forever [start | stop | restart | stopall | list | cleanlogs] [options] SCRIPT [script options]",
"",
"options:",
" start start SCRIPT as a daemon",
" stop stop the daemon SCRIPT",
" stopall stop all running forever scripts",
" restart restart the daemon SCRIPT",
" list list all running forever scripts",
" cleanlogs [CAREFUL] Deletes all historical forever log files",
"",
Expand Down Expand Up @@ -156,6 +157,19 @@ loader.on('load', function () {
});
break;

case 'restart':
var runner = forever.restart(file, true);
runner.on('restart', function (processes) {
if (processes) {
sys.puts('Forever restarted processes:');
sys.puts(processes);
}
else {
sys.puts('No forever processes running');
}
});
break;

case 'list':
var processes = forever.list(true);
sys.puts(processes ? processes : 'No forever processes running');
Expand Down
56 changes: 53 additions & 3 deletions lib/forever.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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,
Expand Down Expand Up @@ -111,10 +112,10 @@ forever.startDaemon = function (script, options) {
// ### function stop (target, [format])
// #### @target {string} Index or script name to stop
// #### @format {boolean} Indicated if we should CLI format the returned output.
// Stops the process with the specified index or script name
// Stops the process(es) with the specified index or script name
// in the list of all processes
//
forever.stop = function (target, format) {
forever.stop = function (target, format, restart) {
var emitter = new events.EventEmitter(),
processes = getAllProcesses(),
results = [];
Expand All @@ -125,7 +126,7 @@ forever.stop = function (target, format) {
if (procs && procs.length > 0) {
procs.forEach(function (proc) {
process.kill(proc.foreverPid);
process.kill(proc.pid);
process.kill(proc.pid);
});

process.nextTick(function () {
Expand All @@ -141,6 +142,55 @@ forever.stop = function (target, format) {
return emitter;
};

//
// ### function restart (target, format)
// #### @target {string} Index or script name to restart
// #### @format {boolean} Indicated if we should CLI format the returned output.
// Restarts the process(es) with the specified index or script name
// in the list of all processes
//
forever.restart = function (target, format) {
var emitter = new events.EventEmitter(),
runner = forever.stop(target, false);

runner.on('stop', function (procs) {
if (procs && procs.length > 0) {
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 = [
'forever',
'start',
// Remark: Is using a new logfile good behavior?
//'-l', path.basename(proc.logFile),
proc.file,
proc.options.join(' ')
].join(' ');

exec(restartCommand, function (err, stdout, stderr) {
next();
});
}, function () {
emitter.emit('restart', format ? forever.list(true, procs) : procs);
});
}
else {
emitter.emit('error', new Error('Cannot find forever process: ' + target));
}
});

// Bubble up the error to the appropriate EventEmitter instance.
runner.on('error', function (err) {
emitter.emit('error', err);
});

return emitter;
};

//
// ### function findByIndex (index, processes)
// #### @index {string} Index of the process to find.
Expand Down
39 changes: 35 additions & 4 deletions lib/forever/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Monitor.prototype.start = function (restart) {
self.log('Forever detected script exited with code: ' + code);
self.times++;

if (self.forever || self.times < self.max) {
if ((self.forever || self.times < self.max) && !self.forceStop) {
process.nextTick(function () {
self.log('Forever restarting script for ' + self.times + ' time');
self.start(true);
Expand Down Expand Up @@ -218,20 +218,51 @@ Monitor.prototype.log = function (message) {
return this;
};

//
// ### function restart ()
// Restarts the target script associated with this instance.
//
Monitor.prototype.restart = function () {
// Listen to start to emit 'restart'
this.once('start', function () {
self.emit('restart');
});

return this.kill(false);
};

//
// ### function stop ()
// Stops the target script associated with this instance.
// Stops the target script associated with this instance. Prevents it from auto-respawning
//
Monitor.prototype.stop = function () {
return this.kill(true);
};

//
// ### function kill (forceStop)
// #### @forceStop {boolean} Value indicating whether short circuit forever auto-restart.
// Kills the ChildProcess object associated with this instance.
//
Monitor.prototype.kill = function (forceStop) {
var self = this;

if (!this.child || !this.running) {
var self = this;
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;

this.child.kill();
this.emit('stop', this.childData)
this.emit('stop', this.childData);
}

return this;
Expand Down

0 comments on commit c073c47

Please sign in to comment.