From c073c4790a917f66a25b5afbda359688bd5baa62 Mon Sep 17 00:00:00 2001 From: indexzero Date: Sun, 13 Feb 2011 22:21:05 -0500 Subject: [PATCH] [api] First pass at "restart" functionality, not 100% yet --- README.md | 4 ++- bin/forever | 18 ++++++++++++-- lib/forever.js | 56 +++++++++++++++++++++++++++++++++++++++--- lib/forever/monitor.js | 39 ++++++++++++++++++++++++++--- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7ca4b0a0..85cf3910 100644 --- a/README.md +++ b/README.md @@ -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:
-  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
diff --git a/bin/forever b/bin/forever
index 0edabad1..b0868c9e 100755
--- a/bin/forever
+++ b/bin/forever
@@ -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];
 }
@@ -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",
     "",
@@ -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');
diff --git a/lib/forever.js b/lib/forever.js
index 831a9349..8468290c 100644
--- a/lib/forever.js
+++ b/lib/forever.js
@@ -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,
@@ -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 = [];
@@ -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 () {
@@ -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.
diff --git a/lib/forever/monitor.js b/lib/forever/monitor.js
index 454330bc..acce4574 100644
--- a/lib/forever/monitor.js
+++ b/lib/forever/monitor.js
@@ -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);
@@ -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;