summaryrefslogtreecommitdiff
path: root/node_modules/forever/lib/forever.js
diff options
context:
space:
mode:
authoryo mama <pepper@scannerjammer.com>2015-04-04 01:00:59 -0700
committeryo mama <pepper@scannerjammer.com>2015-04-04 01:00:59 -0700
commitc7c22e3db1c826bcfb2bc66651ec480aae0d4ae0 (patch)
tree8546df448afef40d3814d2581f4dacff7cebb87f /node_modules/forever/lib/forever.js
Diffstat (limited to 'node_modules/forever/lib/forever.js')
-rw-r--r--node_modules/forever/lib/forever.js907
1 files changed, 907 insertions, 0 deletions
diff --git a/node_modules/forever/lib/forever.js b/node_modules/forever/lib/forever.js
new file mode 100644
index 0000000..290be83
--- /dev/null
+++ b/node_modules/forever/lib/forever.js
@@ -0,0 +1,907 @@
+/*
+ * forever.js: Top level include for the forever module
+ *
+ * (C) 2010 Nodejitsu Inc.
+ * MIT LICENCE
+ *
+ */
+
+var fs = require('fs'),
+ path = require('path'),
+ events = require('events'),
+ exec = require('child_process').exec,
+ spawn = require('child_process').spawn,
+ cliff = require('cliff'),
+ nconf = require('nconf'),
+ nssocket = require('nssocket'),
+ timespan = require('timespan'),
+ utile = require('utile'),
+ winston = require('winston'),
+ mkdirp = utile.mkdirp,
+ async = utile.async;
+
+var forever = exports;
+
+//
+// Setup `forever.log` to be a custom `winston` logger.
+//
+forever.log = new (winston.Logger)({
+ transports: [
+ new (winston.transports.Console)()
+ ]
+});
+
+forever.log.cli();
+
+//
+// Setup `forever out` for logEvents with `winston` custom logger.
+//
+forever.out = new (winston.Logger)({
+ transports: [
+ new (winston.transports.Console)()
+ ]
+});
+
+//
+// ### Export Components / Settings
+// Export `version` and important Prototypes from `lib/forever/*`
+//
+forever.initialized = false;
+forever.kill = require('forever-monitor').kill;
+forever.checkProcess = require('forever-monitor').checkProcess;
+forever.root = path.join(process.env.HOME || process.env.USERPROFILE || '/root', '.forever');
+forever.config = new nconf.File({ file: path.join(forever.root, 'config.json') });
+forever.Forever = forever.Monitor = require('forever-monitor').Monitor;
+forever.Worker = require('./forever/worker').Worker;
+forever.cli = require('./forever/cli');
+
+//
+// Expose version through `pkginfo`
+//
+require('pkginfo')(module, 'version');
+
+//
+// Expose the global forever service
+//
+forever.__defineGetter__('service', function () {
+ return require('./forever/service');
+});
+
+//
+// ### function getSockets (sockPath, callback)
+// #### @sockPath {string} Path in which to look for UNIX domain sockets
+// #### @callback {function} Continuation to pass control to when complete
+// Attempts to read the files from `sockPath` if the directory does not exist,
+// then it is created using `mkdirp`.
+//
+function getSockets(sockPath, callback) {
+ var sockets;
+
+ try {
+ sockets = fs.readdirSync(sockPath);
+ }
+ catch (ex) {
+ if (ex.code !== 'ENOENT') {
+ return callback(ex);
+ }
+
+ return mkdirp(sockPath, '0755', function (err) {
+ return err ? callback(err) : callback(null, []);
+ });
+ }
+
+ callback(null, sockets);
+}
+
+//
+// ### function getAllProcess (callback)
+// #### @callback {function} Continuation to respond to when complete.
+// Returns all data for processes managed by forever.
+//
+function getAllProcesses(callback) {
+ var sockPath = forever.config.get('sockPath');
+
+ function getProcess(name, next) {
+ var fullPath = path.join(sockPath, name),
+ socket = new nssocket.NsSocket();
+
+ socket.connect(fullPath, function (err) {
+ if (err) {
+ next(err);
+ }
+
+ socket.dataOnce(['data'], function (data) {
+ data.socket = fullPath;
+ next(null, data);
+ socket.end();
+ });
+
+ socket.send(['data']);
+ });
+
+ socket.on('error', function (err) {
+ if (err.code === 'ECONNREFUSED') {
+ fs.unlink(fullPath, function () {
+ next();
+ });
+ }
+ else {
+ next();
+ }
+ });
+ }
+
+ getSockets(sockPath, function (err, sockets) {
+ if (err || (sockets && sockets.length === 0)) {
+ return callback(err);
+ }
+
+ async.map(sockets, getProcess, function (err, processes) {
+ callback(processes.filter(Boolean));
+ });
+ });
+}
+
+//
+// ### function getAllPids ()
+// Returns the set of all pids managed by forever.
+// e.x. [{ pid: 12345, foreverPid: 12346 }, ...]
+//
+function getAllPids(processes) {
+ return !processes ? null : processes.map(function (proc) {
+ return {
+ pid: proc.pid,
+ foreverPid: proc.foreverPid
+ };
+ });
+}
+
+function stopOrRestart(action, event, format, target) {
+ var emitter = new events.EventEmitter(),
+ results = [];
+
+ function sendAction(proc, next) {
+ var socket = new nssocket.NsSocket();
+
+ socket.connect(proc.socket, function (err) {
+ if (err) {
+ next(err);
+ }
+
+ socket.dataOnce([action, 'ok'], function (data) {
+ next();
+ socket.end();
+ });
+
+ socket.send([action]);
+ });
+
+ socket.on('error', function (err) {
+ next(err);
+ });
+ }
+
+ getAllProcesses(function (processes) {
+ var procs = processes;
+
+ if (target !== undefined && target !== null) {
+ procs = forever.findByIndex(target, processes)
+ || forever.findByScript(target, processes)
+ || forever.findByUid(target, processes);
+ }
+
+ if (procs && procs.length > 0) {
+ async.map(procs, sendAction, function (err, results) {
+ if (err) {
+ emitter.emit('error', err);
+ }
+
+ emitter.emit(event, forever.format(format, procs));
+ });
+ }
+ else {
+ process.nextTick(function () {
+ emitter.emit('error', new Error('Cannot find forever process: ' + target));
+ });
+ }
+ });
+
+ return emitter;
+}
+
+//
+// ### function load (options, [callback])
+// #### @options {Object} Options to load into the forever module
+// Initializes configuration for forever module
+//
+forever.load = function (options) {
+ //
+ // Setup the incoming options with default options.
+ //
+ options = options || {};
+ options.loglength = options.loglength || 100;
+ options.logstream = options.logstream || false;
+ options.root = options.root || forever.root;
+ options.pidPath = options.pidPath || path.join(options.root, 'pids');
+ options.sockPath = options.sockPath || path.join(options.root, 'sock');
+
+ //
+ // 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.File({ file: path.join(options.root, 'config.json') });
+
+ //
+ // Try to load the forever `config.json` from
+ // the specified location.
+ //
+ try {
+ forever.config.loadSync();
+ }
+ catch (ex) { }
+
+ //
+ // Setup the columns for `forever list`.
+ //
+ options.columns = options.columns || forever.config.get('columns');
+ if (!options.columns) {
+ options.columns = [
+ 'uid', 'command', 'script', 'forever', 'pid', 'logfile', 'uptime'
+ ];
+ }
+
+ forever.config.set('root', options.root);
+ forever.config.set('pidPath', options.pidPath);
+ forever.config.set('sockPath', options.sockPath);
+ forever.config.set('loglength', options.loglength);
+ forever.config.set('logstream', options.logstream);
+ forever.config.set('columns', options.columns);
+
+ //
+ // Setup timestamp to event logger
+ //
+ forever.out.transports.console.timestamp = forever.config.get('timestamp') === 'true';
+
+ //
+ // 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');
+ }
+ catch (ex) { }
+ }
+
+ tryCreate(forever.config.get('root'));
+ tryCreate(forever.config.get('pidPath'));
+ tryCreate(forever.config.get('sockPath'));
+
+ //
+ // Attempt to save the new `config.json` for forever
+ //
+ try {
+ forever.config.saveSync();
+ }
+ catch (ex) { }
+
+ forever.initialized = true;
+};
+
+//
+// ### @private function _debug ()
+// Sets up debugging for this forever process
+//
+forever._debug = function () {
+ var debug = forever.config.get('debug');
+
+ if (!debug) {
+ forever.config.set('debug', true);
+ forever.log.add(winston.transports.File, {
+ level: 'silly',
+ filename: path.join(forever.config.get('root'), 'forever.debug.log')
+ });
+ }
+};
+
+//
+// Ensure forever will always be loaded the first time it is required.
+//
+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) {
+ var logAppend;
+
+ if (arguments.length === 4) {
+ logAppend = callback;
+ callback = arguments[3];
+ }
+
+ fs.stat(script, function (err, stats) {
+ if (err) {
+ return callback(new Error('script ' + script + ' does not exist.'));
+ }
+
+ return logAppend ? callback(null) : fs.stat(logFile, function (err, stats) {
+ return !err
+ ? callback(new Error('log file ' + logFile + ' exists. Use the -a or --append option to append log.'))
+ : callback(null);
+ });
+ });
+};
+
+//
+// ### function start (script, options)
+// #### @script {string} Location of the script to run.
+// #### @options {Object} Configuration for forever instance.
+// Starts a script with forever
+//
+forever.start = function (script, options) {
+ if (!options.uid) {
+ options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_');
+ }
+
+ if (!options.logFile) {
+ options.logFile = forever.logFilePath(options.uid + '.log');
+ }
+
+ //
+ // Create the monitor, log events, and start.
+ //
+ var monitor = new forever.Monitor(script, options);
+ forever.logEvents(monitor);
+ return monitor.start();
+};
+
+//
+// ### function startDaemon (script, options)
+// #### @script {string} Location of the script to run.
+// #### @options {Object} Configuration for forever instance.
+// Starts a script with forever as a daemon
+//
+forever.startDaemon = function (script, options) {
+ options = options || {};
+ options.uid = options.uid || utile.randomString(4).replace(/^\-/, '_');
+ options.logFile = forever.logFilePath(options.logFile || options.uid + '.log');
+ options.pidFile = forever.pidFilePath(options.pidFile || options.uid + '.pid');
+
+ var monitor, outFD, errFD, workerPath;
+
+ //
+ // This log file is forever's log file - the user's outFile and errFile
+ // options are not taken into account here. This will be an aggregate of all
+ // the app's output, as well as messages from the monitor process, where
+ // applicable.
+ //
+ outFD = fs.openSync(options.logFile, 'a');
+ errFD = fs.openSync(options.logFile, 'a');
+ monitorPath = path.resolve(__dirname, '..', 'bin', 'monitor');
+
+ monitor = spawn(process.execPath, [monitorPath, script], {
+ stdio: ['ipc', outFD, errFD],
+ detached: true
+ });
+
+ monitor.on('exit', function (code) {
+ console.error('Monitor died unexpectedly with exit code %d', code);
+ });
+
+ monitor.send(JSON.stringify(options));
+ monitor.unref();
+ return monitor;
+};
+
+//
+// ### function startServer ()
+// #### @arguments {forever.Monitor...} A list of forever.Monitor instances
+// Starts the `forever` HTTP server for communication with the forever CLI.
+// **NOTE:** This will change your `process.title`.
+//
+forever.startServer = function () {
+ var args = Array.prototype.slice.call(arguments),
+ monitors = [],
+ callback;
+
+ args.forEach(function (a) {
+ if (Array.isArray(a)) {
+ monitors = monitors.concat(a.filter(function (m) {
+ return m instanceof forever.Monitor;
+ }));
+ }
+ else if (a instanceof forever.Monitor) {
+ monitors.push(a);
+ }
+ else if (typeof a === 'function') {
+ callback = a;
+ }
+ });
+
+ async.map(monitors, function (monitor, next) {
+ var worker = new forever.Worker({
+ monitor: monitor,
+ sockPath: forever.config.get('sockPath'),
+ exitOnStop: true
+ });
+
+ worker.start(function (err) {
+ return err ? next(err) : next(null, worker);
+ });
+ }, callback || function () {});
+};
+
+
+//
+// ### 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(es) with the specified index or script name
+// in the list of all processes
+//
+forever.stop = function (target, format) {
+ return stopOrRestart('stop', 'stop', format, target);
+};
+
+//
+// ### 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) {
+ return stopOrRestart('restart', 'restart', format, target);
+};
+
+//
+// ### function restartAll (format)
+// #### @format {boolean} Value indicating if we should format output
+// Restarts all processes managed by forever.
+//
+forever.restartAll = function (format) {
+ return stopOrRestart('restart', 'restartAll', format);
+};
+
+//
+// ### function stopAll (format)
+// #### @format {boolean} Value indicating if we should format output
+// Stops all processes managed by forever.
+//
+forever.stopAll = function (format) {
+ return stopOrRestart('stop', 'stopAll', format);
+};
+
+//
+// ### function list (format, procs, callback)
+// #### @format {boolean} If set, will return a formatted string of data
+// #### @callback {function} Continuation to respond to when complete.
+// Returns the list of all process data managed by forever.
+//
+forever.list = function (format, callback) {
+ getAllProcesses(function (processes) {
+ callback(null, forever.format(format, processes));
+ });
+};
+
+//
+// ### function tail (target, length, callback)
+// #### @target {string} Target script to list logs for
+// #### @options {length|stream} **Optional** Length of the logs to tail, boolean stream
+// #### @callback {function} Continuation to respond to when complete.
+// Responds with the latest `length` logs for the specified `target` process
+// managed by forever. If no `length` is supplied then `forever.config.get('loglength`)`
+// is used.
+//
+forever.tail = function (target, options, callback) {
+ if (!callback && typeof options === 'function') {
+ callback = options;
+ options.length = 0;
+ options.stream = false;
+ }
+
+ length = options.length || forever.config.get('loglength');
+ stream = options.stream || forever.config.get('logstream');
+
+ var that = this,
+ blanks = function (e, i, a) { return e !== '' },
+ title = function (e, i, a) { return e.match(/^==>/) },
+ args = ['-n', length],
+ logs;
+
+ if (stream) args.unshift('-f');
+
+ function tailProcess(procs, next) {
+ var map = {},
+ count = 0;
+
+ procs.forEach(function (proc) {
+ args.push(proc.logFile);
+ map[proc.logFile] = { pid: proc.pid, file: proc.file };
+ count++;
+ });
+
+ tail = spawn('tail', args, {
+ stdio: [null, 'pipe', 'pipe'],
+ });
+
+ tail.stdio[1].setEncoding('utf8');
+ tail.stdio[2].setEncoding('utf8');
+
+ tail.stdio[1].on('data', function (data) {
+ chunk = data.split('\n\n');
+ chunk.forEach(function (logs) {
+ var logs = logs.split('\n').filter(blanks),
+ file = logs.filter(title),
+ lines,
+ proc;
+
+ file.length
+ ? proc = map[file[0].split(' ')[1]]
+ : proc = map[procs[0].logFile];
+
+ count === 1
+ ? lines = logs
+ : lines = logs.slice(1);
+
+ lines.forEach(function (line) {
+ callback(null, { file: proc.file, pid: proc.pid, line: line });
+ });
+ });
+ });
+
+ tail.stdio[2].on('data', function (err) {
+ return callback(err);
+ });
+ }
+
+ getAllProcesses(function (processes) {
+ if (!processes) {
+ return callback(new Error('Cannot find forever process: ' + target));
+ }
+
+ var procs = forever.findByIndex(target, processes)
+ || forever.findByScript(target, processes);
+
+ if (!procs) {
+ return callback(new Error('No logs available for process: ' + target));
+ }
+
+ tailProcess(procs, callback);
+ });
+};
+
+//
+// ### function findByIndex (index, processes)
+// #### @index {string} Index of the process to find.
+// #### @processes {Array} Set of processes to find in.
+// Finds the process with the specified index.
+//
+forever.findByIndex = function (index, processes) {
+ var indexAsNum = parseInt(index, 10);
+ if (indexAsNum == index) {
+ var proc = processes && processes[indexAsNum];
+ }
+ return proc ? [proc] : null;
+};
+
+//
+// ### function findByScript (script, processes)
+// #### @script {string} The name of the script to find.
+// #### @processes {Array} Set of processes to find in.
+// Finds the process with the specified script name.
+//
+forever.findByScript = function (script, processes) {
+ if (!processes) return null;
+
+ var procs = processes.filter(function (p) {
+ return p.file === script;
+ });
+
+ if (procs.length === 0) procs = null;
+
+ return procs;
+};
+
+//
+// ### function findByUid (uid, processes)
+// #### @uid {string} The uid of the process to find.
+// #### @processes {Array} Set of processes to find in.
+// Finds the process with the specified uid.
+//
+forever.findByUid = function (script, processes) {
+ return !processes
+ ? null
+ : processes.filter(function (p) {
+ return p.uid === script;
+ });
+};
+
+//
+// ### function format (format, procs)
+// #### @format {Boolean} Value indicating if processes should be formatted
+// #### @procs {Array} Processes to format
+// Returns a formatted version of the `procs` supplied based on the column
+// configuration in `forever.config`.
+//
+forever.format = function (format, procs) {
+ if (!procs || procs.length === 0) {
+ return null;
+ }
+
+ var index = 0,
+ columns = forever.config.get('columns'),
+ rows = [[' '].concat(columns)],
+ formatted;
+
+ function mapColumns(prefix, mapFn) {
+ return [prefix].concat(columns.map(mapFn));
+ }
+
+ if (format) {
+ //
+ // Iterate over the procs to see which has the
+ // longest options string
+ //
+ procs.forEach(function (proc) {
+ rows.push(mapColumns('[' + index + ']', function (column) {
+ return forever.columns[column]
+ ? forever.columns[column].get(proc)
+ : 'MISSING';
+ }));
+
+ index++;
+ });
+
+ formatted = cliff.stringifyRows(rows, mapColumns('white', function (column) {
+ return forever.columns[column]
+ ? forever.columns[column].color
+ : 'white';
+ }));
+ }
+
+ return format ? formatted : procs;
+};
+
+//
+// ### function cleanUp ()
+// Utility function for removing excess pid and
+// config, and log files used by forever.
+//
+forever.cleanUp = function (cleanLogs, allowManager) {
+ var emitter = new events.EventEmitter(),
+ pidPath = forever.config.get('pidPath');
+
+ getAllProcesses(function (processes) {
+ if (cleanLogs) {
+ forever.cleanLogsSync(processes);
+ }
+
+ function unlinkProcess(proc, done) {
+ fs.unlink(path.join(pidPath, proc.uid + '.pid'), function () {
+ //
+ // Ignore errors (in case the file doesnt exist).
+ //
+
+ if (cleanLogs && proc.logFile) {
+ //
+ // If we are cleaning logs then do so if the process
+ // has a 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) {
+ proc.child = forever.checkProcess(proc.pid);
+ proc.manager = forever.checkProcess(proc.foreverPid);
+ cleanProcess(proc, next);
+ }
+
+ if (processes && processes.length > 0) {
+ (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 () {
+ emitter.emit('cleanUp');
+ });
+ }
+ });
+
+ return emitter;
+};
+
+//
+// ### function cleanLogsSync (processes)
+// #### @processes {Array} The set of all forever processes
+// Removes all log files from the root forever directory
+// that do not belong to current running forever processes.
+//
+forever.cleanLogsSync = function (processes) {
+ var root = forever.config.get('root'),
+ files = fs.readdirSync(root),
+ running,
+ runningLogs;
+
+ 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(root, file));
+ }
+ });
+};
+
+//
+// ### function logFilePath (logFile)
+// #### @logFile {string} Log file path
+// Determines the full logfile path name
+//
+forever.logFilePath = function (logFile, uid) {
+ return logFile && (logFile[0] === '/' || logFile[1] === ':')
+ ? logFile
+ : path.join(forever.config.get('root'), logFile || (uid || 'forever') + '.log');
+};
+
+//
+// ### function pidFilePath (pidFile)
+// #### @logFile {string} Pid file path
+// Determines the full pid file path name
+//
+forever.pidFilePath = function (pidFile) {
+ return pidFile && (pidFile[0] === '/' || pidFile[1] === ':')
+ ? pidFile
+ : path.join(forever.config.get('pidPath'), pidFile);
+};
+
+//
+// ### @function logEvents (monitor)
+// #### @monitor {forever.Monitor} Monitor to log events for
+// Logs important restart and error events to `console.error`
+//
+forever.logEvents = function (monitor) {
+ monitor.on('watch:error', function (info) {
+ forever.out.error(info.message);
+ forever.out.error(info.error);
+ });
+
+ monitor.on('watch:restart', function (info) {
+ forever.out.error('restarting script because ' + info.file + ' changed')
+ });
+
+ monitor.on('restart', function () {
+ forever.out.error('Forever restarting script for ' + monitor.times + ' time')
+ });
+
+ monitor.on('exit:code', function (code, signal) {
+ forever.out.error(code
+ ? 'Forever detected script exited with code: ' + code
+ : 'Forever detected script was killed by signal: ' + signal);
+ });
+};
+
+//
+// ### @columns {Object}
+// Property descriptors for accessing forever column information
+// through `forever list` and `forever.list()`
+//
+forever.columns = {
+ uid: {
+ color: 'white',
+ get: function (proc) {
+ return proc.uid;
+ }
+ },
+ command: {
+ color: 'grey',
+ get: function (proc) {
+ return (proc.command || 'node').grey;
+ }
+ },
+ script: {
+ color: 'grey',
+ get: function (proc) {
+ return [proc.file].concat(proc.options).join(' ').grey;
+ }
+ },
+ forever: {
+ color: 'white',
+ get: function (proc) {
+ return proc.foreverPid;
+ }
+ },
+ pid: {
+ color: 'white',
+ get: function (proc) {
+ return proc.pid;
+ }
+ },
+ logfile: {
+ color: 'magenta',
+ get: function (proc) {
+ return proc.logFile ? proc.logFile.magenta : '';
+ }
+ },
+ dir: {
+ color: 'grey',
+ get: function (proc) {
+ return proc.sourceDir.grey;
+ }
+ },
+ uptime: {
+ color: 'yellow',
+ get: function (proc) {
+ return timespan.fromDates(new Date(proc.ctime), new Date()).toString().yellow;
+ }
+ }
+};