mirror of
https://github.com/xuxiaobo-bobo/boda_jsEnv.git
synced 2025-04-21 11:10:15 +08:00
617 lines
20 KiB
JavaScript
617 lines
20 KiB
JavaScript
/**
|
|
* 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();
|
|
});
|
|
});
|
|
};
|
|
|
|
}
|