@zappinginc/zm2 6.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.gitattributes +4 -0
  3. package/.mocharc.js +14 -0
  4. package/CHANGELOG.md +2416 -0
  5. package/CLAUDE.md +84 -0
  6. package/CONTRIBUTING.md +124 -0
  7. package/GNU-AGPL-3.0.txt +665 -0
  8. package/LICENSE +1 -0
  9. package/README.md +248 -0
  10. package/bin/zm2 +3 -0
  11. package/bin/zm2-dev +3 -0
  12. package/bin/zm2-docker +3 -0
  13. package/bin/zm2-runtime +3 -0
  14. package/bin/zm2-windows +3 -0
  15. package/bin/zm2.ps1 +3 -0
  16. package/bun.lock +421 -0
  17. package/constants.js +114 -0
  18. package/index.js +13 -0
  19. package/lib/API/Configuration.js +212 -0
  20. package/lib/API/Containerizer.js +335 -0
  21. package/lib/API/Dashboard.js +459 -0
  22. package/lib/API/Deploy.js +117 -0
  23. package/lib/API/Extra.js +775 -0
  24. package/lib/API/ExtraMgmt/Docker.js +30 -0
  25. package/lib/API/Log.js +315 -0
  26. package/lib/API/LogManagement.js +371 -0
  27. package/lib/API/Modules/LOCAL.js +122 -0
  28. package/lib/API/Modules/Modularizer.js +148 -0
  29. package/lib/API/Modules/NPM.js +445 -0
  30. package/lib/API/Modules/TAR.js +362 -0
  31. package/lib/API/Modules/flagExt.js +46 -0
  32. package/lib/API/Modules/index.js +120 -0
  33. package/lib/API/Monit.js +247 -0
  34. package/lib/API/Serve.js +343 -0
  35. package/lib/API/Startup.js +629 -0
  36. package/lib/API/UX/helpers.js +213 -0
  37. package/lib/API/UX/index.js +9 -0
  38. package/lib/API/UX/pm2-describe.js +193 -0
  39. package/lib/API/UX/pm2-ls-minimal.js +31 -0
  40. package/lib/API/UX/pm2-ls.js +483 -0
  41. package/lib/API/Version.js +382 -0
  42. package/lib/API/interpreter.json +12 -0
  43. package/lib/API/pm2-plus/PM2IO.js +372 -0
  44. package/lib/API/pm2-plus/auth-strategies/CliAuth.js +288 -0
  45. package/lib/API/pm2-plus/auth-strategies/WebAuth.js +187 -0
  46. package/lib/API/pm2-plus/helpers.js +97 -0
  47. package/lib/API/pm2-plus/link.js +126 -0
  48. package/lib/API/pm2-plus/pres/motd +16 -0
  49. package/lib/API/pm2-plus/pres/motd.update +26 -0
  50. package/lib/API/pm2-plus/pres/welcome +28 -0
  51. package/lib/API/pm2-plus/process-selector.js +52 -0
  52. package/lib/API/schema.json +379 -0
  53. package/lib/API.js +1931 -0
  54. package/lib/Client.js +776 -0
  55. package/lib/Common.js +911 -0
  56. package/lib/Configuration.js +304 -0
  57. package/lib/Daemon.js +456 -0
  58. package/lib/Event.js +37 -0
  59. package/lib/God/ActionMethods.js +909 -0
  60. package/lib/God/ClusterMode.js +97 -0
  61. package/lib/God/ForkMode.js +297 -0
  62. package/lib/God/Methods.js +265 -0
  63. package/lib/God/Reload.js +240 -0
  64. package/lib/God.js +632 -0
  65. package/lib/HttpInterface.js +76 -0
  66. package/lib/ProcessContainer.js +305 -0
  67. package/lib/ProcessContainerBun.js +360 -0
  68. package/lib/ProcessContainerFork.js +42 -0
  69. package/lib/ProcessContainerForkBun.js +33 -0
  70. package/lib/ProcessUtils.js +55 -0
  71. package/lib/TreeKill.js +118 -0
  72. package/lib/Utility.js +430 -0
  73. package/lib/VersionCheck.js +46 -0
  74. package/lib/Watcher.js +117 -0
  75. package/lib/Worker.js +169 -0
  76. package/lib/binaries/CLI.js +1041 -0
  77. package/lib/binaries/DevCLI.js +183 -0
  78. package/lib/binaries/Runtime.js +101 -0
  79. package/lib/binaries/Runtime4Docker.js +192 -0
  80. package/lib/completion.js +229 -0
  81. package/lib/completion.sh +40 -0
  82. package/lib/motd +36 -0
  83. package/lib/templates/Dockerfiles/Dockerfile-java.tpl +7 -0
  84. package/lib/templates/Dockerfiles/Dockerfile-nodejs.tpl +8 -0
  85. package/lib/templates/Dockerfiles/Dockerfile-ruby.tpl +7 -0
  86. package/lib/templates/ecosystem-es.tpl +24 -0
  87. package/lib/templates/ecosystem-simple-es.tpl +8 -0
  88. package/lib/templates/ecosystem-simple.tpl +6 -0
  89. package/lib/templates/ecosystem.tpl +22 -0
  90. package/lib/templates/init-scripts/launchd.tpl +35 -0
  91. package/lib/templates/init-scripts/openrc.tpl +52 -0
  92. package/lib/templates/init-scripts/pm2-init-amazon.sh +86 -0
  93. package/lib/templates/init-scripts/rcd-openbsd.tpl +41 -0
  94. package/lib/templates/init-scripts/rcd.tpl +44 -0
  95. package/lib/templates/init-scripts/smf.tpl +43 -0
  96. package/lib/templates/init-scripts/systemd-online.tpl +22 -0
  97. package/lib/templates/init-scripts/systemd.tpl +22 -0
  98. package/lib/templates/init-scripts/upstart.tpl +103 -0
  99. package/lib/templates/logrotate.d/pm2 +10 -0
  100. package/lib/templates/sample-apps/http-server/README.md +14 -0
  101. package/lib/templates/sample-apps/http-server/api.js +9 -0
  102. package/lib/templates/sample-apps/http-server/ecosystem.config.js +14 -0
  103. package/lib/templates/sample-apps/http-server/package.json +11 -0
  104. package/lib/templates/sample-apps/pm2-plus-metrics-actions/README.md +45 -0
  105. package/lib/templates/sample-apps/pm2-plus-metrics-actions/custom-metrics.js +66 -0
  106. package/lib/templates/sample-apps/pm2-plus-metrics-actions/ecosystem.config.js +12 -0
  107. package/lib/templates/sample-apps/pm2-plus-metrics-actions/package.json +11 -0
  108. package/lib/templates/sample-apps/python-app/README.md +4 -0
  109. package/lib/templates/sample-apps/python-app/echo.py +7 -0
  110. package/lib/templates/sample-apps/python-app/ecosystem.config.js +12 -0
  111. package/lib/templates/sample-apps/python-app/package.json +11 -0
  112. package/lib/tools/Config.js +248 -0
  113. package/lib/tools/IsAbsolute.js +20 -0
  114. package/lib/tools/copydirSync.js +101 -0
  115. package/lib/tools/deleteFolderRecursive.js +19 -0
  116. package/lib/tools/find-package-json.js +74 -0
  117. package/lib/tools/fmt.js +72 -0
  118. package/lib/tools/isbinaryfile.js +94 -0
  119. package/lib/tools/json5.js +752 -0
  120. package/lib/tools/open.js +63 -0
  121. package/lib/tools/passwd.js +58 -0
  122. package/lib/tools/promise.min.js +1 -0
  123. package/lib/tools/sexec.js +55 -0
  124. package/lib/tools/treeify.js +113 -0
  125. package/lib/tools/which.js +120 -0
  126. package/lib/tools/xdg-open +861 -0
  127. package/package.json +219 -0
  128. package/paths.js +93 -0
  129. package/pm2 +11 -0
  130. package/preinstall.js +24 -0
  131. package/run.sh +9 -0
  132. package/types/index.d.ts +722 -0
  133. package/types/tsconfig.json +14 -0
