%PDF- %PDF-
Direktori : /lib/node_modules/pm2/lib/API/ |
Current File : //lib/node_modules/pm2/lib/API/Startup.js |
/** * 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(); }); }); }; }