123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- /**
- * Copyright 2013-2022 the PM2 project authors. All rights reserved.
- * Use of this source code is governed by a license that
- * can be found in the LICENSE file.
- */
- var chalk = require('chalk');
- var path = require('path');
- var fs = require('fs');
- var forEachLimit = require('async/forEachLimit');
- var eachLimit = require('async/eachLimit');
- var Common = require('../Common.js');
- var cst = require('../../constants.js');
- var util = require('util');
- var tmpPath = require('os').tmpdir;
- var which = require('../tools/which.js');
- var sexec = require('../tools/sexec')
- module.exports = function(CLI) {
- /**
- * If command is launched without root right
- * Display helper
- */
- function isNotRoot(startup_mode, platform, opts, cb) {
- Common.printOut(`${cst.PREFIX_MSG}To ${startup_mode} the Startup Script, copy/paste the following command:`);
- if (opts.user) {
- console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 ' + opts.args[1].name() + ' ' + platform + ' -u ' + opts.user + ' --hp ' + process.env.HOME);
- return cb(new Error('You have to run this with elevated rights'));
- }
- return sexec('whoami', {silent: true}, function(err, stdout, stderr) {
- console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' ' + require.main.filename + ' ' + opts.args[1].name() + ' ' + platform + ' -u ' + stdout.trim() + ' --hp ' + process.env.HOME);
- return cb(new Error('You have to run this with elevated rights'));
- });
- }
- /**
- * Detect running init system
- */
- function detectInitSystem() {
- var hash_map = {
- 'systemctl' : 'systemd',
- 'update-rc.d': 'upstart',
- 'chkconfig' : 'systemv',
- 'rc-update' : 'openrc',
- 'launchctl' : 'launchd',
- 'sysrc' : 'rcd',
- 'rcctl' : 'rcd-openbsd',
- 'svcadm' : 'smf'
- };
- var init_systems = Object.keys(hash_map);
- for (var i = 0; i < init_systems.length; i++) {
- if (which(init_systems[i]) != null) {
- break;
- }
- }
- if (i >= init_systems.length) {
- Common.printError(cst.PREFIX_MSG_ERR + 'Init system not found');
- return null;
- }
- Common.printOut(cst.PREFIX_MSG + 'Init System found: ' + chalk.bold(hash_map[init_systems[i]]));
- return hash_map[init_systems[i]];
- }
- CLI.prototype.uninstallStartup = function(platform, opts, cb) {
- var commands;
- var that = this;
- var actual_platform = detectInitSystem();
- var user = opts.user || process.env.USER || process.env.LOGNAME; // Use LOGNAME on Solaris-like systems
- var service_name = (opts.serviceName || 'pm2-' + user);
- var openrc_service_name = 'pm2';
- var launchd_service_name = (opts.serviceName || 'pm2.' + user);
- if (!platform)
- platform = actual_platform;
- else if (actual_platform && actual_platform !== platform) {
- Common.printOut('-----------------------------------------------------------')
- Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
- Common.printOut(' Please verify that your choice is indeed your init system')
- Common.printOut(' If you arent sure, just run : pm2 startup')
- Common.printOut('-----------------------------------------------------------')
- }
- if (platform === null)
- throw new Error('Init system not found')
- if (!cb) {
- cb = function(err, data) {
- if (err)
- return that.exitCli(cst.ERROR_EXIT);
- return that.exitCli(cst.SUCCESS_EXIT);
- }
- }
- if (process.getuid() != 0) {
- return isNotRoot('unsetup', platform, opts, cb);
- }
- if (fs.existsSync('/etc/init.d/pm2-init.sh')) {
- platform = 'oldsystem';
- }
- switch(platform) {
- case 'systemd':
- commands = [
- 'systemctl stop ' + service_name,
- 'systemctl disable ' + service_name,
- 'rm /etc/systemd/system/' + service_name + '.service'
- ];
- break;
- case 'systemv':
- commands = [
- 'chkconfig ' + service_name + ' off',
- 'rm /etc/init.d/' + service_name
- ];
- break;
- case 'oldsystem':
- Common.printOut(cst.PREFIX_MSG + 'Disabling and deleting old startup system');
- commands = [
- 'update-rc.d pm2-init.sh disable',
- 'update-rc.d -f pm2-init.sh remove',
- 'rm /etc/init.d/pm2-init.sh'
- ];
- break;
- case 'openrc':
- service_name = openrc_service_name;
- commands = [
- '/etc/init.d/' + service_name + ' stop',
- 'rc-update delete ' + service_name + ' default',
- 'rm /etc/init.d/' + service_name
- ];
- break;
- case 'upstart':
- commands = [
- 'update-rc.d ' + service_name + ' disable',
- 'update-rc.d -f ' + service_name + ' remove',
- 'rm /etc/init.d/' + service_name
- ];
- break;
- case 'launchd':
- var destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
- commands = [
- 'launchctl remove ' + launchd_service_name + ' || true',
- 'rm ' + destination
- ];
- break;
- case 'rcd':
- service_name = (opts.serviceName || 'pm2_' + user);
- commands = [
- '/usr/local/etc/rc.d/' + service_name + ' stop',
- 'sysrc -x ' + service_name + '_enable',
- 'rm /usr/local/etc/rc.d/' + service_name
- ];
- break;
- case 'rcd-openbsd':
- service_name = (opts.serviceName || 'pm2_' + user);
- var destination = path.join('/etc/rc.d', service_name);
- commands = [
- 'rcctl stop ' + service_name,
- 'rcctl disable ' + service_name,
- 'rm ' + destination
- ];
- break;
- case 'smf':
- service_name = (opts.serviceName || 'pm2_' + user);
- commands = [
- 'svcadm disable ' + service_name,
- 'svccfg delete -f ' + service_name
- ]
- };
- sexec(commands.join('&& '), function(code, stdout, stderr) {
- Common.printOut(stdout);
- Common.printOut(stderr);
- if (code == 0) {
- Common.printOut(cst.PREFIX_MSG + chalk.bold('Init file disabled.'));
- } else {
- Common.printOut(cst.ERROR_MSG + chalk.bold('Return code : ' + code));
- }
- cb(null, {
- commands : commands,
- platform : platform
- });
- });
- };
- /**
- * Startup script generation
- * @method startup
- * @param {string} platform type (centos|redhat|amazon|gentoo|systemd|smf)
- */
- CLI.prototype.startup = function(platform, opts, cb) {
- var that = this;
- var actual_platform = detectInitSystem();
- var user = (opts.user || process.env.USER || process.env.LOGNAME); // Use LOGNAME on Solaris-like systems
- var service_name = (opts.serviceName || 'pm2-' + user);
- var openrc_service_name = 'pm2';
- var launchd_service_name = (opts.serviceName || 'pm2.' + user);
- if (!platform)
- platform = actual_platform;
- else if (actual_platform && actual_platform !== platform) {
- Common.printOut('-----------------------------------------------------------')
- Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
- Common.printOut(' Please verify that your choice is indeed your init system')
- Common.printOut(' If you arent sure, just run : pm2 startup')
- Common.printOut('-----------------------------------------------------------')
- }
- if (platform == null)
- throw new Error('Init system not found');
- if (!cb) {
- cb = function(err, data) {
- if (err)
- return that.exitCli(cst.ERROR_EXIT);
- return that.exitCli(cst.SUCCESS_EXIT);
- }
- }
- if (process.getuid() != 0) {
- return isNotRoot('setup', platform, opts, cb);
- }
- var destination;
- var commands;
- var template;
- function getTemplate(type) {
- return fs.readFileSync(path.join(__dirname, '..', 'templates/init-scripts', type + '.tpl'), {encoding: 'utf8'});
- }
- switch(platform) {
- case 'ubuntu':
- case 'centos':
- case 'arch':
- case 'oracle':
- case 'systemd':
- if (opts.waitIp)
- template = getTemplate('systemd-online');
- else
- template = getTemplate('systemd');
- destination = '/etc/systemd/system/' + service_name + '.service';
- commands = [
- 'systemctl enable ' + service_name
- ];
- break;
- case 'ubuntu14':
- case 'ubuntu12':
- case 'upstart':
- template = getTemplate('upstart');
- destination = '/etc/init.d/' + service_name;
- commands = [
- 'chmod +x ' + destination,
- 'mkdir -p /var/lock/subsys',
- 'touch /var/lock/subsys/' + service_name,
- 'update-rc.d ' + service_name + ' defaults'
- ];
- break;
- case 'systemv':
- case 'amazon':
- case 'centos6':
- template = getTemplate('upstart');
- destination = '/etc/init.d/' + service_name;
- commands = [
- 'chmod +x ' + destination,
- 'mkdir -p /var/lock/subsys',
- 'touch /var/lock/subsys/' + service_name,
- 'chkconfig --add ' + service_name,
- 'chkconfig ' + service_name + ' on',
- 'initctl list'
- ];
- break;
- case 'macos':
- case 'darwin':
- case 'launchd':
- template = getTemplate('launchd');
- destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
- commands = [
- 'mkdir -p ' + path.join(process.env.HOME, 'Library/LaunchAgents'),
- 'launchctl load -w ' + destination
- ]
- break;
- case 'freebsd':
- case 'rcd':
- template = getTemplate('rcd');
- service_name = (opts.serviceName || 'pm2_' + user);
- destination = '/usr/local/etc/rc.d/' + service_name;
- commands = [
- 'chmod 755 ' + destination,
- 'sysrc ' + service_name + '_enable=YES'
- ];
- break;
- case 'openbsd':
- case 'rcd-openbsd':
- template = getTemplate('rcd-openbsd');
- service_name = (opts.serviceName || 'pm2_' + user);
- destination = path.join('/etc/rc.d/', service_name);
- commands = [
- 'chmod 755 ' + destination,
- 'rcctl enable ' + service_name,
- 'rcctl start ' + service_name
- ];
- break;
- case 'openrc':
- template = getTemplate('openrc');
- service_name = openrc_service_name;
- destination = '/etc/init.d/' + service_name;
- commands = [
- 'chmod +x ' + destination,
- 'rc-update add ' + service_name + ' default'
- ];
- break;
- case 'smf':
- case 'sunos':
- case 'solaris':
- template = getTemplate('smf');
- service_name = (opts.serviceName || 'pm2_' + user);
- destination = path.join(tmpPath(), service_name + '.xml');
- commands = [
- 'svccfg import ' + destination,
- 'svcadm enable ' + service_name
- ];
- break;
- default:
- throw new Error('Unknown platform / init system name');
- }
- /**
- * 4# Replace template variable value
- */
- var envPath
- if (cst.HAS_NODE_EMBEDDED == true)
- envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
- else if (new RegExp(path.dirname(process.execPath)).test(process.env.PATH))
- envPath = process.env.PATH
- else
- envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
- template = template.replace(/%PM2_PATH%/g, process.mainModule.filename)
- .replace(/%NODE_PATH%/g, envPath)
- .replace(/%USER%/g, user)
- .replace(/%HOME_PATH%/g, opts.hp ? path.resolve(opts.hp, '.pm2') : cst.PM2_ROOT_PATH)
- .replace(/%SERVICE_NAME%/g, service_name);
- Common.printOut(chalk.bold('Platform'), platform);
- Common.printOut(chalk.bold('Template'));
- Common.printOut(template);
- Common.printOut(chalk.bold('Target path'));
- Common.printOut(destination);
- Common.printOut(chalk.bold('Command list'));
- Common.printOut(commands);
- Common.printOut(cst.PREFIX_MSG + 'Writing init configuration in ' + destination);
- try {
- fs.writeFileSync(destination, template);
- } catch (e) {
- console.error(cst.PREFIX_MSG_ERR + 'Failure when trying to write startup script');
- console.error(e.message || e);
- return cb(e);
- }
- Common.printOut(cst.PREFIX_MSG + 'Making script booting at startup...');
- forEachLimit(commands, 1, function(command, next) {
- Common.printOut(cst.PREFIX_MSG + '[-] Executing: %s...', chalk.bold(command));
- sexec(command, function(code, stdout, stderr) {
- if (code === 0) {
- Common.printOut(cst.PREFIX_MSG + chalk.bold('[v] Command successfully executed.'));
- return next();
- } else {
- Common.printOut(chalk.red('[ERROR] Exit code : ' + code))
- return next(new Error(command + ' failed, see error above.'));
- }
- })
- }, function(err) {
- if (err) {
- console.error(cst.PREFIX_MSG_ERR + (err.message || err));
- return cb(err);
- }
- Common.printOut(chalk.bold.blue('+---------------------------------------+'));
- Common.printOut(chalk.bold.blue((cst.PREFIX_MSG + 'Freeze a process list on reboot via:' )));
- Common.printOut(chalk.bold('$ pm2 save'));
- Common.printOut('');
- Common.printOut(chalk.bold.blue(cst.PREFIX_MSG + 'Remove init script via:'));
- Common.printOut(chalk.bold('$ pm2 unstartup ' + platform));
- return cb(null, {
- destination : destination,
- template : template
- });
- });
- };
- /**
- * DISABLED FEATURE
- * KEEPING METHOD FOR BACKWARD COMPAT
- */
- CLI.prototype.autodump = function(cb) {
- return cb()
- }
- /**
- * Dump current processes managed by pm2 into DUMP_FILE_PATH file
- * @method dump
- * @param {} cb
- * @return
- */
- CLI.prototype.dump = function(force, cb) {
- var env_arr = [];
- var that = this;
- if (typeof(force) === 'function') {
- cb = force
- force = false
- }
- if (!cb)
- Common.printOut(cst.PREFIX_MSG + 'Saving current process list...');
- that.Client.executeRemote('getMonitorData', {}, function(err, list) {
- if (err) {
- Common.printError('Error retrieving process list: ' + err);
- return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
- }
- /**
- * Description
- * @method fin
- * @param {} err
- * @return
- */
- function fin(err) {
- // try to fix issues with empty dump file
- // like #3485
- if (!force && env_arr.length === 0 && !process.env.FORCE) {
- // fix : if no dump file, no process, only module and after pm2 update
- if (!fs.existsSync(cst.DUMP_FILE_PATH)) {
- that.clearDump(function(){});
- }
- // if no process in list don't modify dump file
- // process list should not be empty
- if (cb) {
- return cb(new Error('Process list empty, cannot save empty list'));
- } else {
- Common.printOut(cst.PREFIX_MSG_WARNING + 'PM2 is not managing any process, skipping save...');
- Common.printOut(cst.PREFIX_MSG_WARNING + 'To force saving use: pm2 save --force');
- that.exitCli(cst.SUCCESS_EXIT);
- return;
- }
- }
- // Back up dump file
- try {
- if (fs.existsSync(cst.DUMP_FILE_PATH)) {
- fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));
- }
- } catch (e) {
- console.error(e.stack || e);
- Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to back up dump file in %s', cst.DUMP_BACKUP_FILE_PATH);
- }
- // Overwrite dump file, delete if broken and exit
- try {
- fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(env_arr, '', 2));
- } catch (e) {
- console.error(e.stack || e);
- try {
- // try to backup file
- if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {
- fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));
- }
- } catch (e) {
- // don't keep broken file
- fs.unlinkSync(cst.DUMP_FILE_PATH);
- console.error(e.stack || e);
- }
- Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH);
- return that.exitCli(cst.ERROR_EXIT);
- }
- if (cb) return cb(null, {success:true});
- Common.printOut(cst.PREFIX_MSG + 'Successfully saved in %s', cst.DUMP_FILE_PATH);
- return that.exitCli(cst.SUCCESS_EXIT);
- }
- (function ex(apps) {
- if (!apps[0]) return fin(null);
- delete apps[0].pm2_env.instances;
- delete apps[0].pm2_env.pm_id;
- delete apps[0].pm2_env.prev_restart_delay;
- if (!apps[0].pm2_env.pmx_module)
- env_arr.push(apps[0].pm2_env);
- apps.shift();
- return ex(apps);
- })(list);
- });
- };
- /**
- * Remove DUMP_FILE_PATH file and DUMP_BACKUP_FILE_PATH file
- * @method dump
- * @param {} cb
- * @return
- */
- CLI.prototype.clearDump = function(cb) {
- fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([]));
- if(cb && typeof cb === 'function') return cb();
- Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH);
- return this.exitCli(cst.SUCCESS_EXIT);
- };
- /**
- * Resurrect processes
- * @method resurrect
- * @param {} cb
- * @return
- */
- CLI.prototype.resurrect = function(cb) {
- var apps = {};
- var that = this;
- var processes;
- function readDumpFile(dumpFilePath) {
- Common.printOut(cst.PREFIX_MSG + 'Restoring processes located in %s', dumpFilePath);
- try {
- var apps = fs.readFileSync(dumpFilePath);
- } catch (e) {
- Common.printError(cst.PREFIX_MSG_ERR + 'Failed to read dump file in %s', dumpFilePath);
- throw e;
- }
- return apps;
- }
- function parseDumpFile(dumpFilePath, apps) {
- try {
- var processes = Common.parseConfig(apps, 'none');
- } catch (e) {
- Common.printError(cst.PREFIX_MSG_ERR + 'Failed to parse dump file in %s', dumpFilePath);
- try {
- fs.unlinkSync(dumpFilePath);
- } catch (e) {
- console.error(e.stack || e);
- }
- throw e;
- }
- return processes;
- }
- // Read dump file, fall back to backup, delete if broken
- try {
- apps = readDumpFile(cst.DUMP_FILE_PATH);
- processes = parseDumpFile(cst.DUMP_FILE_PATH, apps);
- } catch(e) {
- try {
- apps = readDumpFile(cst.DUMP_BACKUP_FILE_PATH);
- processes = parseDumpFile(cst.DUMP_BACKUP_FILE_PATH, apps);
- } catch(e) {
- Common.printError(cst.PREFIX_MSG_ERR + 'No processes saved; DUMP file doesn\'t exist');
- // if (cb) return cb(Common.retErr(e));
- // else return that.exitCli(cst.ERROR_EXIT);
- return that.speedList();
- }
- }
- that.Client.executeRemote('getMonitorData', {}, function(err, list) {
- if (err) {
- Common.printError(err);
- return that.exitCli(1);
- }
- var current = [];
- var target = [];
- list.forEach(function(app) {
- if (!current[app.name])
- current[app.name] = 0;
- current[app.name]++;
- });
- processes.forEach(function(app) {
- if (!target[app.name])
- target[app.name] = 0;
- target[app.name]++;
- });
- var tostart = Object.keys(target).filter(function(i) {
- return Object.keys(current).indexOf(i) < 0;
- })
- eachLimit(processes, cst.CONCURRENT_ACTIONS, function(app, next) {
- if (tostart.indexOf(app.name) == -1)
- return next();
- that.Client.executeRemote('prepare', app, function(err, dt) {
- if (err)
- Common.printError(err);
- else
- Common.printOut(cst.PREFIX_MSG + 'Process %s restored', app.pm_exec_path);
- next();
- });
- }, function(err) {
- return cb ? cb(null, apps) : that.speedList();
- });
- });
- };
- }
|