From 124cc25051aef93fdfcc99ec47546c1235464639 Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 23 Dec 2010 12:38:44 -0500 Subject: [PATCH] [fix api bin test] Check for scripts with fs.stat() before running them. Use process.kill instead of exec('kill'). Clean logs from command line. Display log file in forever list. Emit save event. --- bin/forever | 9 ++++- lib/forever.js | 94 ++++++++++++++++++++++++++++++-------------- test/forever-test.js | 15 ++++++- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/bin/forever b/bin/forever index 861b167a..f0903b9d 100755 --- a/bin/forever +++ b/bin/forever @@ -107,7 +107,14 @@ loader.on('load', function () { options.uid = uid; options.pidFile = 'forever' + uid + '.pid'; options.logFile = argv.l || 'forever' + uid + '.log' - forever.startDaemon(file, options); + + fs.stat(file, function (err, stats) { + if (err) { + sys.puts('Cannot start forever: ' + file + ' does not exist.'); + process.exit(0); + } + forever.startDaemon(file, options); + }); break; case 'stop': diff --git a/lib/forever.js b/lib/forever.js index c2690e64..ee7fe69c 100644 --- a/lib/forever.js +++ b/lib/forever.js @@ -18,15 +18,15 @@ var sys = require('sys'), var forever = exports, config; // -// function load () +// function load (options, [callback]) // Initializes configuration for forever module // forever.load = function (options, callback) { var emitter = new events.EventEmitter(); - options = options || {}; + options = options || {}; options.root = options.root || path.join('/tmp', 'forever'), options.pidPath = options.pidPath || path.join(options.root, 'pids'); - config = options; + config = options; // Create the two directories, ignoring errors fs.mkdir(config.root, 0755, function (err) { @@ -95,12 +95,11 @@ forever.stop = function (index, format) { proc = processes && processes[index]; if (proc) { - exec('kill ' + proc.foreverPid, function () { - exec('kill ' + proc.pid, function () { - if (format) proc = formatProcess(proc, index, ''); - emitter.emit('stop', proc); - }); - }); + process.kill(proc.foreverPid); + process.kill(proc.pid); + + if (format) proc = formatProcess(proc, index, ''); + emitter.emit('stop', proc); } else { process.nextTick(function () { @@ -126,13 +125,15 @@ forever.stopAll = function (format) { } if (pids && processes) { - var fPids = pids.map(function (pid) { return pid.foreverPid }).join(' '), - cPids = pids.map(function (pid) { return pid.pid }).join(' '); - - exec('kill ' + fPids, function () { - exec('kill ' + cPids, function () { - emitter.emit('stopAll', processes); - }); + var fPids = pids.map(function (pid) { return pid.foreverPid }), + cPids = pids.map(function (pid) { return pid.pid }); + + fPids.concat(cPids).forEach(function (pid) { + process.kill(pid); + }); + + process.nextTick(function () { + emitter.emit('stopAll', processes); }); } else { @@ -177,7 +178,7 @@ forever.list = function (format, procs) { // Utility function for removing excess pid and // config files used by forever. // -forever.cleanUp = function () { +forever.cleanUp = function (cleanLogs) { var emitter = new events.EventEmitter(), processes = getAllProcesses(); @@ -196,6 +197,10 @@ forever.cleanUp = function () { fs.unlink(path.join(config.pidPath, proc.foreverPid + '.fvr'), function () { // Ignore errors }); + + if (cleanLogs) { + fs.unlink(proc.logFile, function () { /* Ignore Errors */ }); + } } checked++; @@ -261,7 +266,8 @@ function formatProcess (proc, index, padding) { // Create an array of the output we can later join return [' [' + index + ']', proc.file.green] .concat(proc.options.map(function (opt) { return opt.green })) - .concat([padding + '[', proc.pid + ',', proc.foreverPid, ']']) + .concat([padding + '[' + proc.pid + ',', proc.foreverPid + ']']) + .concat(proc.logFile.magenta) .join(' '); }; @@ -273,9 +279,7 @@ function getAllProcesses () { var processes = []; try { var files = fs.readdirSync(config.pidPath); - if (files.length === 0) { - return null; - } + if (files.length === 0) return null; files = files.filter(function(file) { return /\.fvr$/.test(file) }); files.forEach(function (file) { @@ -322,6 +326,9 @@ var Forever = function (file, options) { options.silent = options.silent || false; options.forever = options.forever || false; options.command = options.command || 'node'; + options.options = options.options || []; + + this.childExists = false; if (Array.isArray(file)) { options.command = file[0]; @@ -361,7 +368,14 @@ Forever.prototype.start = function (restart) { }); } - var child = spawn(this.options.command, this.options.options); + var child = this.trySpawn(); + if (!child) { + process.nextTick(function () { + self.emit('error', new Error('Target script does not exist: ' + self.options.options[0])); + }); + return this; + } + this.child = child; this.running = true; @@ -418,6 +432,28 @@ Forever.prototype.start = function (restart) { return this; }; +// +// function trySpawn() +// Tries to spawn the target Forever child process. Depending on +// configuration, it checks the first argument of the options +// to see if the file exists. This is useful is you are +// trying to execute a script with an env: e.g. node myfile.js +// +Forever.prototype.trySpawn = function () { + if (this.options.command === 'node' || (this.options.checkFile + && !this.childExists)) { + try { + var stats = fs.statSync(this.options.options[0]); + this.childExists = true; + } + catch (ex) { + return false; + } + } + + return spawn(this.options.command, this.options.options); +}; + // // function save () // Persists this instance of forever to disk. @@ -446,6 +482,7 @@ Forever.prototype.save = function () { var childPath = path.join(config.pidPath, childData.foreverPid + '.fvr'); fs.writeFile(childPath, JSON.stringify(childData), function (err) { if (err) self.emit('error', err); + self.emit('save', childPath, childData); }); // @@ -460,13 +497,12 @@ Forever.prototype.save = function () { // process.exit(0); // }); // - //process.on('SIGTERM', function () { - // process.exit(0); - //}); - - //process.on('exit', function () { - // fs.unlinkSync(childPath); - //}); + // process.on('SIGTERM', function () { + // process.exit(0); + // }); + // process.on('exit', function () { + // fs.unlinkSync(childPath); + // }); return this; }; diff --git a/test/forever-test.js b/test/forever-test.js index a36a20a6..0b1d8d84 100644 --- a/test/forever-test.js +++ b/test/forever-test.js @@ -43,7 +43,7 @@ vows.describe('forever').addBatch({ } } }, - "when running error-on-timer sample three times": { + "running error-on-timer sample three times": { topic: function () { var child = new (forever.Forever)(path.join(__dirname, '..', 'samples', 'error-on-timer.js'), { max: 3, @@ -71,5 +71,18 @@ vows.describe('forever').addBatch({ "should get back moo": function (err, buf) { assert.equal(buf.toString(), 'moo\n'); } + }, + "attempting to start a script that doesn't exist": { + topic: function () { + var child = forever.start('invalid-path.js', { + max: 1, + silent: true + }); + child.on('error', this.callback.bind({}, null)); + }, + "should throw an error about the invalid file": function (err) { + assert.isNotNull(err); + assert.isTrue(err.message.indexOf('does not exist') !== -1); + } } }).export(module);