1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var chalk = require('chalk');
|
7 | var path = require('path');
|
8 | var fs = require('fs');
|
9 | var forEachLimit = require('async/forEachLimit');
|
10 | var eachLimit = require('async/eachLimit');
|
11 | var Common = require('../Common.js');
|
12 | var cst = require('../../constants.js');
|
13 | var util = require('util');
|
14 | var tmpPath = require('os').tmpdir;
|
15 | var which = require('../tools/which.js');
|
16 | var sexec = require('../tools/sexec')
|
17 | module.exports = function(CLI) {
|
18 | |
19 |
|
20 |
|
21 |
|
22 | function isNotRoot(startup_mode, platform, opts, cb) {
|
23 | Common.printOut(`${cst.PREFIX_MSG}To ${startup_mode} the Startup Script, copy/paste the following command:`);
|
24 | if (opts.user) {
|
25 | console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 ' + opts.args[1].name() + ' ' + platform + ' -u ' + opts.user + ' --hp ' + process.env.HOME);
|
26 | return cb(new Error('You have to run this with elevated rights'));
|
27 | }
|
28 | return sexec('whoami', {silent: true}, function(err, stdout, stderr) {
|
29 | 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);
|
30 | return cb(new Error('You have to run this with elevated rights'));
|
31 | });
|
32 | }
|
33 |
|
34 | |
35 |
|
36 |
|
37 | function detectInitSystem() {
|
38 | var hash_map = {
|
39 | 'systemctl' : 'systemd',
|
40 | 'update-rc.d': 'upstart',
|
41 | 'chkconfig' : 'systemv',
|
42 | 'rc-update' : 'openrc',
|
43 | 'launchctl' : 'launchd',
|
44 | 'sysrc' : 'rcd',
|
45 | 'rcctl' : 'rcd-openbsd',
|
46 | 'svcadm' : 'smf'
|
47 | };
|
48 | var init_systems = Object.keys(hash_map);
|
49 |
|
50 | for (var i = 0; i < init_systems.length; i++) {
|
51 | if (which(init_systems[i]) != null) {
|
52 | break;
|
53 | }
|
54 | }
|
55 |
|
56 | if (i >= init_systems.length) {
|
57 | Common.printError(cst.PREFIX_MSG_ERR + 'Init system not found');
|
58 | return null;
|
59 | }
|
60 | Common.printOut(cst.PREFIX_MSG + 'Init System found: ' + chalk.bold(hash_map[init_systems[i]]));
|
61 | return hash_map[init_systems[i]];
|
62 | }
|
63 |
|
64 | CLI.prototype.uninstallStartup = function(platform, opts, cb) {
|
65 | var commands;
|
66 | var that = this;
|
67 | var actual_platform = detectInitSystem();
|
68 | var user = opts.user || process.env.USER || process.env.LOGNAME;
|
69 | var service_name = (opts.serviceName || 'pm2-' + user);
|
70 | var openrc_service_name = 'pm2';
|
71 | var launchd_service_name = (opts.serviceName || 'pm2.' + user);
|
72 |
|
73 | if (!platform)
|
74 | platform = actual_platform;
|
75 | else if (actual_platform && actual_platform !== platform) {
|
76 | Common.printOut('-----------------------------------------------------------')
|
77 | Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
|
78 | Common.printOut(' Please verify that your choice is indeed your init system')
|
79 | Common.printOut(' If you arent sure, just run : pm2 startup')
|
80 | Common.printOut('-----------------------------------------------------------')
|
81 | }
|
82 | if (platform === null)
|
83 | throw new Error('Init system not found')
|
84 |
|
85 | if (!cb) {
|
86 | cb = function(err, data) {
|
87 | if (err)
|
88 | return that.exitCli(cst.ERROR_EXIT);
|
89 | return that.exitCli(cst.SUCCESS_EXIT);
|
90 | }
|
91 | }
|
92 |
|
93 | if (process.getuid() != 0) {
|
94 | return isNotRoot('unsetup', platform, opts, cb);
|
95 | }
|
96 |
|
97 | if (fs.existsSync('/etc/init.d/pm2-init.sh')) {
|
98 | platform = 'oldsystem';
|
99 | }
|
100 |
|
101 | switch(platform) {
|
102 | case 'systemd':
|
103 | commands = [
|
104 | 'systemctl stop ' + service_name,
|
105 | 'systemctl disable ' + service_name,
|
106 | 'rm /etc/systemd/system/' + service_name + '.service'
|
107 | ];
|
108 | break;
|
109 | case 'systemv':
|
110 | commands = [
|
111 | 'chkconfig ' + service_name + ' off',
|
112 | 'rm /etc/init.d/' + service_name
|
113 | ];
|
114 | break;
|
115 | case 'oldsystem':
|
116 | Common.printOut(cst.PREFIX_MSG + 'Disabling and deleting old startup system');
|
117 | commands = [
|
118 | 'update-rc.d pm2-init.sh disable',
|
119 | 'update-rc.d -f pm2-init.sh remove',
|
120 | 'rm /etc/init.d/pm2-init.sh'
|
121 | ];
|
122 | break;
|
123 | case 'openrc':
|
124 | service_name = openrc_service_name;
|
125 | commands = [
|
126 | '/etc/init.d/' + service_name + ' stop',
|
127 | 'rc-update delete ' + service_name + ' default',
|
128 | 'rm /etc/init.d/' + service_name
|
129 | ];
|
130 | break;
|
131 | case 'upstart':
|
132 | commands = [
|
133 | 'update-rc.d ' + service_name + ' disable',
|
134 | 'update-rc.d -f ' + service_name + ' remove',
|
135 | 'rm /etc/init.d/' + service_name
|
136 | ];
|
137 | break;
|
138 | case 'launchd':
|
139 | var destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
|
140 | commands = [
|
141 | 'launchctl remove ' + launchd_service_name + ' || true',
|
142 | 'rm ' + destination
|
143 | ];
|
144 | break;
|
145 | case 'rcd':
|
146 | service_name = (opts.serviceName || 'pm2_' + user);
|
147 | commands = [
|
148 | '/usr/local/etc/rc.d/' + service_name + ' stop',
|
149 | 'sysrc -x ' + service_name + '_enable',
|
150 | 'rm /usr/local/etc/rc.d/' + service_name
|
151 | ];
|
152 | break;
|
153 | case 'rcd-openbsd':
|
154 | service_name = (opts.serviceName || 'pm2_' + user);
|
155 | var destination = path.join('/etc/rc.d', service_name);
|
156 | commands = [
|
157 | 'rcctl stop ' + service_name,
|
158 | 'rcctl disable ' + service_name,
|
159 | 'rm ' + destination
|
160 | ];
|
161 | break;
|
162 | case 'smf':
|
163 | service_name = (opts.serviceName || 'pm2_' + user);
|
164 | commands = [
|
165 | 'svcadm disable ' + service_name,
|
166 | 'svccfg delete -f ' + service_name
|
167 | ]
|
168 | };
|
169 |
|
170 | sexec(commands.join('&& '), function(code, stdout, stderr) {
|
171 | Common.printOut(stdout);
|
172 | Common.printOut(stderr);
|
173 | if (code == 0) {
|
174 | Common.printOut(cst.PREFIX_MSG + chalk.bold('Init file disabled.'));
|
175 | } else {
|
176 | Common.printOut(cst.ERROR_MSG + chalk.bold('Return code : ' + code));
|
177 | }
|
178 |
|
179 | cb(null, {
|
180 | commands : commands,
|
181 | platform : platform
|
182 | });
|
183 | });
|
184 | };
|
185 |
|
186 | |
187 |
|
188 |
|
189 |
|
190 |
|
191 | CLI.prototype.startup = function(platform, opts, cb) {
|
192 | var that = this;
|
193 | var actual_platform = detectInitSystem();
|
194 | var user = (opts.user || process.env.USER || process.env.LOGNAME);
|
195 | var service_name = (opts.serviceName || 'pm2-' + user);
|
196 | var openrc_service_name = 'pm2';
|
197 | var launchd_service_name = (opts.serviceName || 'pm2.' + user);
|
198 |
|
199 | if (!platform)
|
200 | platform = actual_platform;
|
201 | else if (actual_platform && actual_platform !== platform) {
|
202 | Common.printOut('-----------------------------------------------------------')
|
203 | Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)
|
204 | Common.printOut(' Please verify that your choice is indeed your init system')
|
205 | Common.printOut(' If you arent sure, just run : pm2 startup')
|
206 | Common.printOut('-----------------------------------------------------------')
|
207 | }
|
208 | if (platform == null)
|
209 | throw new Error('Init system not found');
|
210 |
|
211 | if (!cb) {
|
212 | cb = function(err, data) {
|
213 | if (err)
|
214 | return that.exitCli(cst.ERROR_EXIT);
|
215 | return that.exitCli(cst.SUCCESS_EXIT);
|
216 | }
|
217 | }
|
218 |
|
219 | if (process.getuid() != 0) {
|
220 | return isNotRoot('setup', platform, opts, cb);
|
221 | }
|
222 |
|
223 | var destination;
|
224 | var commands;
|
225 | var template;
|
226 |
|
227 | function getTemplate(type) {
|
228 | return fs.readFileSync(path.join(__dirname, '..', 'templates/init-scripts', type + '.tpl'), {encoding: 'utf8'});
|
229 | }
|
230 |
|
231 | switch(platform) {
|
232 | case 'ubuntu':
|
233 | case 'centos':
|
234 | case 'arch':
|
235 | case 'oracle':
|
236 | case 'systemd':
|
237 | if (opts.waitIp)
|
238 | template = getTemplate('systemd-online');
|
239 | else
|
240 | template = getTemplate('systemd');
|
241 | destination = '/etc/systemd/system/' + service_name + '.service';
|
242 | commands = [
|
243 | 'systemctl enable ' + service_name
|
244 | ];
|
245 | break;
|
246 | case 'ubuntu14':
|
247 | case 'ubuntu12':
|
248 | case 'upstart':
|
249 | template = getTemplate('upstart');
|
250 | destination = '/etc/init.d/' + service_name;
|
251 | commands = [
|
252 | 'chmod +x ' + destination,
|
253 | 'mkdir -p /var/lock/subsys',
|
254 | 'touch /var/lock/subsys/' + service_name,
|
255 | 'update-rc.d ' + service_name + ' defaults'
|
256 | ];
|
257 | break;
|
258 | case 'systemv':
|
259 | case 'amazon':
|
260 | case 'centos6':
|
261 | template = getTemplate('upstart');
|
262 | destination = '/etc/init.d/' + service_name;
|
263 | commands = [
|
264 | 'chmod +x ' + destination,
|
265 | 'mkdir -p /var/lock/subsys',
|
266 | 'touch /var/lock/subsys/' + service_name,
|
267 | 'chkconfig --add ' + service_name,
|
268 | 'chkconfig ' + service_name + ' on',
|
269 | 'initctl list'
|
270 | ];
|
271 | break;
|
272 | case 'macos':
|
273 | case 'darwin':
|
274 | case 'launchd':
|
275 | template = getTemplate('launchd');
|
276 | destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');
|
277 | commands = [
|
278 | 'launchctl load -w ' + destination
|
279 | ]
|
280 | break;
|
281 | case 'freebsd':
|
282 | case 'rcd':
|
283 | template = getTemplate('rcd');
|
284 | service_name = (opts.serviceName || 'pm2_' + user);
|
285 | destination = '/usr/local/etc/rc.d/' + service_name;
|
286 | commands = [
|
287 | 'chmod 755 ' + destination,
|
288 | 'sysrc ' + service_name + '_enable=YES'
|
289 | ];
|
290 | break;
|
291 | case 'openbsd':
|
292 | case 'rcd-openbsd':
|
293 | template = getTemplate('rcd-openbsd');
|
294 | service_name = (opts.serviceName || 'pm2_' + user);
|
295 | destination = path.join('/etc/rc.d/', service_name);
|
296 | commands = [
|
297 | 'chmod 755 ' + destination,
|
298 | 'rcctl enable ' + service_name,
|
299 | 'rcctl start ' + service_name
|
300 | ];
|
301 | break;
|
302 | case 'openrc':
|
303 | template = getTemplate('openrc');
|
304 | service_name = openrc_service_name;
|
305 | destination = '/etc/init.d/' + service_name;
|
306 | commands = [
|
307 | 'chmod +x ' + destination,
|
308 | 'rc-update add ' + service_name + ' default'
|
309 | ];
|
310 | break;
|
311 | case 'smf':
|
312 | case 'sunos':
|
313 | case 'solaris':
|
314 | template = getTemplate('smf');
|
315 | service_name = (opts.serviceName || 'pm2_' + user);
|
316 | destination = path.join(tmpPath(), service_name + '.xml');
|
317 | commands = [
|
318 | 'svccfg import ' + destination,
|
319 | 'svcadm enable ' + service_name
|
320 | ];
|
321 | break;
|
322 | default:
|
323 | throw new Error('Unknown platform / init system name');
|
324 | }
|
325 |
|
326 | |
327 |
|
328 |
|
329 | var envPath
|
330 |
|
331 | if (cst.HAS_NODE_EMBEDDED == true)
|
332 | envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
|
333 | else if (new RegExp(path.dirname(process.execPath)).test(process.env.PATH))
|
334 | envPath = process.env.PATH
|
335 | else
|
336 | envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))
|
337 |
|
338 | template = template.replace(/%PM2_PATH%/g, process.mainModule.filename)
|
339 | .replace(/%NODE_PATH%/g, envPath)
|
340 | .replace(/%USER%/g, user)
|
341 | .replace(/%HOME_PATH%/g, opts.hp ? path.resolve(opts.hp, '.pm2') : cst.PM2_ROOT_PATH)
|
342 | .replace(/%SERVICE_NAME%/g, service_name);
|
343 |
|
344 | Common.printOut(chalk.bold('Platform'), platform);
|
345 | Common.printOut(chalk.bold('Template'));
|
346 | Common.printOut(template);
|
347 | Common.printOut(chalk.bold('Target path'));
|
348 | Common.printOut(destination);
|
349 | Common.printOut(chalk.bold('Command list'));
|
350 | Common.printOut(commands);
|
351 |
|
352 | Common.printOut(cst.PREFIX_MSG + 'Writing init configuration in ' + destination);
|
353 | try {
|
354 | fs.writeFileSync(destination, template);
|
355 | } catch (e) {
|
356 | console.error(cst.PREFIX_MSG_ERR + 'Failure when trying to write startup script');
|
357 | console.error(e.message || e);
|
358 | return cb(e);
|
359 | }
|
360 |
|
361 | Common.printOut(cst.PREFIX_MSG + 'Making script booting at startup...');
|
362 |
|
363 | forEachLimit(commands, 1, function(command, next) {
|
364 | Common.printOut(cst.PREFIX_MSG + '[-] Executing: %s...', chalk.bold(command));
|
365 |
|
366 | sexec(command, function(code, stdout, stderr) {
|
367 | if (code === 0) {
|
368 | Common.printOut(cst.PREFIX_MSG + chalk.bold('[v] Command successfully executed.'));
|
369 | return next();
|
370 | } else {
|
371 | Common.printOut(chalk.red('[ERROR] Exit code : ' + code))
|
372 | return next(new Error(command + ' failed, see error above.'));
|
373 | }
|
374 | })
|
375 |
|
376 | }, function(err) {
|
377 | if (err) {
|
378 | console.error(cst.PREFIX_MSG_ERR + (err.message || err));
|
379 | return cb(err);
|
380 | }
|
381 | Common.printOut(chalk.bold.blue('+---------------------------------------+'));
|
382 | Common.printOut(chalk.bold.blue((cst.PREFIX_MSG + 'Freeze a process list on reboot via:' )));
|
383 | Common.printOut(chalk.bold('$ pm2 save'));
|
384 | Common.printOut('');
|
385 | Common.printOut(chalk.bold.blue(cst.PREFIX_MSG + 'Remove init script via:'));
|
386 | Common.printOut(chalk.bold('$ pm2 unstartup ' + platform));
|
387 |
|
388 | return cb(null, {
|
389 | destination : destination,
|
390 | template : template
|
391 | });
|
392 | });
|
393 | };
|
394 |
|
395 | |
396 |
|
397 |
|
398 |
|
399 | CLI.prototype.autodump = function(cb) {
|
400 | return cb()
|
401 | }
|
402 |
|
403 | |
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | CLI.prototype.dump = function(force, cb) {
|
410 | var env_arr = [];
|
411 | var that = this;
|
412 |
|
413 | if (typeof(force) === 'function') {
|
414 | cb = force
|
415 | force = false
|
416 | }
|
417 |
|
418 | if (!cb)
|
419 | Common.printOut(cst.PREFIX_MSG + 'Saving current process list...');
|
420 |
|
421 | that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
422 | if (err) {
|
423 | Common.printError('Error retrieving process list: ' + err);
|
424 | return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
|
425 | }
|
426 |
|
427 | |
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | function fin(err) {
|
434 |
|
435 |
|
436 |
|
437 | if (!force && env_arr.length === 0 && !process.env.FORCE) {
|
438 |
|
439 |
|
440 | if (!fs.existsSync(cst.DUMP_FILE_PATH)) {
|
441 | that.clearDump(function(){});
|
442 | }
|
443 |
|
444 |
|
445 |
|
446 | if (cb) {
|
447 | return cb(new Error('Process list empty, cannot save empty list'));
|
448 | } else {
|
449 | Common.printOut(cst.PREFIX_MSG_WARNING + 'PM2 is not managing any process, skipping save...');
|
450 | Common.printOut(cst.PREFIX_MSG_WARNING + 'To force saving use: pm2 save --force');
|
451 | that.exitCli(cst.SUCCESS_EXIT);
|
452 | return;
|
453 | }
|
454 | }
|
455 |
|
456 |
|
457 | try {
|
458 | if (fs.existsSync(cst.DUMP_FILE_PATH)) {
|
459 | fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));
|
460 | }
|
461 | } catch (e) {
|
462 | console.error(e.stack || e);
|
463 | Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to back up dump file in %s', cst.DUMP_BACKUP_FILE_PATH);
|
464 | }
|
465 |
|
466 |
|
467 | try {
|
468 | fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(env_arr, '', 2));
|
469 | } catch (e) {
|
470 | console.error(e.stack || e);
|
471 | try {
|
472 |
|
473 | if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {
|
474 | fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));
|
475 | }
|
476 | } catch (e) {
|
477 |
|
478 | fs.unlinkSync(cst.DUMP_FILE_PATH);
|
479 | console.error(e.stack || e);
|
480 | }
|
481 | Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH);
|
482 | return that.exitCli(cst.ERROR_EXIT);
|
483 | }
|
484 | if (cb) return cb(null, {success:true});
|
485 |
|
486 | Common.printOut(cst.PREFIX_MSG + 'Successfully saved in %s', cst.DUMP_FILE_PATH);
|
487 | return that.exitCli(cst.SUCCESS_EXIT);
|
488 | }
|
489 |
|
490 | (function ex(apps) {
|
491 | if (!apps[0]) return fin(null);
|
492 | delete apps[0].pm2_env.instances;
|
493 | delete apps[0].pm2_env.pm_id;
|
494 | delete apps[0].pm2_env.prev_restart_delay;
|
495 | if (!apps[0].pm2_env.pmx_module)
|
496 | env_arr.push(apps[0].pm2_env);
|
497 | apps.shift();
|
498 | return ex(apps);
|
499 | })(list);
|
500 | });
|
501 | };
|
502 |
|
503 | |
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 | CLI.prototype.clearDump = function(cb) {
|
510 | fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([]));
|
511 |
|
512 | if(cb && typeof cb === 'function') return cb();
|
513 |
|
514 | Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH);
|
515 | return this.exitCli(cst.SUCCESS_EXIT);
|
516 | };
|
517 |
|
518 | |
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 | CLI.prototype.resurrect = function(cb) {
|
525 | var apps = {};
|
526 | var that = this;
|
527 |
|
528 | var processes;
|
529 |
|
530 | function readDumpFile(dumpFilePath) {
|
531 | Common.printOut(cst.PREFIX_MSG + 'Restoring processes located in %s', dumpFilePath);
|
532 | try {
|
533 | var apps = fs.readFileSync(dumpFilePath);
|
534 | } catch (e) {
|
535 | Common.printError(cst.PREFIX_MSG_ERR + 'Failed to read dump file in %s', dumpFilePath);
|
536 | throw e;
|
537 | }
|
538 |
|
539 | return apps;
|
540 | }
|
541 |
|
542 | function parseDumpFile(dumpFilePath, apps) {
|
543 | try {
|
544 | var processes = Common.parseConfig(apps, 'none');
|
545 | } catch (e) {
|
546 | Common.printError(cst.PREFIX_MSG_ERR + 'Failed to parse dump file in %s', dumpFilePath);
|
547 | try {
|
548 | fs.unlinkSync(dumpFilePath);
|
549 | } catch (e) {
|
550 | console.error(e.stack || e);
|
551 | }
|
552 | throw e;
|
553 | }
|
554 |
|
555 | return processes;
|
556 | }
|
557 |
|
558 |
|
559 | try {
|
560 | apps = readDumpFile(cst.DUMP_FILE_PATH);
|
561 | processes = parseDumpFile(cst.DUMP_FILE_PATH, apps);
|
562 | } catch(e) {
|
563 | try {
|
564 | apps = readDumpFile(cst.DUMP_BACKUP_FILE_PATH);
|
565 | processes = parseDumpFile(cst.DUMP_BACKUP_FILE_PATH, apps);
|
566 | } catch(e) {
|
567 | Common.printError(cst.PREFIX_MSG_ERR + 'No processes saved; DUMP file doesn\'t exist');
|
568 |
|
569 |
|
570 | return that.speedList();
|
571 | }
|
572 | }
|
573 |
|
574 | that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
575 | if (err) {
|
576 | Common.printError(err);
|
577 | return that.exitCli(1);
|
578 | }
|
579 |
|
580 | var current = [];
|
581 | var target = [];
|
582 |
|
583 | list.forEach(function(app) {
|
584 | if (!current[app.name])
|
585 | current[app.name] = 0;
|
586 | current[app.name]++;
|
587 | });
|
588 |
|
589 | processes.forEach(function(app) {
|
590 | if (!target[app.name])
|
591 | target[app.name] = 0;
|
592 | target[app.name]++;
|
593 | });
|
594 |
|
595 | var tostart = Object.keys(target).filter(function(i) {
|
596 | return Object.keys(current).indexOf(i) < 0;
|
597 | })
|
598 |
|
599 | eachLimit(processes, cst.CONCURRENT_ACTIONS, function(app, next) {
|
600 | if (tostart.indexOf(app.name) == -1)
|
601 | return next();
|
602 | that.Client.executeRemote('prepare', app, function(err, dt) {
|
603 | if (err)
|
604 | Common.printError(err);
|
605 | else
|
606 | Common.printOut(cst.PREFIX_MSG + 'Process %s restored', app.pm_exec_path);
|
607 | next();
|
608 | });
|
609 | }, function(err) {
|
610 | return cb ? cb(null, apps) : that.speedList();
|
611 | });
|
612 | });
|
613 | };
|
614 |
|
615 | }
|