mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
601 lines
16 KiB
JavaScript
601 lines
16 KiB
JavaScript
|
|
/***************************
|
|
*
|
|
* Extra methods
|
|
*
|
|
**************************/
|
|
|
|
var cst = require('../../constants.js');
|
|
var Common = require('../Common.js');
|
|
var UX = require('./CliUx');
|
|
var chalk = require('chalk');
|
|
var async = require('async');
|
|
var path = require('path');
|
|
var fs = require('fs');
|
|
var fmt = require('../tools/fmt.js');
|
|
var moment = require('moment');
|
|
var pkg = require('../../package.json');
|
|
|
|
module.exports = function(CLI) {
|
|
|
|
/**
|
|
* Get version of the daemonized PM2
|
|
* @method getVersion
|
|
* @callback cb
|
|
*/
|
|
CLI.prototype.getVersion = function(cb) {
|
|
var that = this;
|
|
|
|
that.Client.executeRemote('getVersion', {}, function(err) {
|
|
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Get version of the daemonized PM2
|
|
* @method getVersion
|
|
* @callback cb
|
|
*/
|
|
CLI.prototype.report = function() {
|
|
var that = this;
|
|
var semver = require('semver');
|
|
|
|
function reporting(cb) {
|
|
|
|
var Log = require('./Log');
|
|
|
|
that.Client.executeRemote('getReport', {}, function(err, report) {
|
|
fmt.sep();
|
|
fmt.title('PM2 REPORT (' + new Date() + ')');
|
|
fmt.sep();
|
|
fmt.title(chalk.bold.blue('Daemon'));
|
|
fmt.field('pm2d version', report.pm2_version);
|
|
fmt.field('node version', report.node_version);
|
|
fmt.field('node path', report.node_path);
|
|
fmt.field('argv', report.argv);
|
|
fmt.field('argv0', report.argv0);
|
|
fmt.field('user', report.user);
|
|
fmt.field('uid', report.uid);
|
|
fmt.field('gid', report.gid);
|
|
fmt.field('uptime', moment(new Date()).diff(report.started_at, 'minute') + 'min');
|
|
|
|
fmt.sep();
|
|
fmt.title(chalk.bold.blue('CLI'));
|
|
fmt.field('local pm2', pkg.version);
|
|
fmt.field('node version', process.versions.node);
|
|
fmt.field('node path', process.env['_']);
|
|
fmt.field('argv', process.argv);
|
|
fmt.field('argv0', process.argv0);
|
|
fmt.field('user', process.env.USER);
|
|
fmt.field('uid', process.geteuid());
|
|
fmt.field('gid', process.getegid());
|
|
|
|
var os = require('os');
|
|
|
|
fmt.sep();
|
|
fmt.title(chalk.bold.blue('System info'));
|
|
fmt.field('arch', os.arch());
|
|
fmt.field('platform', os.platform());
|
|
fmt.field('type', os.type());
|
|
fmt.field('cpus', os.cpus()[0].model);
|
|
fmt.field('cpus nb', Object.keys(os.cpus()).length);
|
|
fmt.field('freemem', os.freemem());
|
|
fmt.field('totalmem', os.totalmem());
|
|
fmt.field('home', os.homedir());
|
|
|
|
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
|
|
fmt.sep();
|
|
fmt.title(chalk.bold.blue('PM2 list'));
|
|
that.list();
|
|
|
|
UX.dispAsTable(list, that.gl_interact_infos);
|
|
|
|
fmt.sep();
|
|
fmt.title(chalk.bold.blue('Daemon logs'));
|
|
Log.tail([{
|
|
path : cst.PM2_LOG_FILE_PATH,
|
|
app_name : 'PM2',
|
|
type : 'PM2'
|
|
}], 20, false, function() {
|
|
console.log(chalk.bold.green('Please copy/paste the above report in your issue on https://github.com/Unitech/pm2/issues'));
|
|
that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
that.Client.executeRemote('getVersion', {}, function(err, data) {
|
|
if (semver.satisfies(data, '>= 2.6.0'))
|
|
reporting();
|
|
else {
|
|
Common.printError(cst.PREFIX_MSG_ERR + 'You need to update your Daemon, please type `$ pm2 update`');
|
|
that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Create PM2 memory snapshot
|
|
* @method getVersion
|
|
* @callback cb
|
|
*/
|
|
CLI.prototype.snapshotPM2 = function(cb) {
|
|
var that = this;
|
|
var moment = require('moment');
|
|
var file = path.join(process.cwd(), moment().format('dd-HH:mm:ss') + '.heapsnapshot');
|
|
|
|
that.Client.executeRemote('snapshotPM2', {
|
|
pwd : file
|
|
}, function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return that.exitCli(1);
|
|
}
|
|
console.log('Heapdump in %s', file);
|
|
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Create PM2 memory snapshot
|
|
* @method getVersion
|
|
* @callback cb
|
|
*/
|
|
CLI.prototype.profilePM2 = function(command, cb) {
|
|
var that = this;
|
|
var moment = require('moment');
|
|
var file = path.join(process.cwd(), moment().format('dd-HH:mm:ss') + '.cpuprofile');
|
|
|
|
if (command == 'start') {
|
|
that.Client.executeRemote('profileStart', {
|
|
}, function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return that.exitCli(1);
|
|
}
|
|
console.log('CPU profiling started, type pm2 profile stop once finished');
|
|
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
}
|
|
else if (command == 'stop') {
|
|
that.Client.executeRemote('profileStop', {
|
|
pwd : file
|
|
}, function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return that.exitCli(1);
|
|
}
|
|
console.log('CPU profile in %s', file);
|
|
return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method sendLineToStdin
|
|
*/
|
|
CLI.prototype.sendLineToStdin = function(pm_id, line, separator, cb) {
|
|
var that = this;
|
|
|
|
if (!cb && typeof(separator) == 'function') {
|
|
cb = separator;
|
|
separator = null;
|
|
}
|
|
|
|
var packet = {
|
|
pm_id : pm_id,
|
|
line : line + (separator || '\n')
|
|
};
|
|
|
|
that.Client.executeRemote('sendLineToStdin', packet, function(err, res) {
|
|
if (err) {
|
|
Common.printError(cst.PREFIX_MSG_ERR + err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
return cb ? cb(null, res) : that.speedList();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method attachToProcess
|
|
*/
|
|
CLI.prototype.attach = function(pm_id, separator, cb) {
|
|
var that = this;
|
|
var readline = require('readline');
|
|
|
|
if (isNaN(pm_id)) {
|
|
Common.printError('pm_id must be a process number (not a process name)');
|
|
return cb ? cb(Common.retErr('pm_id must be number')) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
|
|
if (typeof(separator) == 'function') {
|
|
cb = separator;
|
|
separator = null;
|
|
}
|
|
|
|
var rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
rl.on('close', function() {
|
|
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
|
|
that.Client.launchBus(function(err, bus, socket) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
|
|
bus.on('log:*', function(type, packet) {
|
|
if (packet.process.pm_id !== parseInt(pm_id))
|
|
return;
|
|
process.stdout.write(packet.data);
|
|
});
|
|
});
|
|
|
|
rl.on('line', function(line) {
|
|
that.sendLineToStdin(pm_id, line, separator, function() {});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method sendDataToProcessId
|
|
*/
|
|
CLI.prototype.sendDataToProcessId = function(proc_id, packet, cb) {
|
|
var that = this;
|
|
|
|
packet.id = proc_id;
|
|
|
|
that.Client.executeRemote('sendDataToProcessId', packet, function(err, res) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut('successfully sent data to process');
|
|
return cb ? cb(null, res) : that.speedList();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Used for custom actions, allows to trigger function inside an app
|
|
* To expose a function you need to use keymetrics/pmx
|
|
*
|
|
* @method msgProcess
|
|
* @param {Object} opts
|
|
* @param {String} id process id
|
|
* @param {String} action_name function name to trigger
|
|
* @param {Object} [opts.opts] object passed as first arg of the function
|
|
* @param {String} [uuid] optional unique identifier when logs are emitted
|
|
*
|
|
*/
|
|
CLI.prototype.msgProcess = function(opts, cb) {
|
|
var that = this;
|
|
|
|
that.Client.executeRemote('msgProcess', opts, cb);
|
|
};
|
|
|
|
/**
|
|
* Trigger a PMX custom action in target application
|
|
* Custom actions allows to interact with an application
|
|
*
|
|
* @method trigger
|
|
* @param {String|Number} pm_id process id or application name
|
|
* @param {String} action_name name of the custom action to trigger
|
|
* @param {Mixed} params parameter to pass to target action
|
|
* @param {Function} cb callback
|
|
*/
|
|
CLI.prototype.trigger = function(pm_id, action_name, params, cb) {
|
|
if (typeof(params) === 'function') {
|
|
cb = params;
|
|
params = null;
|
|
}
|
|
var cmd = {
|
|
msg : action_name
|
|
};
|
|
var counter = 0;
|
|
var process_wait_count = 0;
|
|
var that = this;
|
|
var results = [];
|
|
|
|
if (params)
|
|
cmd.opts = params;
|
|
if (isNaN(pm_id))
|
|
cmd.name = pm_id;
|
|
else
|
|
cmd.id = pm_id;
|
|
|
|
this.launchBus(function(err, bus) {
|
|
bus.on('axm:reply', function(ret) {
|
|
if (ret.process.name == pm_id || ret.process.pm_id == pm_id) {
|
|
results.push(ret);
|
|
Common.printOut('[%s:%s]=%j', ret.process.name, ret.process.pm_id, ret.data.return);
|
|
if (++counter == process_wait_count)
|
|
return cb ? cb(null, results) : that.exitCli(cst.SUCCESS_EXIT);
|
|
}
|
|
});
|
|
|
|
that.msgProcess(cmd, function(err, data) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
process_wait_count = data.process_count;
|
|
Common.printOut(chalk.bold('%s processes have received command %s'),
|
|
data.process_count, action_name);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method sendSignalToProcessName
|
|
* @param {} signal
|
|
* @param {} process_name
|
|
* @return
|
|
*/
|
|
CLI.prototype.sendSignalToProcessName = function(signal, process_name, cb) {
|
|
var that = this;
|
|
|
|
that.Client.executeRemote('sendSignalToProcessName', {
|
|
signal : signal,
|
|
process_name : process_name
|
|
}, function(err, list) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut('successfully sent signal %s to process name %s', signal, process_name);
|
|
return cb ? cb(null, list) : that.speedList();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method sendSignalToProcessId
|
|
* @param {} signal
|
|
* @param {} process_id
|
|
* @return
|
|
*/
|
|
CLI.prototype.sendSignalToProcessId = function(signal, process_id, cb) {
|
|
var that = this;
|
|
|
|
that.Client.executeRemote('sendSignalToProcessId', {
|
|
signal : signal,
|
|
process_id : process_id
|
|
}, function(err, list) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut('successfully sent signal %s to process id %s', signal, process_id);
|
|
return cb ? cb(null, list) : that.speedList();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Launch API interface
|
|
* @method web
|
|
* @return
|
|
*/
|
|
CLI.prototype.web = function(port, cb) {
|
|
var that = this;
|
|
|
|
if (typeof(port) === 'function') {
|
|
cb = port;
|
|
port = 9615;
|
|
}
|
|
|
|
var filepath = path.resolve(path.dirname(module.filename), '../HttpInterface.js');
|
|
|
|
that.start({
|
|
script : filepath,
|
|
name : 'pm2-http-interface',
|
|
execMode : 'fork_mode',
|
|
env : {
|
|
PM2_WEB_PORT : port
|
|
}
|
|
}, function(err, proc) {
|
|
if (err) {
|
|
Common.printError(cst.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);
|
|
return cb ? cb(Common.retErr(err)) : that.speedList();
|
|
}
|
|
Common.printOut(cst.PREFIX_MSG + 'Process launched');
|
|
return cb ? cb(null, proc) : that.speedList();
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* API method to launch a process that will serve directory over http
|
|
*
|
|
* @param {Object} opts options
|
|
* @param {String} opts.path path to be served
|
|
* @param {Number} opts.port port on which http will bind
|
|
* @param {Function} cb optional callback
|
|
*/
|
|
CLI.prototype.serve = function (target_path, port, opts, cb) {
|
|
var that = this;
|
|
var servePort = process.env.PM2_SERVE_PORT || port || 8080;
|
|
var servePath = path.resolve(process.env.PM2_SERVE_PATH || target_path || '.');
|
|
|
|
var filepath = path.resolve(path.dirname(module.filename), './Serve.js');
|
|
|
|
if (!opts.name || typeof(opts.name) == 'function')
|
|
opts.name = 'static-page-server-' + servePort;
|
|
if (!opts.env)
|
|
opts.env = {};
|
|
opts.env.PM2_SERVE_PORT = servePort;
|
|
opts.env.PM2_SERVE_PATH = servePath;
|
|
opts.cwd = servePath;
|
|
|
|
this.start(filepath, opts, function (err, res) {
|
|
if (err) {
|
|
Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);
|
|
return cb ? cb(err) : that.speedList(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut(cst.PREFIX_MSG + 'Serving ' + servePath + ' on port ' + servePort);
|
|
return cb ? cb(null, res) : that.speedList();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ping daemon - if PM2 daemon not launched, it will launch it
|
|
* @method ping
|
|
*/
|
|
CLI.prototype.ping = function(cb) {
|
|
var that = this;
|
|
|
|
that.Client.executeRemote('ping', {}, function(err, res) {
|
|
if (err) {
|
|
Common.printError(err);
|
|
return cb ? cb(new Error(err)) : that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut(res);
|
|
return cb ? cb(null, res) : that.exitCli(cst.SUCCESS_EXIT);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Execute remote command
|
|
*/
|
|
CLI.prototype.remote = function(command, opts, cb) {
|
|
var that = this;
|
|
|
|
that[command](opts.name, function(err_cmd, ret) {
|
|
if (err_cmd)
|
|
console.error(err_cmd);
|
|
console.log('Command %s finished', command);
|
|
return cb(err_cmd, ret);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* This remote method allows to pass multiple arguments
|
|
* to PM2
|
|
* It is used for the new scoped PM2 action system
|
|
*/
|
|
CLI.prototype.remoteV2 = function(command, opts, cb) {
|
|
var that = this;
|
|
|
|
if (that[command].length == 1)
|
|
return that[command](cb);
|
|
|
|
opts.args.push(cb);
|
|
return that[command].apply(this, opts.args);
|
|
};
|
|
|
|
|
|
/**
|
|
* Description
|
|
* @method generateSample
|
|
* @param {} name
|
|
* @return
|
|
*/
|
|
CLI.prototype.generateSample = function(mode) {
|
|
var that = this;
|
|
var templatePath;
|
|
|
|
if (mode == 'simple')
|
|
templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL_SIMPLE);
|
|
else
|
|
templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL);
|
|
|
|
var sample = fs.readFileSync(templatePath);
|
|
var dt = sample.toString();
|
|
var f_name = 'ecosystem.config.js';
|
|
var pwd = process.env.PWD || process.cwd();
|
|
|
|
try {
|
|
fs.writeFileSync(path.join(pwd, f_name), dt);
|
|
} catch (e) {
|
|
console.error(e.stack || e);
|
|
return that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
Common.printOut('File %s generated', path.join(pwd, f_name));
|
|
that.exitCli(cst.SUCCESS_EXIT);
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method dashboard
|
|
* @return
|
|
*/
|
|
CLI.prototype.dashboard = function(cb) {
|
|
var that = this;
|
|
|
|
var Dashboard = require('./Dashboard');
|
|
|
|
if (cb) return cb(new Error('Dashboard cant be called programmatically'));
|
|
|
|
Dashboard.init();
|
|
|
|
this.Client.launchBus(function (err, bus) {
|
|
if (err) {
|
|
console.error('Error launchBus: ' + err);
|
|
that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
bus.on('log:*', function(type, data) {
|
|
Dashboard.log(type, data);
|
|
})
|
|
});
|
|
|
|
process.on('SIGINT', function() {
|
|
this.Client.disconnectBus(function() {
|
|
process.exit(cst.SUCCESS_EXIT);
|
|
});
|
|
});
|
|
|
|
function launchDashboard() {
|
|
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
if (err) {
|
|
console.error('Error retrieving process list: ' + err);
|
|
that.exitCli(cst.ERROR_EXIT);
|
|
}
|
|
|
|
Dashboard.refresh(list);
|
|
|
|
setTimeout(function() {
|
|
launchDashboard();
|
|
}, 800);
|
|
});
|
|
}
|
|
|
|
launchDashboard();
|
|
};
|
|
|
|
CLI.prototype.monit = function(cb) {
|
|
var that = this;
|
|
|
|
var Monit = require('./Monit.js');
|
|
|
|
if (cb) return cb(new Error('Monit cant be called programmatically'));
|
|
|
|
Monit.init();
|
|
|
|
function launchMonitor() {
|
|
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
if (err) {
|
|
console.error('Error retrieving process list: ' + err);
|
|
that.exitCli(conf.ERROR_EXIT);
|
|
}
|
|
|
|
Monit.refresh(list);
|
|
|
|
setTimeout(function() {
|
|
launchMonitor();
|
|
}, 400);
|
|
});
|
|
}
|
|
|
|
launchMonitor();
|
|
};
|
|
};
|