From 11ffce8d1720893340a5fac4f3c54dca3a01eaaa Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 12:00:47 -0600 Subject: [PATCH 01/12] Load the forever lib relative to the binary rather than using module notation. --- bin/forever | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/forever b/bin/forever index 4495b063..cd6ccf9c 100755 --- a/bin/forever +++ b/bin/forever @@ -14,7 +14,7 @@ if (accepts.indexOf(process.argv[2]) !== -1) { var argv = require('optimist').argv; require.paths.unshift(path.join(__dirname, '..', 'lib')); -var forever = require('forever'); +var forever = require('../lib/forever'); var help = [ "usage: forever [start | stop | restart | stopall | list | cleanlogs] [options] SCRIPT [script options]", From b29a258680df1548292324fda785ea6580077b4a Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 12:23:43 -0600 Subject: [PATCH 02/12] Return non-zero error code on tryStart failure --- bin/forever | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/forever b/bin/forever index cd6ccf9c..d7ba14b9 100755 --- a/bin/forever +++ b/bin/forever @@ -132,7 +132,7 @@ function tryStart (callback) { forever.stat(fullLog, fullScript, function (err) { if (err) { winston.error('Cannot start forever: ' + err.message); - process.exit(0); + process.exit(-1); } callback(); From 8e323ca18070f2d47de15e39b0e5fafcfe01880b Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 12:24:46 -0600 Subject: [PATCH 03/12] Pass cwd to spawn --- bin/forever | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/forever b/bin/forever index d7ba14b9..cd9d32c3 100755 --- a/bin/forever +++ b/bin/forever @@ -107,6 +107,11 @@ if (!options.sourceDir) { options.sourceDir = file && file[0] !== '/' ? process.cwd() : '/'; } +// Pass the source dir to spawn +options.spawnWith = { + cwd: options.sourceDir +}; + // // Configure winston for forever based on the CLI options // From e9b2cd3d05f01b45c4ca522d9f68245e9a51e9f4 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 16:55:58 -0600 Subject: [PATCH 04/12] forever.logFilePath utility. Treat paths that start with / as paths relative to the root, not the forever root. --- bin/forever | 3 ++- lib/forever.js | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/bin/forever b/bin/forever index cd9d32c3..37ce2c2c 100755 --- a/bin/forever +++ b/bin/forever @@ -131,7 +131,8 @@ function tryStart (callback) { options.uid = uid; options.pidFile = 'forever' + uid + '.pid'; options.logFile = argv.l || 'forever' + uid + '.log'; - fullLog = path.join(forever.config.root, options.logFile); + + fullLog = forever.logFilePath(options.logFile); fullScript = path.join(options.sourceDir, file); forever.stat(fullLog, fullScript, function (err) { diff --git a/lib/forever.js b/lib/forever.js index 4805a735..26b59ace 100644 --- a/lib/forever.js +++ b/lib/forever.js @@ -108,7 +108,7 @@ forever.start = function (script, options) { // Starts a script with forever as a daemon // forever.startDaemon = function (script, options) { - options.logFile = path.join(config.root, options.logFile || 'forever.log'); + options.logFile = forever.logFilePath(options.logFile); options.pidFile = path.join(config.pidPath, options.pidFile); var runner = new forever.Monitor(script, options); @@ -387,6 +387,19 @@ forever.randomString = function (bits) { return ret; }; +// +// ### function logFilePath (logFile) +// #### @logFile {string} Log file path +// Determines the full logfile path name +// +forever.logFilePath = function(logFile) { + if (logFile && logFile[0] === "/") { + return logFile; + } else { + return path.join(forever.config.root, logFile || "forever.log"); + } +}; + // // ### function checkProcess (pid, callback) // #### @pid {string} pid of the process to check From 52184ae3fbd62e3a1ca1661f5279b3c8308fcbd7 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 21:20:51 -0600 Subject: [PATCH 05/12] forever.pidFilePath implementation --- lib/forever.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/forever.js b/lib/forever.js index 26b59ace..8a170f91 100644 --- a/lib/forever.js +++ b/lib/forever.js @@ -109,7 +109,7 @@ forever.start = function (script, options) { // forever.startDaemon = function (script, options) { options.logFile = forever.logFilePath(options.logFile); - options.pidFile = path.join(config.pidPath, options.pidFile); + options.pidFile = forever.pidFilePath(options.pidFile); var runner = new forever.Monitor(script, options); daemon.daemonize(options.logFile, options.pidFile, function (err, pid) { @@ -400,6 +400,19 @@ forever.logFilePath = function(logFile) { } }; +// +// ### function pidFilePath (pidFile) +// #### @logFile {string} Pid file path +// Determines the full pid file path name +// +forever.pidFilePath = function(pidFile) { + if (pidFile && pidFile[0] === "/") { + return pidFile; + } else { + return path.join(config.pidPath, pidFile); + } +}; + // // ### function checkProcess (pid, callback) // #### @pid {string} pid of the process to check From dca33d8e6435e4e6b14c968f625acd10bb47fcb4 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 21:22:30 -0600 Subject: [PATCH 06/12] CLI pidfile argument --- bin/forever | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/forever b/bin/forever index 37ce2c2c..e4f61213 100755 --- a/bin/forever +++ b/bin/forever @@ -27,16 +27,17 @@ var help = [ " 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", - " -e ERRFILE Logs stderr from child script to ERRFILE", - " -d SOURCEDIR The source directory for which SCRIPT is relative to", - " -p PATH Base path for all forever related filesĀ (pid files, etc.)", - " -c COMMAND COMMAND to execute (defaults to node)", - " -v, --verbose Turns on the verbose messages from Forever", - " -s, --silent Run the child script silencing stdout and stderr", - " -h, --help You're staring at it", + " -m MAX Only run the specified script MAX times", + " -l LOGFILE Logs the forever output to LOGFILE", + " -o OUTFILE Logs stdout from child script to OUTFILE", + " -e ERRFILE Logs stderr from child script to ERRFILE", + " -d SOURCEDIR The source directory for which SCRIPT is relative to", + " -p PATH Base path for all forever related filesĀ (pid files, etc.)", + " -c COMMAND COMMAND to execute (defaults to node)", + " --pidfile=PIDFILE The pid file", + " -v, --verbose Turns on the verbose messages from Forever", + " -s, --silent Run the child script silencing stdout and stderr", + " -h, --help You're staring at it", "", "[Long Running Process]", " The forever process will continue to run outputting log messages to the console.", @@ -59,6 +60,7 @@ var mappings = { 'm': 'max', 'o': 'outFile', 'p': 'path', + 'pidfile': 'pidFile', 's': 'silent', 'silent': 'silent', 'v': 'verbose', @@ -129,7 +131,7 @@ var config = { function tryStart (callback) { var fullLog, fullScript, uid = forever.randomString(16); options.uid = uid; - options.pidFile = 'forever' + uid + '.pid'; + options.pidFile = options.pidFile || 'forever' + uid + '.pid'; options.logFile = argv.l || 'forever' + uid + '.log'; fullLog = forever.logFilePath(options.logFile); From ab497f4516c04ee7d97f7f1ff5b3fb2e6b7233ed Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 21:25:09 -0600 Subject: [PATCH 07/12] forever.stat append flag --- lib/forever.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/forever.js b/lib/forever.js index 8a170f91..335739cc 100644 --- a/lib/forever.js +++ b/lib/forever.js @@ -76,17 +76,31 @@ 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 // Ensures that the logFile doesn't exist and that // the target script does exist before executing callback. // forever.stat = function (logFile, script, callback) { - fs.stat(logFile, function (err, stats) { - if (!err) return callback(new Error('log file ' + logFile + ' exists.')); - fs.stat(script, function (err, stats) { - if (err) return callback(new Error('script ' + script + ' does not exist.')); - callback(null); + var logAppend, + realCallback = callback; + if (arguments.length === 4) { + logAppend = callback; + realCallback = arguments[3]; + } + + fs.stat(script, function (err, stats) { + if (err) return realCallback(new Error('script ' + script + ' does not exist.')); + + if (logAppend) { + realCallback(null); + return; + } + + fs.stat(logFile, function (err, stats) { + if (!err) return realCallback(new Error('log file ' + logFile + ' exists.')); + realCallback(null); }); }); }; From 588b2bf42f97f969d2a9c02ec54480ff73643f44 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 21:26:07 -0600 Subject: [PATCH 08/12] Append log CLI --- bin/forever | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/forever b/bin/forever index e4f61213..23e0d05b 100755 --- a/bin/forever +++ b/bin/forever @@ -29,6 +29,7 @@ var help = [ "", " -m MAX Only run the specified script MAX times", " -l LOGFILE Logs the forever output to LOGFILE", + " -a, --append Append logs", " -o OUTFILE Logs stdout from child script to OUTFILE", " -e ERRFILE Logs stderr from child script to ERRFILE", " -d SOURCEDIR The source directory for which SCRIPT is relative to", @@ -57,6 +58,8 @@ var mappings = { 'e': 'errFile', 'd': 'sourceDir', 'l': 'logFile', + 'a': 'appendLog', + 'append': 'appendLog', 'm': 'max', 'o': 'outFile', 'p': 'path', @@ -137,7 +140,7 @@ function tryStart (callback) { fullLog = forever.logFilePath(options.logFile); fullScript = path.join(options.sourceDir, file); - forever.stat(fullLog, fullScript, function (err) { + forever.stat(fullLog, fullScript, options.appendLog, function (err) { if (err) { winston.error('Cannot start forever: ' + err.message); process.exit(-1); From 51bc6c084048189e57efc36a5acb7a223172bdac Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Feb 2011 22:03:11 -0600 Subject: [PATCH 09/12] Append log implementation --- lib/forever.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/forever.js b/lib/forever.js index 335739cc..8becc7ea 100644 --- a/lib/forever.js +++ b/lib/forever.js @@ -126,9 +126,13 @@ forever.startDaemon = function (script, options) { options.pidFile = forever.pidFilePath(options.pidFile); var runner = new forever.Monitor(script, options); - daemon.daemonize(options.logFile, options.pidFile, function (err, pid) { + + fs.open(options.logFile, options.appendLog ? 'a+' : 'w+', function (err, fd) { 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 // with the daemon process. @@ -136,7 +140,7 @@ forever.startDaemon = function (script, options) { // process.on('exit', function () { // fs.unlinkSync(options.pidFile); // }); - + process.pid = pid; runner.start(); }); From b181dd74126192a37afbc7bdf68747d5ad7c1cde Mon Sep 17 00:00:00 2001 From: kpdecker Date: Mon, 28 Feb 2011 00:06:19 -0600 Subject: [PATCH 10/12] Init.d Example script --- examples/initd-example | 95 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 examples/initd-example diff --git a/examples/initd-example b/examples/initd-example new file mode 100644 index 00000000..b37cbdca --- /dev/null +++ b/examples/initd-example @@ -0,0 +1,95 @@ +#!/bin/bash +# +# initd-example Node init.d +# +# chkconfig: 345 80 20 +# description: Node init.d example +# processname: node +# pidfile: /var/run/initd-example.pid +# logfile: /var/log/initd-example.log +# + +# Source function library. +. /etc/init.d/functions + +NAME=initd-example # Unique name for the application +PORT=1234 # Port (in this case the application uses process.env.PORT to set the port) +INSTANCE_DIR=/var/www/$NAME # Location of the application source +SOURCE_NAME=main.js # Name os the applcation entry point script + +user=apache +pidfile=/var/run/$NAME.pid +logfile=/var/log/$NAME.log + +node=node +forever=forever +awk=awk +sed=sed + +start() { + echo "Starting $NAME node instance: " + + if [ "$id" = "" ]; then + # Create the log and pid files, making sure that the target use has access to them + touch $logfile + chown $user $logfile + + touch $pidfile + chown $user $pidfile + + # Launch the application + daemon --user=$user \ + env PORT=$PORT \ + $forever start --pidfile $pidfile -l $logfile -a -d $INSTANCE_DIR $SOURCE_NAME + RETVAL=$? + else + echo "Instance already running" + RETVAL=0 + fi +} + +restart() { + echo -n "Restarting $NAME node instance : " + if [ "$id" != "" ]; then + $forever restart $id + RETVAL=$? + else + start + fi +} + +stop() { + echo -n "Shutting down $NAME node instance : " + if [ "$id" != "" ]; then + $forever stop $id + else + echo "Instance is not running"; + fi + RETVAL=$? +} + +getForeverId() { + local pid=$(pidofproc $pidfile) + $forever list | $sed -e 's/\x1b\[[0-9; ]*m//g' | $awk "\$4 == \"$pid]\" { gsub(/[\[\]]/, \"\", \$1); print \$1; }" +} +id=$(getForeverId) + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status -p ${pidfile} + ;; + restart) + restart + ;; + *) + echo "Usage: {start|stop|status|restart}" + exit 1 + ;; +esac +exit $RETVAL From 13bf64530bd99c7e2ec412d20c7a471c89491815 Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Mar 2011 18:23:32 -0500 Subject: [PATCH 11/12] Add custom root directory to the initd-example (For cases where /tmp is removed) --- examples/initd-example | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/initd-example b/examples/initd-example index b37cbdca..7c0307a4 100644 --- a/examples/initd-example +++ b/examples/initd-example @@ -20,6 +20,7 @@ SOURCE_NAME=main.js # Name os the applcation entry point script user=apache pidfile=/var/run/$NAME.pid logfile=/var/log/$NAME.log +forever_dir=/var/run/forever # Forever root directory. node=node forever=forever @@ -40,7 +41,7 @@ start() { # Launch the application daemon --user=$user \ env PORT=$PORT \ - $forever start --pidfile $pidfile -l $logfile -a -d $INSTANCE_DIR $SOURCE_NAME + $forever start -p $forever_dir --pidfile $pidfile -l $logfile -a -d $INSTANCE_DIR $SOURCE_NAME RETVAL=$? else echo "Instance already running" @@ -51,7 +52,7 @@ start() { restart() { echo -n "Restarting $NAME node instance : " if [ "$id" != "" ]; then - $forever restart $id + $forever restart -p $forever_dir $id RETVAL=$? else start @@ -61,7 +62,7 @@ restart() { stop() { echo -n "Shutting down $NAME node instance : " if [ "$id" != "" ]; then - $forever stop $id + $forever stop -p $forever_dir $id else echo "Instance is not running"; fi @@ -70,7 +71,7 @@ stop() { getForeverId() { local pid=$(pidofproc $pidfile) - $forever list | $sed -e 's/\x1b\[[0-9; ]*m//g' | $awk "\$4 == \"$pid]\" { gsub(/[\[\]]/, \"\", \$1); print \$1; }" + $forever list -p $forever_dir | $sed -e 's/\x1b\[[0-9; ]*m//g' | $awk "\$4 == \"$pid]\" { gsub(/[\[\]]/, \"\", \$1); print \$1; }" } id=$(getForeverId) From 95434b394aca9fe009673c56f0449920ac742d5b Mon Sep 17 00:00:00 2001 From: kpdecker Date: Sun, 27 Mar 2011 18:23:54 -0500 Subject: [PATCH 12/12] Proper pid lookup in getForeverId --- examples/initd-example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/initd-example b/examples/initd-example index 7c0307a4..e2e1818e 100644 --- a/examples/initd-example +++ b/examples/initd-example @@ -70,7 +70,7 @@ stop() { } getForeverId() { - local pid=$(pidofproc $pidfile) + local pid=$(pidofproc -p $pidfile) $forever list -p $forever_dir | $sed -e 's/\x1b\[[0-9; ]*m//g' | $awk "\$4 == \"$pid]\" { gsub(/[\[\]]/, \"\", \$1); print \$1; }" } id=$(getForeverId)