package/lib/API.js ADDED
@@ -0,0 +1,1931 @@
1
+ /**
2
+ * Copyright 2013-2022 the ZM2 project authors. All rights reserved.
3
+ * Use of this source code is governed by a license that
4
+ * can be found in the LICENSE file.
5
+ */
6
+ 'use strict';
7
+
8
+ const commander = require('commander');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const eachLimit = require('async/eachLimit');
12
+ const series = require('async/series');
13
+ const debug = require('debug')('zm2:cli');
14
+ const util = require('util');
15
+ const chalk = require('ansis');
16
+ const fclone = require('fclone');
17
+
18
+ var DockerMgmt = require('./API/ExtraMgmt/Docker.js')
19
+ var conf = require('../constants.js');
20
+ var Client = require('./Client');
21
+ var Common = require('./Common');
22
+ var KMDaemon = require('@pm2/agent/src/InteractorClient');
23
+ var Config = require('./tools/Config');
24
+ var Modularizer = require('./API/Modules/Modularizer.js');
25
+ var path_structure = require('../paths.js');
26
+ var UX = require('./API/UX');
27
+ var pkg = require('../package.json');
28
+ var hf = require('./API/Modules/flagExt.js');
29
+ var Configuration = require('./Configuration.js');
30
+ const sexec = require('./tools/sexec.js')
31
+
32
+ var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');
33
+
34
+ /**
35
+ * Main Function to be imported
36
+ * can be aliased to ZM2
37
+ *
38
+ * To use it when ZM2 is installed as a module:
39
+ *
40
+ * var ZM2 = require('zm2');
41
+ *
42
+ * var zm2 = ZM2(<opts>);
43
+ *
44
+ *
45
+ * @param {Object} opts
46
+ * @param {String} [opts.cwd=<current>] override zm2 cwd for starting scripts
47
+ * @param {String} [opts.pm2_home=[<paths.js>]] zm2 directory for log, pids, socket files
48
+ * @param {Boolean} [opts.independent=false] unique ZM2 instance (random pm2_home)
49
+ * @param {Boolean} [opts.daemon_mode=true] should be called in the same process or not
50
+ * @param {String} [opts.public_key=null] zm2 plus bucket public key
51
+ * @param {String} [opts.secret_key=null] zm2 plus bucket secret key
52
+ * @param {String} [opts.machine_name=null] zm2 plus instance name
53
+ */
54
+ class API {
55
+
56
+ constructor (opts) {
57
+ if (!opts) opts = {};
58
+ var that = this;
59
+
60
+ this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;
61
+ this.pm2_home = conf.PM2_ROOT_PATH;
62
+ this.public_key = conf.PUBLIC_KEY || opts.public_key || null;
63
+ this.secret_key = conf.SECRET_KEY || opts.secret_key || null;
64
+ this.machine_name = conf.MACHINE_NAME || opts.machine_name || null
65
+
66
+ /**
67
+ * CWD resolution
68
+ */
69
+ this.cwd = process.cwd();
70
+ if (opts.cwd) {
71
+ this.cwd = path.resolve(opts.cwd);
72
+ }
73
+
74
+ /**
75
+ * ZM2 HOME resolution
76
+ */
77
+ if (opts.pm2_home && opts.independent == true)
78
+ throw new Error('You cannot set a zm2_home and independent instance in same time');
79
+
80
+ if (opts.pm2_home) {
81
+ // Override default conf file
82
+ this.pm2_home = opts.pm2_home;
83
+ conf = Object.assign(conf, path_structure(this.pm2_home));
84
+ }
85
+ else if (opts.independent == true && conf.IS_WINDOWS === false) {
86
+ // Create an unique zm2 instance
87
+ const crypto = require('crypto');
88
+ var random_file = crypto.randomBytes(8).toString('hex');
89
+ this.pm2_home = path.join('/tmp', random_file);
90
+
91
+ // If we dont explicitly tell to have a daemon
92
+ // It will go as in proc
93
+ if (typeof(opts.daemon_mode) == 'undefined')
94
+ this.daemon_mode = false;
95
+ conf = Object.assign(conf, path_structure(this.pm2_home));
96
+ }
97
+
98
+ this._conf = conf;
99
+
100
+ if (conf.IS_WINDOWS) {
101
+ // Weird fix, may need to be dropped
102
+ // @todo windows connoisseur double check
103
+ if (process.stdout._handle && process.stdout._handle.setBlocking)
104
+ process.stdout._handle.setBlocking(true);
105
+ }
106
+
107
+ this.Client = new Client({
108
+ pm2_home: that.pm2_home,
109
+ conf: this._conf,
110
+ secret_key: this.secret_key,
111
+ public_key: this.public_key,
112
+ daemon_mode: this.daemon_mode,
113
+ machine_name: this.machine_name
114
+ });
115
+
116
+ this.pm2_configuration = Configuration.getSync('pm2') || {}
117
+
118
+ this.gl_interact_infos = null;
119
+ this.gl_is_km_linked = false;
120
+
121
+ try {
122
+ var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH);
123
+ pid = parseInt(pid.toString().trim());
124
+ process.kill(pid, 0);
125
+ that.gl_is_km_linked = true;
126
+ } catch (e) {
127
+ that.gl_is_km_linked = false;
128
+ }
129
+
130
+ // For testing purposes
131
+ if (this.secret_key && process.env.NODE_ENV == 'local_test')
132
+ that.gl_is_km_linked = true;
133
+
134
+ KMDaemon.ping(this._conf, function(err, result) {
135
+ if (!err && result === true) {
136
+ fs.readFile(conf.INTERACTION_CONF, (err, _conf) => {
137
+ if (!err) {
138
+ try {
139
+ that.gl_interact_infos = JSON.parse(_conf.toString())
140
+ } catch(e) {
141
+ var json5 = require('./tools/json5.js')
142
+ try {
143
+ that.gl_interact_infos = json5.parse(_conf.toString())
144
+ } catch(e) {
145
+ console.error(e)
146
+ that.gl_interact_infos = null
147
+ }
148
+ }
149
+ }
150
+ })
151
+ }
152
+ })
153
+
154
+ this.gl_retry = 0;
155
+ }
156
+
157
+ /**
158
+ * Connect to ZM2
159
+ * Calling this command is now optional
160
+ *
161
+ * @param {Function} cb callback once zm2 is ready for commands
162
+ */
163
+ connect (noDaemon, cb) {
164
+ var that = this;
165
+ this.start_timer = new Date();
166
+
167
+ if (typeof(cb) == 'undefined') {
168
+ cb = noDaemon;
169
+ noDaemon = false;
170
+ } else if (noDaemon === true) {
171
+ // Backward compatibility with ZM2 1.x
172
+ this.Client.daemon_mode = false;
173
+ this.daemon_mode = false;
174
+ }
175
+
176
+ this.Client.start(function(err, meta) {
177
+ if (err)
178
+ return cb(err);
179
+
180
+ if (meta.new_pm2_instance == false && that.daemon_mode === true)
181
+ return cb(err, meta);
182
+
183
+ that.launchSysMonitoring(() => {})
184
+ // If new zm2 instance has been popped
185
+ // Launch all modules
186
+ that.launchAll(that, function(err_mod) {
187
+ return cb(err, meta);
188
+ });
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Usefull when custom ZM2 created with independent flag set to true
194
+ * This will cleanup the newly created instance
195
+ * by removing folder, killing ZM2 and so on
196
+ *
197
+ * @param {Function} cb callback once cleanup is successfull
198
+ */
199
+ destroy (cb) {
200
+ var that = this;
201
+
202
+ debug('Killing and deleting current deamon');
203
+
204
+ this.killDaemon(function() {
205
+ var cmd = 'rm -rf ' + that.pm2_home;
206
+ var test_path = path.join(that.pm2_home, 'module_conf.json');
207
+ var test_path_2 = path.join(that.pm2_home, 'pm2.pid');
208
+
209
+ if (that.pm2_home.indexOf('.pm2') > -1)
210
+ return cb(new Error('Destroy is not a allowed method on .pm2'));
211
+
212
+ fs.access(test_path, fs.constants.R_OK, function(err) {
213
+ if (err) return cb(err);
214
+ debug('Deleting temporary folder %s', that.pm2_home);
215
+ sexec(cmd, cb);
216
+ });
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Disconnect from ZM2 instance
222
+ * This will allow your software to exit by itself
223
+ *
224
+ * @param {Function} [cb] optional callback once connection closed
225
+ */
226
+ disconnect (cb) {
227
+ var that = this;
228
+
229
+ if (!cb) cb = function() {};
230
+
231
+ this.Client.close(function(err, data) {
232
+ debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);
233
+ return cb(err, data);
234
+ });
235
+ };
236
+
237
+ /**
238
+ * Alias on disconnect
239
+ * @param cb
240
+ */
241
+ close (cb) {
242
+ this.disconnect(cb);
243
+ }
244
+
245
+ /**
246
+ * Launch modules
247
+ *
248
+ * @param {Function} cb callback once zm2 has launched modules
249
+ */
250
+ launchModules (cb) {
251
+ this.launchAll(this, cb);
252
+ }
253
+
254
+ /**
255
+ * Enable bus allowing to retrieve various process event
256
+ * like logs, restarts, reloads
257
+ *
258
+ * @param {Function} cb callback called with 1st param err and 2nb param the bus
259
+ */
260
+ launchBus (cb) {
261
+ this.Client.launchBus(cb);
262
+ }
263
+
264
+ /**
265
+ * Exit methods for API
266
+ * @param {Integer} code exit code for terminal
267
+ */
268
+ exitCli (code) {
269
+ var that = this;
270
+
271
+ // Do nothing if ZM2 called programmatically (also in speedlist)
272
+ if (conf.PM2_PROGRAMMATIC && (process.env.ZM2_USAGE || process.env.PM2_USAGE) != 'CLI') return false;
273
+
274
+ KMDaemon.disconnectRPC(function() {
275
+ that.Client.close(function() {
276
+ code = code || 0;
277
+ // Safe exits process after all streams are drained.
278
+ // file descriptor flag.
279
+ var fds = 0;
280
+ // exits process when stdout (1) and sdterr(2) are both drained.
281
+ function tryToExit() {
282
+ if ((fds & 1) && (fds & 2)) {
283
+ debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000);
284
+ process.exit(code);
285
+ }
286
+ }
287
+
288
+ [process.stdout, process.stderr].forEach(function(std) {
289
+ var fd = std.fd;
290
+ if (!std.bufferSize) {
291
+ // bufferSize equals 0 means current stream is drained.
292
+ fds = fds | fd;
293
+ } else {
294
+ // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`.
295
+ std.write && std.write('', function() {
296
+ fds = fds | fd;
297
+ tryToExit();
298
+ });
299
+ }
300
+ // Does not write anything more.
301
+ delete std.write;
302
+ });
303
+ tryToExit();
304
+ });
305
+ });
306
+ }
307
+
308
+ ////////////////////////////
309
+ // Application management //
310
+ ////////////////////////////
311
+
312
+ /**
313
+ * Start a file or json with configuration
314
+ * @param {Object||String} cmd script to start or json
315
+ * @param {Function} cb called when application has been started
316
+ */
317
+ start (cmd, opts, cb) {
318
+ if (typeof(opts) == "function") {
319
+ cb = opts;
320
+ opts = {};
321
+ }
322
+ if (!opts) opts = {};
323
+
324
+ var that = this;
325
+ if (Array.isArray(opts.watch) && opts.watch.length === 0)
326
+ opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false;
327
+
328
+ if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) {
329
+ that._startJson(cmd, opts, 'restartProcessId', (err, procs) => {
330
+ return cb ? cb(err, procs) : this.speedList()
331
+ })
332
+ }
333
+ else {
334
+ that._startScript(cmd, opts, (err, procs) => {
335
+ return cb ? cb(err, procs) : this.speedList(0)
336
+ })
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Reset process counters
342
+ *
343
+ * @method resetMetaProcess
344
+ */
345
+ reset (process_name, cb) {
346
+ var that = this;
347
+
348
+ function processIds(ids, cb) {
349
+ eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) {
350
+ that.Client.executeRemote('resetMetaProcessId', id, function(err, res) {
351
+ if (err) console.error(err);
352
+ Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id);
353
+ return next();
354
+ });
355
+ }, function(err) {
356
+ if (err) return cb(Common.retErr(err));
357
+ return cb ? cb(null, {success:true}) : that.speedList();
358
+ });
359
+ }
360
+
361
+ if (process_name == 'all') {
362
+ that.Client.getAllProcessId(function(err, ids) {
363
+ if (err) {
364
+ Common.printError(err);
365
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
366
+ }
367
+ return processIds(ids, cb);
368
+ });
369
+ }
370
+ else if (isNaN(process_name)) {
371
+ that.Client.getProcessIdByName(process_name, function(err, ids) {
372
+ if (err) {
373
+ Common.printError(err);
374
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
375
+ }
376
+ if (ids.length === 0) {
377
+ Common.printError('Unknown process name');
378
+ return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT);
379
+ }
380
+ return processIds(ids, cb);
381
+ });
382
+ } else {
383
+ processIds([process_name], cb);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Update daemonized ZM2 Daemon
389
+ *
390
+ * @param {Function} cb callback when zm2 has been upgraded
391
+ */
392
+ update (cb) {
393
+ var that = this;
394
+
395
+ Common.printOut('Be sure to have the latest version by doing `npm install zm2@latest -g` before doing this procedure.');
396
+
397
+ // Dump ZM2 processes
398
+ that.Client.executeRemote('notifyKillPM2', {}, function() {});
399
+
400
+ that.getVersion(function(err, new_version) {
401
+ // If not linked to ZM2 plus, and update ZM2 to latest, display motd.update
402
+ if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) {
403
+ var dt = fs.readFileSync(path.join(__dirname, that._conf.PM2_UPDATE));
404
+ console.log(dt.toString());
405
+ }
406
+
407
+ that.dump(function(err) {
408
+ that.killDaemon(function() {
409
+ that.Client.launchDaemon({interactor:false}, function(err, child) {
410
+ that.Client.launchRPC(function() {
411
+ that.resurrect(function() {
412
+ Common.printOut(chalk.blue.bold('>>>>>>>>>> ZM2 updated'));
413
+ that.launchSysMonitoring(() => {})
414
+ that.launchAll(that, function() {
415
+ KMDaemon.launchAndInteract(that._conf, {
416
+ pm2_version: pkg.version
417
+ }, function(err, data, interactor_proc) {
418
+ })
419
+ setTimeout(() => {
420
+ return cb ? cb(null, {success:true}) : that.speedList();
421
+ }, 250)
422
+ });
423
+ });
424
+ });
425
+ });
426
+ });
427
+ });
428
+ });
429
+
430
+ return false;
431
+ }
432
+
433
+ /**
434
+ * Reload an application
435
+ *
436
+ * @param {String} process_name Application Name or All
437
+ * @param {Object} opts Options
438
+ * @param {Function} cb Callback
439
+ */
440
+ reload (process_name, opts, cb) {
441
+ var that = this;
442
+
443
+ if (typeof(opts) == "function") {
444
+ cb = opts;
445
+ opts = {};
446
+ }
447
+
448
+ var delay = Common.lockReload();
449
+ if (delay > 0 && opts.force != true) {
450
+ Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force');
451
+ return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);
452
+ }
453
+
454
+ if (Common.isConfigFile(process_name))
455
+ that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {
456
+ Common.unlockReload();
457
+ if (err)
458
+ return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
459
+ return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
460
+ });
461
+ else {
462
+ if (opts && opts.env) {
463
+ var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
464
+ Common.err(err);
465
+ Common.unlockReload();
466
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
467
+ }
468
+
469
+ if (opts && !opts.updateEnv)
470
+ Common.printOut(IMMUTABLE_MSG);
471
+
472
+ that._operate('reloadProcessId', process_name, opts, function(err, apps) {
473
+ Common.unlockReload();
474
+
475
+ if (err)
476
+ return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
477
+ return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
478
+ });
479
+ }
480
+ }
481
+
482
+ /**
483
+ * Restart process
484
+ *
485
+ * @param {String} cmd Application Name / Process id / JSON application file / 'all'
486
+ * @param {Object} opts Extra options to be updated
487
+ * @param {Function} cb Callback
488
+ */
489
+ restart (cmd, opts, cb) {
490
+ if (typeof(opts) == "function") {
491
+ cb = opts;
492
+ opts = {};
493
+ }
494
+ var that = this;
495
+
496
+ if (typeof(cmd) === 'number')
497
+ cmd = cmd.toString();
498
+
499
+ if (cmd == "-") {
500
+ // Restart from PIPED JSON
501
+ process.stdin.resume();
502
+ process.stdin.setEncoding('utf8');
503
+ process.stdin.on('data', function (param) {
504
+ process.stdin.pause();
505
+ that.actionFromJson('restartProcessId', param, opts, 'pipe', cb);
506
+ });
507
+ }
508
+ else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object')
509
+ that._startJson(cmd, opts, 'restartProcessId', cb);
510
+ else {
511
+ if (opts && opts.env) {
512
+ var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
513
+ Common.err(err);
514
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
515
+ }
516
+ if (opts && !opts.updateEnv)
517
+ Common.printOut(IMMUTABLE_MSG);
518
+ that._operate('restartProcessId', cmd, opts, cb);
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Delete process
524
+ *
525
+ * @param {String} process_name Application Name / Process id / Application file / 'all'
526
+ * @param {Function} cb Callback
527
+ */
528
+ delete (process_name, jsonVia, cb) {
529
+ var that = this;
530
+
531
+ if (typeof(jsonVia) === "function") {
532
+ cb = jsonVia;
533
+ jsonVia = null;
534
+ }
535
+
536
+ if (typeof(process_name) === "number") {
537
+ process_name = process_name.toString();
538
+ }
539
+
540
+ if (jsonVia == 'pipe')
541
+ return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', (err, procs) => {
542
+ return cb ? cb(err, procs) : this.speedList()
543
+ });
544
+ if (Common.isConfigFile(process_name))
545
+ return that.actionFromJson('deleteProcessId', process_name, commander, 'file', (err, procs) => {
546
+ return cb ? cb(err, procs) : this.speedList()
547
+ });
548
+ else {
549
+ that._operate('deleteProcessId', process_name, (err, procs) => {
550
+ return cb ? cb(err, procs) : this.speedList()
551
+ });
552
+ }
553
+ }
554
+
555
+ /**
556
+ * Stop process
557
+ *
558
+ * @param {String} process_name Application Name / Process id / Application file / 'all'
559
+ * @param {Function} cb Callback
560
+ */
561
+ stop (process_name, cb) {
562
+ var that = this;
563
+
564
+ if (typeof(process_name) === 'number')
565
+ process_name = process_name.toString();
566
+
567
+ if (process_name == "-") {
568
+ process.stdin.resume();
569
+ process.stdin.setEncoding('utf8');
570
+ process.stdin.on('data', function (param) {
571
+ process.stdin.pause();
572
+ that.actionFromJson('stopProcessId', param, commander, 'pipe', (err, procs) => {
573
+ return cb ? cb(err, procs) : this.speedList()
574
+ })
575
+ });
576
+ }
577
+ else if (Common.isConfigFile(process_name))
578
+ that.actionFromJson('stopProcessId', process_name, commander, 'file', (err, procs) => {
579
+ return cb ? cb(err, procs) : this.speedList()
580
+ });
581
+ else
582
+ that._operate('stopProcessId', process_name, (err, procs) => {
583
+ return cb ? cb(err, procs) : this.speedList()
584
+ });
585
+ }
586
+
587
+ /**
588
+ * Get list of all processes managed
589
+ *
590
+ * @param {Function} cb Callback
591
+ */
592
+ list (opts, cb) {
593
+ var that = this;
594
+
595
+ if (typeof(opts) == 'function') {
596
+ cb = opts;
597
+ opts = null;
598
+ }
599
+
600
+ that.Client.executeRemote('getMonitorData', {}, function(err, list) {
601
+ if (err) {
602
+ Common.printError(err);
603
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
604
+ }
605
+
606
+ if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {
607
+ var dayjs = require('dayjs');
608
+ function show() {
609
+ process.stdout.write('\x1b[2J');
610
+ process.stdout.write('\x1b[0f');
611
+ console.log('Last refresh: ', dayjs().format());
612
+ that.Client.executeRemote('getMonitorData', {}, function(err, list) {
613
+ UX.list(list, null);
614
+ });
615
+ }
616
+
617
+ show();
618
+ setInterval(show, 900);
619
+ return false;
620
+ }
621
+
622
+ return cb ? cb(null, list) : that.speedList(null);
623
+ });
624
+ }
625
+
626
+ /**
627
+ * Kill Daemon
628
+ *
629
+ * @param {Function} cb Callback
630
+ */
631
+ killDaemon (cb) {
632
+ process.env.ZM2_STATUS = 'stopping'
633
+
634
+ var that = this;
635
+
636
+ that.Client.executeRemote('notifyKillPM2', {}, function() {});
637
+
638
+ that._operate('deleteProcessId', 'all', function(err, list) {
639
+ Common.printOut(conf.PREFIX_MSG + '[v] All Applications Stopped');
640
+ process.env.ZM2_SILENT = 'false';
641
+
642
+ that.killAgent(function(err, data) {
643
+ if (!err) {
644
+ Common.printOut(conf.PREFIX_MSG + '[v] Agent Stopped');
645
+ }
646
+
647
+ that.Client.killDaemon(function(err, res) {
648
+ if (err) Common.printError(err);
649
+ Common.printOut(conf.PREFIX_MSG + '[v] ZM2 Daemon Stopped');
650
+ return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);
651
+ });
652
+
653
+ });
654
+ })
655
+ }
656
+
657
+ kill (cb) {
658
+ this.killDaemon(cb);
659
+ }
660
+
661
+ /////////////////////
662
+ // Private methods //
663
+ /////////////////////
664
+
665
+ /**
666
+ * Method to START / RESTART a script
667
+ *
668
+ * @private
669
+ * @param {string} script script name (will be resolved according to location)
670
+ */
671
+ _startScript (script, opts, cb) {
672
+ if (typeof opts == "function") {
673
+ cb = opts;
674
+ opts = {};
675
+ }
676
+ var that = this;
677
+
678
+ /**
679
+ * Commander.js tricks
680
+ */
681
+ var app_conf = Config.filterOptions(opts);
682
+ var appConf = {};
683
+
684
+ if (typeof app_conf.name == 'function')
685
+ delete app_conf.name;
686
+
687
+ delete app_conf.args;
688
+
689
+ // Retrieve arguments via -- <args>
690
+ var argsIndex;
691
+
692
+ if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0)
693
+ app_conf.args = opts.rawArgs.slice(argsIndex + 1);
694
+ else if (opts.scriptArgs)
695
+ app_conf.args = opts.scriptArgs;
696
+
697
+ app_conf.script = script;
698
+ if(!app_conf.namespace)
699
+ app_conf.namespace = 'default';
700
+
701
+ if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) {
702
+ Common.err(appConf)
703
+ return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);
704
+ }
705
+
706
+ app_conf = appConf[0];
707
+
708
+ if (opts.watchDelay) {
709
+ if (typeof opts.watchDelay === "string" && opts.watchDelay.indexOf("ms") !== -1)
710
+ app_conf.watch_delay = parseInt(opts.watchDelay);
711
+ else {
712
+ app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000;
713
+ }
714
+ }
715
+
716
+ var mas = [];
717
+ if(typeof opts.ext != 'undefined')
718
+ hf.make_available_extension(opts, mas); // for -e flag
719
+ mas.length > 0 ? app_conf.ignore_watch = mas : 0;
720
+
721
+ /**
722
+ * If -w option, write configuration to configuration.json file
723
+ */
724
+ if (app_conf.write) {
725
+ var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-zm2.json');
726
+ Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path));
727
+ // pretty JSON
728
+ try {
729
+ fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2));
730
+ } catch (e) {
731
+ console.error(e.stack || e);
732
+ }
733
+ }
734
+
735
+ series([
736
+ restartExistingProcessName,
737
+ restartExistingNameSpace,
738
+ restartExistingProcessId,
739
+ restartExistingProcessPathOrStartNew
740
+ ], function(err, data) {
741
+ if (err instanceof Error)
742
+ return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
743
+
744
+ var ret = {};
745
+
746
+ data.forEach(function(_dt) {
747
+ if (_dt !== undefined)
748
+ ret = _dt;
749
+ });
750
+
751
+ return cb ? cb(null, ret) : that.speedList();
752
+ });
753
+
754
+ /**
755
+ * If start <app_name> start/restart application
756
+ */
757
+ function restartExistingProcessName(cb) {
758
+ if (!isNaN(script) ||
759
+ (typeof script === 'string' && script.indexOf('/') != -1) ||
760
+ (typeof script === 'string' && path.extname(script) !== ''))
761
+ return cb(null);
762
+
763
+ that.Client.getProcessIdByName(script, function(err, ids) {
764
+ if (err && cb) return cb(err);
765
+ if (ids.length > 0) {
766
+ that._operate('restartProcessId', script, opts, function(err, list) {
767
+ if (err) return cb(err);
768
+ Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
769
+ return cb(true, list);
770
+ });
771
+ }
772
+ else return cb(null);
773
+ });
774
+ }
775
+
776
+ /**
777
+ * If start <namespace> start/restart namespace
778
+ */
779
+ function restartExistingNameSpace(cb) {
780
+ if (!isNaN(script) ||
781
+ (typeof script === 'string' && script.indexOf('/') != -1) ||
782
+ (typeof script === 'string' && path.extname(script) !== ''))
783
+ return cb(null);
784
+
785
+ if (script !== 'all') {
786
+ that.Client.getProcessIdsByNamespace(script, function (err, ids) {
787
+ if (err && cb) return cb(err);
788
+ if (ids.length > 0) {
789
+ that._operate('restartProcessId', script, opts, function (err, list) {
790
+ if (err) return cb(err);
791
+ Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
792
+ return cb(true, list);
793
+ });
794
+ }
795
+ else return cb(null);
796
+ });
797
+ }
798
+ else {
799
+ that._operate('restartProcessId', 'all', function(err, list) {
800
+ if (err) return cb(err);
801
+ Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
802
+ return cb(true, list);
803
+ });
804
+ }
805
+ }
806
+
807
+ function restartExistingProcessId(cb) {
808
+ if (isNaN(script)) return cb(null);
809
+
810
+ that._operate('restartProcessId', script, opts, function(err, list) {
811
+ if (err) return cb(err);
812
+ Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
813
+ return cb(true, list);
814
+ });
815
+ }
816
+
817
+ /**
818
+ * Restart a process with the same full path
819
+ * Or start it
820
+ */
821
+ function restartExistingProcessPathOrStartNew(cb) {
822
+ that.Client.executeRemote('getMonitorData', {}, function(err, procs) {
823
+ if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);
824
+
825
+ var full_path = path.resolve(that.cwd, script);
826
+ var managed_script = null;
827
+
828
+ procs.forEach(function(proc) {
829
+ if (proc.pm2_env.pm_exec_path == full_path &&
830
+ proc.pm2_env.name == app_conf.name)
831
+ managed_script = proc;
832
+ });
833
+
834
+ if (managed_script &&
835
+ (managed_script.pm2_env.status == conf.STOPPED_STATUS ||
836
+ managed_script.pm2_env.status == conf.STOPPING_STATUS ||
837
+ managed_script.pm2_env.status == conf.ERRORED_STATUS)) {
838
+ // Restart process if stopped
839
+ var app_name = managed_script.pm2_env.name;
840
+
841
+ that._operate('restartProcessId', app_name, opts, function(err, list) {
842
+ if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);
843
+ Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
844
+ return cb(true, list);
845
+ });
846
+ return false;
847
+ }
848
+ else if (managed_script && !opts.force) {
849
+ Common.err('Script already launched, add -f option to force re-execution');
850
+ return cb(new Error('Script already launched'));
851
+ }
852
+
853
+ var resolved_paths = null;
854
+
855
+ try {
856
+ resolved_paths = Common.resolveAppAttributes({
857
+ cwd : that.cwd,
858
+ pm2_home : that.pm2_home
859
+ }, app_conf);
860
+ } catch(e) {
861
+ Common.err(e.message);
862
+ return cb(Common.retErr(e));
863
+ }
864
+
865
+ Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')',
866
+ resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances);
867
+
868
+ if (!resolved_paths.env) resolved_paths.env = {};
869
+
870
+ // Set ZM2 HOME in case of child process using ZM2 API
871
+ resolved_paths.env['ZM2_HOME'] = that.pm2_home;
872
+ resolved_paths.env['PM2_HOME'] = that.pm2_home;
873
+
874
+ var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
875
+ Object.assign(resolved_paths.env, additional_env);
876
+
877
+ // Is KM linked?
878
+ resolved_paths.km_link = that.gl_is_km_linked;
879
+
880
+ that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
881
+ if (err) {
882
+ Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);
883
+ return cb(Common.retErr(err));
884
+ }
885
+
886
+ Common.printOut(conf.PREFIX_MSG + 'Done.');
887
+ return cb(true, data);
888
+ });
889
+ return false;
890
+ });
891
+ }
892
+ }
893
+
894
+ /**
895
+ * Method to start/restart/reload processes from a JSON file
896
+ * It will start app not started
897
+ * Can receive only option to skip applications
898
+ *
899
+ * @private
900
+ */
901
+ _startJson (file, opts, action, pipe, cb) {
902
+ var config = {};
903
+ var appConf = {};
904
+ var staticConf = [];
905
+ var deployConf = {};
906
+ var apps_info = [];
907
+ var that = this;
908
+
909
+ /**
910
+ * Get File configuration
911
+ */
912
+ if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {
913
+ cb = pipe;
914
+ }
915
+ if (typeof(file) === 'object') {
916
+ config = file;
917
+ } else if (pipe === 'pipe') {
918
+ config = Common.parseConfig(file, 'pipe');
919
+ } else {
920
+ var data = null;
921
+
922
+ var isAbsolute = path.isAbsolute(file)
923
+ var file_path = isAbsolute ? file : path.join(that.cwd, file);
924
+
925
+ debug('Resolved filepath %s', file_path);
926
+
927
+ try {
928
+ data = fs.readFileSync(file_path);
929
+ } catch(e) {
930
+ Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
931
+ return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
932
+ }
933
+
934
+ try {
935
+ config = Common.parseConfig(data, file);
936
+ } catch(e) {
937
+ Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
938
+ console.error(e);
939
+ return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
940
+ }
941
+ }
942
+
943
+ /**
944
+ * Alias some optional fields
945
+ */
946
+ if (config.deploy)
947
+ deployConf = config.deploy;
948
+ if (config.static)
949
+ staticConf = config.static;
950
+ if (config.apps)
951
+ appConf = config.apps;
952
+ else if (config.pm2)
953
+ appConf = config.pm2;
954
+ else
955
+ appConf = config;
956
+ if (!Array.isArray(appConf))
957
+ appConf = [appConf];
958
+
959
+ if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
960
+ return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
961
+
962
+ process.env.ZM2_JSON_PROCESSING = true;
963
+
964
+ // Get App list
965
+ var apps_name = [];
966
+ var proc_list = {};
967
+
968
+ // Add statics to apps
969
+ staticConf.forEach(function(serve) {
970
+ appConf.push({
971
+ name: serve.name ? serve.name : `static-page-server-${serve.port}`,
972
+ script: path.resolve(__dirname, 'API', 'Serve.js'),
973
+ env: {
974
+ ZM2_SERVE_PORT: serve.port,
975
+ ZM2_SERVE_HOST: serve.host,
976
+ ZM2_SERVE_PATH: serve.path,
977
+ ZM2_SERVE_SPA: serve.spa,
978
+ ZM2_SERVE_DIRECTORY: serve.directory,
979
+ ZM2_SERVE_BASIC_AUTH: serve.basic_auth !== undefined,
980
+ ZM2_SERVE_BASIC_AUTH_USERNAME: serve.basic_auth ? serve.basic_auth.username : null,
981
+ ZM2_SERVE_BASIC_AUTH_PASSWORD: serve.basic_auth ? serve.basic_auth.password : null,
982
+ ZM2_SERVE_MONITOR: serve.monitor
983
+ }
984
+ });
985
+ });
986
+
987
+ // Here we pick only the field we want from the CLI when starting a JSON
988
+ appConf.forEach(function(app) {
989
+ if (!app.env) { app.env = {}; }
990
+ app.env.io = app.io;
991
+ // --only <app>
992
+ if (opts.only) {
993
+ var apps = opts.only.split(/,| /)
994
+ if (apps.indexOf(app.name) == -1)
995
+ return false
996
+ }
997
+ // Namespace
998
+ if (!app.namespace) {
999
+ if (opts.namespace)
1000
+ app.namespace = opts.namespace;
1001
+ else
1002
+ app.namespace = 'default';
1003
+ }
1004
+ // --watch
1005
+ if (!app.watch && opts.watch && opts.watch === true)
1006
+ app.watch = true;
1007
+ // --ignore-watch
1008
+ if (!app.ignore_watch && opts.ignore_watch)
1009
+ app.ignore_watch = opts.ignore_watch;
1010
+ if (opts.install_url)
1011
+ app.install_url = opts.install_url;
1012
+ // --instances <nb>
1013
+ if (opts.instances && typeof(opts.instances) === 'number')
1014
+ app.instances = opts.instances;
1015
+ // --uid <user>
1016
+ if (opts.uid)
1017
+ app.uid = opts.uid;
1018
+ // --gid <user>
1019
+ if (opts.gid)
1020
+ app.gid = opts.gid;
1021
+ // Specific
1022
+ if (app.append_env_to_name && opts.env)
1023
+ app.name += ('-' + opts.env);
1024
+ if (opts.name_prefix && app.name.indexOf(opts.name_prefix) == -1)
1025
+ app.name = `${opts.name_prefix}:${app.name}`
1026
+
1027
+ app.username = Common.getCurrentUsername();
1028
+ apps_name.push(app.name);
1029
+ });
1030
+
1031
+ that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) {
1032
+ if (err) {
1033
+ Common.printError(err);
1034
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1035
+ }
1036
+
1037
+ /**
1038
+ * Uniquify in memory process list
1039
+ */
1040
+ raw_proc_list.forEach(function(proc) {
1041
+ proc_list[proc.name] = proc;
1042
+ });
1043
+
1044
+ /**
1045
+ * Auto detect application already started
1046
+ * and act on them depending on action
1047
+ */
1048
+ eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) {
1049
+ // Skip app name (--only option)
1050
+ if (apps_name.indexOf(proc_name) == -1)
1051
+ return next();
1052
+
1053
+ if (!(action == 'reloadProcessId' ||
1054
+ action == 'softReloadProcessId' ||
1055
+ action == 'restartProcessId'))
1056
+ throw new Error('Wrong action called');
1057
+
1058
+ var apps = appConf.filter(function(app) {
1059
+ return app.name == proc_name;
1060
+ });
1061
+
1062
+ var envs = apps.map(function(app){
1063
+ // Binds env_diff to env and returns it.
1064
+ return Common.mergeEnvironmentVariables(app, opts.env, deployConf);
1065
+ });
1066
+
1067
+ // Assigns own enumerable properties of all
1068
+ // Notice: if people use the same name in different apps,
1069
+ // duplicated envs will be overrode by the last one
1070
+ var env = envs.reduce(function(e1, e2){
1071
+ return Object.assign(e1, e2);
1072
+ });
1073
+
1074
+ // When we are processing JSON, allow to keep the new env by default
1075
+ env.updateEnv = true;
1076
+
1077
+ // Pass `env` option
1078
+ that._operate(action, proc_name, env, function(err, ret) {
1079
+ if (err) Common.printError(err);
1080
+
1081
+ // For return
1082
+ apps_info = apps_info.concat(ret);
1083
+
1084
+ that.Client.notifyGod(action, proc_name);
1085
+ // And Remove from array to spy
1086
+ apps_name.splice(apps_name.indexOf(proc_name), 1);
1087
+ return next();
1088
+ });
1089
+
1090
+ }, function(err) {
1091
+ if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1092
+ if (apps_name.length > 0 && action != 'start')
1093
+ Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', '));
1094
+ // Start missing apps
1095
+ return startApps(apps_name, function(err, apps) {
1096
+ apps_info = apps_info.concat(apps);
1097
+ return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0);
1098
+ });
1099
+ });
1100
+ return false;
1101
+ });
1102
+
1103
+ function startApps(app_name_to_start, cb) {
1104
+ var apps_to_start = [];
1105
+ var apps_started = [];
1106
+ var apps_errored = [];
1107
+
1108
+ appConf.forEach(function(app, i) {
1109
+ if (app_name_to_start.indexOf(app.name) != -1) {
1110
+ apps_to_start.push(appConf[i]);
1111
+ }
1112
+ });
1113
+
1114
+ eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) {
1115
+ if (opts.cwd)
1116
+ app.cwd = opts.cwd;
1117
+ if (opts.force_name)
1118
+ app.name = opts.force_name;
1119
+ if (opts.started_as_module)
1120
+ app.pmx_module = true;
1121
+
1122
+ var resolved_paths = null;
1123
+
1124
+ // hardcode script name to use `serve` feature inside a process file
1125
+ if (app.script === 'serve') {
1126
+ app.script = path.resolve(__dirname, 'API', 'Serve.js')
1127
+ }
1128
+
1129
+ try {
1130
+ resolved_paths = Common.resolveAppAttributes({
1131
+ cwd : that.cwd,
1132
+ pm2_home : that.pm2_home
1133
+ }, app);
1134
+ } catch (e) {
1135
+ apps_errored.push(e)
1136
+ Common.err(`Error: ${e.message}`)
1137
+ return next();
1138
+ }
1139
+
1140
+ if (!resolved_paths.env) resolved_paths.env = {};
1141
+
1142
+ // Set ZM2 HOME in case of child process using ZM2 API
1143
+ resolved_paths.env['ZM2_HOME'] = that.pm2_home;
1144
+ resolved_paths.env['PM2_HOME'] = that.pm2_home;
1145
+
1146
+ var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
1147
+ Object.assign(resolved_paths.env, additional_env);
1148
+
1149
+ resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);
1150
+
1151
+ delete resolved_paths.env.current_conf;
1152
+
1153
+ // Is KM linked?
1154
+ resolved_paths.km_link = that.gl_is_km_linked;
1155
+
1156
+ if (resolved_paths.wait_ready) {
1157
+ Common.warn(`App ${resolved_paths.name} has option 'wait_ready' set, waiting for app to be ready...`)
1158
+ }
1159
+ that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
1160
+ if (err) {
1161
+ Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err);
1162
+ return next();
1163
+ }
1164
+ if (data.length === 0) {
1165
+ Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data);
1166
+ return next();
1167
+ }
1168
+
1169
+ Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);
1170
+ apps_started = apps_started.concat(data);
1171
+ next();
1172
+ });
1173
+
1174
+ }, function(err) {
1175
+ var final_error = err || apps_errored.length > 0 ? apps_errored : null
1176
+ return cb ? cb(final_error, apps_started) : that.speedList();
1177
+ });
1178
+ return false;
1179
+ }
1180
+ }
1181
+
1182
+ /**
1183
+ * Apply a RPC method on the json file
1184
+ * @private
1185
+ * @method actionFromJson
1186
+ * @param {string} action RPC Method
1187
+ * @param {object} options
1188
+ * @param {string|object} file file
1189
+ * @param {string} jsonVia action type (=only 'pipe' ?)
1190
+ * @param {Function}
1191
+ */
1192
+ actionFromJson (action, file, opts, jsonVia, cb) {
1193
+ var appConf = {};
1194
+ var ret_processes = [];
1195
+ var that = this;
1196
+
1197
+ //accept programmatic calls
1198
+ if (typeof file == 'object') {
1199
+ cb = typeof jsonVia == 'function' ? jsonVia : cb;
1200
+ appConf = file;
1201
+ }
1202
+ else if (jsonVia == 'file') {
1203
+ var data = null;
1204
+
1205
+ try {
1206
+ data = fs.readFileSync(file);
1207
+ } catch(e) {
1208
+ Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
1209
+ return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
1210
+ }
1211
+
1212
+ try {
1213
+ appConf = Common.parseConfig(data, file);
1214
+ } catch(e) {
1215
+ Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
1216
+ console.error(e);
1217
+ return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
1218
+ }
1219
+ } else if (jsonVia == 'pipe') {
1220
+ appConf = Common.parseConfig(file, 'pipe');
1221
+ } else {
1222
+ Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe');
1223
+ return that.exitCli(conf.ERROR_EXIT);
1224
+ }
1225
+
1226
+ // Backward compatibility
1227
+ if (appConf.apps)
1228
+ appConf = appConf.apps;
1229
+
1230
+ if (!Array.isArray(appConf))
1231
+ appConf = [appConf];
1232
+
1233
+ if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
1234
+ return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
1235
+
1236
+ eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {
1237
+ var name = '';
1238
+ var new_env;
1239
+
1240
+ if (!proc.name)
1241
+ name = path.basename(proc.script);
1242
+ else
1243
+ name = proc.name;
1244
+
1245
+ if (opts.only && opts.only != name)
1246
+ return process.nextTick(next1);
1247
+
1248
+ if (opts && opts.env)
1249
+ new_env = Common.mergeEnvironmentVariables(proc, opts.env);
1250
+ else
1251
+ new_env = Common.mergeEnvironmentVariables(proc);
1252
+
1253
+ that.Client.getProcessIdByName(name, function(err, ids) {
1254
+ if (err) {
1255
+ Common.printError(err);
1256
+ return next1();
1257
+ }
1258
+ if (!ids) return next1();
1259
+
1260
+ eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) {
1261
+ var opts = {};
1262
+
1263
+ //stopProcessId could accept options to?
1264
+ if (action == 'restartProcessId') {
1265
+ opts = {id : id, env : new_env};
1266
+ } else {
1267
+ opts = id;
1268
+ }
1269
+
1270
+ that.Client.executeRemote(action, opts, function(err, res) {
1271
+ ret_processes.push(res);
1272
+ if (err) {
1273
+ Common.printError(err);
1274
+ return next2();
1275
+ }
1276
+
1277
+ if (action == 'restartProcessId') {
1278
+ that.Client.notifyGod('restart', id);
1279
+ } else if (action == 'deleteProcessId') {
1280
+ that.Client.notifyGod('delete', id);
1281
+ } else if (action == 'stopProcessId') {
1282
+ that.Client.notifyGod('stop', id);
1283
+ }
1284
+
1285
+ Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id);
1286
+ return next2();
1287
+ });
1288
+ }, function(err) {
1289
+ return next1(null, ret_processes);
1290
+ });
1291
+ });
1292
+ }, function(err) {
1293
+ if (cb) return cb(null, ret_processes);
1294
+ else return that.speedList();
1295
+ });
1296
+ }
1297
+
1298
+
1299
+ /**
1300
+ * Main function to operate with ZM2 daemon
1301
+ *
1302
+ * @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId)
1303
+ * @param {String} process_name can be 'all', a id integer or process name
1304
+ * @param {Object} envs object with CLI options / environment
1305
+ */
1306
+ _operate (action_name, process_name, envs, cb) {
1307
+ var that = this;
1308
+ var update_env = false;
1309
+ var ret = [];
1310
+
1311
+ // Make sure all options exist
1312
+ if (!envs)
1313
+ envs = {};
1314
+
1315
+ if (typeof(envs) == 'function'){
1316
+ cb = envs;
1317
+ envs = {};
1318
+ }
1319
+
1320
+ // Set via env.update (JSON processing)
1321
+ if (envs.updateEnv === true)
1322
+ update_env = true;
1323
+
1324
+ var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;
1325
+
1326
+ if (!(process.env.ZM2_JSON_PROCESSING || process.env.PM2_JSON_PROCESSING) || envs.commands) {
1327
+ envs = that._handleAttributeUpdate(envs);
1328
+ }
1329
+
1330
+ /**
1331
+ * Set current updated configuration if not passed
1332
+ */
1333
+ if (!envs.current_conf) {
1334
+ var _conf = fclone(envs);
1335
+ envs = {
1336
+ current_conf : _conf
1337
+ }
1338
+
1339
+ // Is KM linked?
1340
+ envs.current_conf.km_link = that.gl_is_km_linked;
1341
+ }
1342
+
1343
+ /**
1344
+ * Operate action on specific process id
1345
+ */
1346
+ function processIds(ids, cb) {
1347
+ Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids);
1348
+
1349
+ if (ids.length <= 2)
1350
+ concurrent_actions = 1;
1351
+
1352
+ if (action_name == 'deleteProcessId')
1353
+ concurrent_actions = 10;
1354
+
1355
+ eachLimit(ids, concurrent_actions, function(id, next) {
1356
+ var opts;
1357
+
1358
+ // These functions need extra param to be passed
1359
+ if (action_name == 'restartProcessId' ||
1360
+ action_name == 'reloadProcessId' ||
1361
+ action_name == 'softReloadProcessId') {
1362
+ var new_env = {};
1363
+
1364
+ if (update_env === true) {
1365
+ if (conf.PM2_PROGRAMMATIC == true)
1366
+ new_env = Common.safeExtend({}, process.env);
1367
+ else
1368
+ new_env = Object.assign({}, process.env);
1369
+
1370
+ Object.keys(envs).forEach(function(k) {
1371
+ new_env[k] = envs[k];
1372
+ });
1373
+ }
1374
+ else {
1375
+ new_env = envs;
1376
+ }
1377
+
1378
+ opts = {
1379
+ id : id,
1380
+ env : new_env
1381
+ };
1382
+ }
1383
+ else {
1384
+ opts = id;
1385
+ }
1386
+
1387
+ that.Client.executeRemote(action_name, opts, function(err, res) {
1388
+ if (err) {
1389
+ Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id);
1390
+ return next(`Process ${id} not found`);
1391
+ }
1392
+
1393
+ if (action_name == 'restartProcessId') {
1394
+ that.Client.notifyGod('restart', id);
1395
+ } else if (action_name == 'deleteProcessId') {
1396
+ that.Client.notifyGod('delete', id);
1397
+ } else if (action_name == 'stopProcessId') {
1398
+ that.Client.notifyGod('stop', id);
1399
+ } else if (action_name == 'reloadProcessId') {
1400
+ that.Client.notifyGod('reload', id);
1401
+ } else if (action_name == 'softReloadProcessId') {
1402
+ that.Client.notifyGod('graceful reload', id);
1403
+ }
1404
+
1405
+ if (!Array.isArray(res))
1406
+ res = [res];
1407
+
1408
+ // Filter return
1409
+ res.forEach(function(proc) {
1410
+ Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id);
1411
+
1412
+ if (action_name == 'stopProcessId' && proc.pm2_env && proc.pm2_env.cron_restart) {
1413
+ Common.warn(`App ${chalk.bold(proc.pm2_env.name)} stopped but CRON RESTART is still UP ${proc.pm2_env.cron_restart}`)
1414
+ }
1415
+
1416
+ if (!proc.pm2_env) return false;
1417
+
1418
+ ret.push({
1419
+ name : proc.pm2_env.name,
1420
+ namespace: proc.pm2_env.namespace,
1421
+ pm_id : proc.pm2_env.pm_id,
1422
+ status : proc.pm2_env.status,
1423
+ restart_time : proc.pm2_env.restart_time,
1424
+ pm2_env : {
1425
+ name : proc.pm2_env.name,
1426
+ namespace: proc.pm2_env.namespace,
1427
+ pm_id : proc.pm2_env.pm_id,
1428
+ status : proc.pm2_env.status,
1429
+ restart_time : proc.pm2_env.restart_time,
1430
+ env : proc.pm2_env.env
1431
+ }
1432
+ });
1433
+ });
1434
+
1435
+ return next();
1436
+ });
1437
+ }, function(err) {
1438
+ if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1439
+ return cb ? cb(null, ret) : that.speedList();
1440
+ });
1441
+ }
1442
+
1443
+ if (process_name == 'all') {
1444
+ // When using shortcuts like 'all', do not delete modules
1445
+ var fn
1446
+
1447
+ if (process.env.ZM2_STATUS == 'stopping' || process.env.PM2_STATUS == 'stopping')
1448
+ that.Client.getAllProcessId(function(err, ids) {
1449
+ reoperate(err, ids)
1450
+ });
1451
+ else
1452
+ that.Client.getAllProcessIdWithoutModules(function(err, ids) {
1453
+ reoperate(err, ids)
1454
+ });
1455
+
1456
+ function reoperate(err, ids) {
1457
+ if (err) {
1458
+ Common.printError(err);
1459
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1460
+ }
1461
+ if (!ids || ids.length === 0) {
1462
+ Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
1463
+ return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
1464
+ }
1465
+ return processIds(ids, cb);
1466
+ }
1467
+ }
1468
+ // operate using regex
1469
+ else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') {
1470
+ var regex = new RegExp(process_name.replace(/\//g, ''));
1471
+
1472
+ that.Client.executeRemote('getMonitorData', {}, function(err, list) {
1473
+ if (err) {
1474
+ Common.printError('Error retrieving process list: ' + err);
1475
+ return cb(err);
1476
+ }
1477
+ var found_proc = [];
1478
+ list.forEach(function(proc) {
1479
+ if (regex.test(proc.pm2_env.name)) {
1480
+ found_proc.push(proc.pm_id);
1481
+ }
1482
+ });
1483
+
1484
+ if (found_proc.length === 0) {
1485
+ Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
1486
+ return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
1487
+ }
1488
+
1489
+ return processIds(found_proc, cb);
1490
+ });
1491
+ }
1492
+ else if (isNaN(process_name)) {
1493
+ /**
1494
+ * We can not stop or delete a module but we can restart it
1495
+ * to refresh configuration variable
1496
+ */
1497
+ var allow_module_restart = action_name == 'restartProcessId' ? true : false;
1498
+
1499
+ that.Client.getProcessIdByName(process_name, allow_module_restart, function (err, ids) {
1500
+ if (err) {
1501
+ Common.printError(err);
1502
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1503
+ }
1504
+ if (ids && ids.length > 0) {
1505
+ /**
1506
+ * Determine if the process to restart is a module
1507
+ * if yes load configuration variables and merge with the current environment
1508
+ */
1509
+ var additional_env = Modularizer.getAdditionalConf(process_name);
1510
+ Object.assign(envs, additional_env);
1511
+ return processIds(ids, cb);
1512
+ }
1513
+
1514
+ that.Client.getProcessIdsByNamespace(process_name, allow_module_restart, function (err, ns_process_ids) {
1515
+ if (err) {
1516
+ Common.printError(err);
1517
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1518
+ }
1519
+ if (!ns_process_ids || ns_process_ids.length === 0) {
1520
+ Common.printError(conf.PREFIX_MSG_ERR + 'Process or Namespace %s not found', process_name);
1521
+ return cb ? cb(new Error('process or namespace not found')) : that.exitCli(conf.ERROR_EXIT);
1522
+ }
1523
+
1524
+ /**
1525
+ * Determine if the process to restart is a module
1526
+ * if yes load configuration variables and merge with the current environment
1527
+ */
1528
+ var ns_additional_env = Modularizer.getAdditionalConf(process_name);
1529
+ Object.assign(envs, ns_additional_env);
1530
+ return processIds(ns_process_ids, cb);
1531
+ });
1532
+ });
1533
+ } else {
1534
+ if (that.pm2_configuration.docker == "true" ||
1535
+ that.pm2_configuration.docker == true) {
1536
+ // Docker/Systemd process interaction detection
1537
+ that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
1538
+ var higher_id = 0
1539
+ proc_list.forEach(p => { p.pm_id > higher_id ? higher_id = p.pm_id : null })
1540
+
1541
+ // Is Docker/Systemd
1542
+ if (process_name > higher_id)
1543
+ return DockerMgmt.processCommand(that, higher_id, process_name, action_name, (err) => {
1544
+ if (err) {
1545
+ Common.printError(conf.PREFIX_MSG_ERR + (err.message ? err.message : err));
1546
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1547
+ }
1548
+
1549
+ return cb ? cb(null, ret) : that.speedList();
1550
+ })
1551
+
1552
+ // Check if application name as number is an app name
1553
+ that.Client.getProcessIdByName(process_name, function(err, ids) {
1554
+ if (ids.length > 0)
1555
+ return processIds(ids, cb);
1556
+
1557
+ // Check if application name as number is an namespace
1558
+ that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
1559
+ if (ns_process_ids.length > 0)
1560
+ return processIds(ns_process_ids, cb);
1561
+ // Else operate on pm id
1562
+ return processIds([process_name], cb);
1563
+ });
1564
+ });
1565
+ })
1566
+ }
1567
+ else {
1568
+ // Check if application name as number is an app name
1569
+ that.Client.getProcessIdByName(process_name, function(err, ids) {
1570
+ if (ids.length > 0)
1571
+ return processIds(ids, cb);
1572
+
1573
+ // Check if application name as number is an namespace
1574
+ that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
1575
+ if (ns_process_ids.length > 0)
1576
+ return processIds(ns_process_ids, cb);
1577
+ // Else operate on pm id
1578
+ return processIds([process_name], cb);
1579
+ });
1580
+ });
1581
+ }
1582
+ }
1583
+ }
1584
+
1585
+ /**
1586
+ * Converts CamelCase Commander.js arguments
1587
+ * to Underscore
1588
+ * (nodeArgs -> node_args)
1589
+ */
1590
+ _handleAttributeUpdate (opts) {
1591
+ var conf = Config.filterOptions(opts);
1592
+ var that = this;
1593
+
1594
+ if (typeof(conf.name) != 'string')
1595
+ delete conf.name;
1596
+
1597
+ var argsIndex = 0;
1598
+ if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {
1599
+ conf.args = opts.rawArgs.slice(argsIndex + 1);
1600
+ }
1601
+
1602
+ var appConf = Common.verifyConfs(conf)[0];
1603
+
1604
+ if (appConf instanceof Error) {
1605
+ Common.printError('Error while transforming CamelCase args to underscore');
1606
+ return appConf;
1607
+ }
1608
+
1609
+ if (argsIndex == -1)
1610
+ delete appConf.args;
1611
+ if (appConf.name == 'undefined')
1612
+ delete appConf.name;
1613
+
1614
+ delete appConf.exec_mode;
1615
+
1616
+ if (Array.isArray(appConf.watch) && appConf.watch.length === 0) {
1617
+ if (!~opts.rawArgs.indexOf('--watch'))
1618
+ delete appConf.watch
1619
+ }
1620
+
1621
+ // Options set via environment variables
1622
+ if (process.env.ZM2_DEEP_MONITORING || process.env.PM2_DEEP_MONITORING)
1623
+ appConf.deep_monitoring = true;
1624
+
1625
+ // Force deletion of defaults values set by commander
1626
+ // to avoid overriding specified configuration by user
1627
+ if (appConf.treekill === true)
1628
+ delete appConf.treekill;
1629
+ if (appConf.pmx === true)
1630
+ delete appConf.pmx;
1631
+ if (appConf.vizion === true)
1632
+ delete appConf.vizion;
1633
+ if (appConf.automation === true)
1634
+ delete appConf.automation;
1635
+ if (appConf.autostart === true)
1636
+ delete appConf.autostart;
1637
+ if (appConf.autorestart === true)
1638
+ delete appConf.autorestart;
1639
+
1640
+ return appConf;
1641
+ }
1642
+
1643
+ getProcessIdByName (name, cb) {
1644
+ var that = this;
1645
+
1646
+ this.Client.getProcessIdByName(name, function(err, id) {
1647
+ if (err) {
1648
+ Common.printError(err);
1649
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1650
+ }
1651
+ console.log(id);
1652
+ return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);
1653
+ });
1654
+ }
1655
+
1656
+ /**
1657
+ * Description
1658
+ * @method jlist
1659
+ * @param {} debug
1660
+ * @return
1661
+ */
1662
+ jlist (debug) {
1663
+ var that = this;
1664
+
1665
+ that.Client.executeRemote('getMonitorData', {}, function(err, list) {
1666
+ if (err) {
1667
+ Common.printError(err);
1668
+ return that.exitCli(conf.ERROR_EXIT);
1669
+ }
1670
+
1671
+ if (debug) {
1672
+ process.stdout.write(util.inspect(list, false, null, false));
1673
+ }
1674
+ else {
1675
+ process.stdout.write(JSON.stringify(list));
1676
+ }
1677
+
1678
+ that.exitCli(conf.SUCCESS_EXIT);
1679
+
1680
+ });
1681
+ }
1682
+
1683
+ /**
1684
+ * Display system information
1685
+ * @method slist
1686
+ * @return
1687
+ */
1688
+ slist (tree) {
1689
+ this.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
1690
+ if (err) {
1691
+ Common.err(err)
1692
+ return this.exitCli(conf.ERROR_EXIT)
1693
+ }
1694
+
1695
+ if (tree === true) {
1696
+ var treeify = require('./tools/treeify.js')
1697
+ console.log(treeify.asTree(sys_infos, true))
1698
+ }
1699
+ else
1700
+ process.stdout.write(util.inspect(sys_infos, false, null, false))
1701
+ this.exitCli(conf.SUCCESS_EXIT)
1702
+ })
1703
+ }
1704
+
1705
+ /**
1706
+ * Description
1707
+ * @method speedList
1708
+ * @return
1709
+ */
1710
+ speedList (code, apps_acted) {
1711
+ var that = this;
1712
+ var systemdata = null
1713
+ var acted = []
1714
+
1715
+ if ((code != 0 && code != null)) {
1716
+ return that.exitCli(code ? code : conf.SUCCESS_EXIT);
1717
+ }
1718
+
1719
+ if (apps_acted && apps_acted.length > 0) {
1720
+ apps_acted.forEach(proc => {
1721
+ acted.push(proc.pm2_env ? proc.pm2_env.pm_id : proc.pm_id)
1722
+ })
1723
+ }
1724
+
1725
+ // Do nothing if ZM2 called programmatically and not called from CLI (also in exitCli)
1726
+ if ((conf.PM2_PROGRAMMATIC && (process.env.ZM2_USAGE || process.env.PM2_USAGE) != 'CLI'))
1727
+ return false;
1728
+
1729
+ return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
1730
+ doList(err, proc_list)
1731
+ })
1732
+
1733
+ function doList(err, list) {
1734
+ if (err) {
1735
+ if (that.gl_retry == 0) {
1736
+ that.gl_retry += 1;
1737
+ return setTimeout(that.speedList.bind(that), 1400);
1738
+ }
1739
+ console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err);
1740
+ return that.exitCli(conf.ERROR_EXIT);
1741
+ }
1742
+ if (process.stdout.isTTY === false) {
1743
+ UX.list_min(list);
1744
+ }
1745
+ else if (commander.miniList && !commander.silent)
1746
+ UX.list_min(list);
1747
+ else if (!commander.silent) {
1748
+ if (that.gl_interact_infos) {
1749
+ var dashboard_url = `https://app.pm2.io/#/r/${that.gl_interact_infos.public_key}`
1750
+
1751
+ if (that.gl_interact_infos.info_node != 'https://root.keymetrics.io') {
1752
+ dashboard_url = `${that.gl_interact_infos.info_node}/#/r/${that.gl_interact_infos.public_key}`
1753
+ }
1754
+
1755
+ Common.printOut('%s ZM2+ activated | Instance Name: %s | Dash: %s',
1756
+ chalk.green.bold('⇆'),
1757
+ chalk.bold(that.gl_interact_infos.machine_name),
1758
+ chalk.bold(dashboard_url))
1759
+ }
1760
+ UX.list(list, commander);
1761
+ //Common.printOut(chalk.white.italic(' Use `zm2 show <id|name>` to get more details about an app'));
1762
+ }
1763
+
1764
+ if (that.Client.daemon_mode == false) {
1765
+ Common.printOut('[--no-daemon] Continue to stream logs');
1766
+ Common.printOut('[--no-daemon] Exit on target ZM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString());
1767
+ global._auto_exit = true;
1768
+ return that.streamLogs('all', 0, false, 'HH:mm:ss', false);
1769
+ }
1770
+ // if (process.stdout.isTTY) if looking for start logs
1771
+ else if (!process.env.TRAVIS && process.env.NODE_ENV != 'test' && acted.length > 0 && (commander.attach === true)) {
1772
+ Common.info(`Log streaming apps id: ${chalk.cyan(acted.join(' '))}, exit with Ctrl-C or will exit in 10secs`)
1773
+
1774
+ // setTimeout(() => {
1775
+ // Common.info(`Log streaming exited automatically, run 'zm2 logs' to continue watching logs`)
1776
+ // return that.exitCli(code ? code : conf.SUCCESS_EXIT);
1777
+ // }, 10000)
1778
+
1779
+ return acted.forEach((proc_name) => {
1780
+ that.streamLogs(proc_name, 0, false, null, false);
1781
+ })
1782
+ }
1783
+ else {
1784
+ return that.exitCli(code ? code : conf.SUCCESS_EXIT);
1785
+ }
1786
+ }
1787
+ }
1788
+
1789
+ /**
1790
+ * Scale up/down a process
1791
+ * @method scale
1792
+ */
1793
+ scale (app_name, number, cb) {
1794
+ var that = this;
1795
+
1796
+ function addProcs(proc, value, cb) {
1797
+ (function ex(proc, number) {
1798
+ if (number-- === 0) return cb();
1799
+ Common.printOut(conf.PREFIX_MSG + 'Scaling up application');
1800
+ that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number));
1801
+ })(proc, number);
1802
+ }
1803
+
1804
+ function rmProcs(procs, value, cb) {
1805
+ var i = 0;
1806
+
1807
+ (function ex(procs, number) {
1808
+ if (number++ === 0) return cb();
1809
+ that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number));
1810
+ })(procs, number);
1811
+ }
1812
+
1813
+ function end() {
1814
+ return cb ? cb(null, {success:true}) : that.speedList();
1815
+ }
1816
+
1817
+ this.Client.getProcessByName(app_name, function(err, procs) {
1818
+ if (err) {
1819
+ Common.printError(err);
1820
+ return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
1821
+ }
1822
+
1823
+ if (!procs || procs.length === 0) {
1824
+ Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name);
1825
+ return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT);
1826
+ }
1827
+
1828
+ var proc_number = procs.length;
1829
+
1830
+ if (typeof(number) === 'string' && number.indexOf('+') >= 0) {
1831
+ number = parseInt(number, 10);
1832
+ return addProcs(procs[0], number, end);
1833
+ }
1834
+ else if (typeof(number) === 'string' && number.indexOf('-') >= 0) {
1835
+ number = parseInt(number, 10);
1836
+ return rmProcs(procs[0], number, end);
1837
+ }
1838
+ else {
1839
+ number = parseInt(number, 10);
1840
+ number = number - proc_number;
1841
+
1842
+ if (number < 0)
1843
+ return rmProcs(procs, number, end);
1844
+ else if (number > 0)
1845
+ return addProcs(procs[0], number, end);
1846
+ else {
1847
+ Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do');
1848
+ return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT);
1849
+ }
1850
+ }
1851
+ });
1852
+ }
1853
+
1854
+ /**
1855
+ * Description
1856
+ * @method describeProcess
1857
+ * @param {} pm2_id
1858
+ * @return
1859
+ */
1860
+ describe (pm2_id, cb) {
1861
+ var that = this;
1862
+
1863
+ var found_proc = [];
1864
+
1865
+ that.Client.executeRemote('getMonitorData', {}, function(err, list) {
1866
+ if (err) {
1867
+ Common.printError('Error retrieving process list: ' + err);
1868
+ that.exitCli(conf.ERROR_EXIT);
1869
+ }
1870
+
1871
+ list.forEach(function(proc) {
1872
+ if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) ||
1873
+ (typeof(pm2_id) === 'string' && proc.name == pm2_id)) {
1874
+ found_proc.push(proc);
1875
+ }
1876
+ });
1877
+
1878
+ if (found_proc.length === 0) {
1879
+ Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id);
1880
+ return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT);
1881
+ }
1882
+
1883
+ if (!cb) {
1884
+ found_proc.forEach(function(proc) {
1885
+ UX.describe(proc);
1886
+ });
1887
+ }
1888
+
1889
+ return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);
1890
+ });
1891
+ }
1892
+
1893
+ /**
1894
+ * API method to perform a deep update of ZM2
1895
+ * @method deepUpdate
1896
+ */
1897
+ deepUpdate (cb) {
1898
+ var that = this;
1899
+
1900
+ Common.printOut(conf.PREFIX_MSG + 'Updating ZM2...');
1901
+
1902
+ var child = sexec("npm i -g zm2@latest; zm2 update");
1903
+
1904
+ child.stdout.on('end', function() {
1905
+ Common.printOut(conf.PREFIX_MSG + 'ZM2 successfully updated');
1906
+ cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);
1907
+ });
1908
+ }
1909
+ };
1910
+
1911
+
1912
+ //////////////////////////
1913
+ // Load all API methods //
1914
+ //////////////////////////
1915
+
1916
+ require('./API/Extra.js')(API);
1917
+ require('./API/Deploy.js')(API);
1918
+ require('./API/Modules/index.js')(API);
1919
+
1920
+ require('./API/pm2-plus/link.js')(API);
1921
+ require('./API/pm2-plus/process-selector.js')(API);
1922
+ require('./API/pm2-plus/helpers.js')(API);
1923
+
1924
+ require('./API/Configuration.js')(API);
1925
+ require('./API/Version.js')(API);
1926
+ require('./API/Startup.js')(API);
1927
+ require('./API/LogManagement.js')(API);
1928
+ require('./API/Containerizer.js')(API);
1929
+
1930
+
1931
+ module.exports = API;