@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/God.js ADDED
@@ -0,0 +1,632 @@
1
+ /**
2
+ * Copyright 2013-2022 the PM2 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
+
7
+ /******************************
8
+ * ______ _______ ______
9
+ * | __ \ | |__ |
10
+ * | __/ | __|
11
+ * |___| |__|_|__|______|
12
+ *
13
+ * Main Daemon side file
14
+ *
15
+ ******************************/
16
+
17
+ var cluster = require('cluster');
18
+ var numCPUs = require('os').cpus() ? require('os').cpus().length : 1;
19
+ var path = require('path');
20
+ var EventEmitter2 = require('eventemitter2').EventEmitter2;
21
+ var fs = require('fs');
22
+ var vizion = require('vizion');
23
+ var debug = require('debug')('zm2:god');
24
+ var Utility = require('./Utility');
25
+ var cst = require('../constants.js');
26
+ var timesLimit = require('async/timesLimit');
27
+ var Configuration = require('./Configuration.js');
28
+
29
+ /**
30
+ * Override cluster module configuration
31
+ */
32
+
33
+ if (cst.IS_BUN == true) {
34
+ cluster.setupMaster({
35
+ windowsHide: true,
36
+ exec : path.resolve(path.dirname(module.filename), 'ProcessContainerBun.js')
37
+ });
38
+ }
39
+ else {
40
+ cluster.setupMaster({
41
+ windowsHide: true,
42
+ exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Expose God
48
+ */
49
+ var God = module.exports = {
50
+ next_id : 0,
51
+ clusters_db : {},
52
+ configuration: {},
53
+ started_at : Date.now(),
54
+ system_infos_proc: null,
55
+ system_infos: null,
56
+ bus : new EventEmitter2({
57
+ wildcard: true,
58
+ delimiter: ':',
59
+ maxListeners: 1000
60
+ })
61
+ };
62
+
63
+ Utility.overrideConsole(God.bus);
64
+
65
+ /**
66
+ * Populate God namespace
67
+ */
68
+ require('./Event.js')(God);
69
+ require('./God/Methods.js')(God);
70
+ require('./God/ForkMode.js')(God);
71
+ require('./God/ClusterMode.js')(God);
72
+ require('./God/Reload')(God);
73
+ require('./God/ActionMethods')(God);
74
+ require('./Watcher')(God);
75
+
76
+ God.init = function() {
77
+ require('./Worker.js')(this)
78
+ God.system_infos_proc = null
79
+
80
+ this.configuration = Configuration.getSync('pm2')
81
+
82
+ setTimeout(function() {
83
+ God.Worker.start()
84
+ }, 500)
85
+ }
86
+
87
+ God.writeExitSeparator = function(pm2_env, code, signal) {
88
+ try {
89
+ var exit_sep = `[PM2][${new Date().toISOString()}] app exited`
90
+ if (code)
91
+ exit_sep += `itself with exit code: ${code}`
92
+ if (signal)
93
+ exit_sep += `by an external signal: ${signal}`
94
+ exit_sep += '\n'
95
+
96
+ if (pm2_env.pm_out_log_path)
97
+ fs.writeFileSync(pm2_env.pm_out_log_path, exit_sep)
98
+ if (pm2_env.pm_err_log_path)
99
+ fs.writeFileSync(pm2_env.pm_err_log_path, exit_sep)
100
+ if (pm2_env.pm_log_path)
101
+ fs.writeFileSync(pm2_env.pm_log_path, exit_sep)
102
+ } catch(e) {
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Init new process
108
+ */
109
+ God.prepare = function prepare (env, cb) {
110
+ // generate a new unique id for each processes
111
+ env.env.unique_id = Utility.generateUUID()
112
+
113
+ // if the app is standalone, no multiple instance
114
+ if (typeof env.instances === 'undefined') {
115
+ env.vizion_running = false;
116
+ if (env.env && env.env.vizion_running) env.env.vizion_running = false;
117
+
118
+ if (env.status == cst.STOPPED_STATUS) {
119
+ env.pm_id = God.getNewId()
120
+ var clu = {
121
+ pm2_env : env,
122
+ process: {
123
+ }
124
+ }
125
+ God.clusters_db[env.pm_id] = clu
126
+ God.registerCron(env)
127
+ return cb(null, [ God.clusters_db[env.pm_id] ])
128
+ }
129
+
130
+ return God.executeApp(env, function (err, clu) {
131
+ if (err) return cb(err);
132
+ God.notify('start', clu, true);
133
+ return cb(null, [ Utility.clone(clu) ]);
134
+ });
135
+ }
136
+
137
+ // find how many replicate the user want
138
+ env.instances = parseInt(env.instances);
139
+ if (env.instances === 0) {
140
+ env.instances = numCPUs;
141
+ } else if (env.instances < 0) {
142
+ env.instances += numCPUs;
143
+ }
144
+ if (env.instances <= 0) {
145
+ env.instances = 1;
146
+ }
147
+
148
+ timesLimit(env.instances, 1, function (n, next) {
149
+ env.vizion_running = false;
150
+ if (env.env && env.env.vizion_running) {
151
+ env.env.vizion_running = false;
152
+ }
153
+
154
+ God.injectVariables(env, function inject (err, _env) {
155
+ if (err) return next(err);
156
+ return God.executeApp(Utility.clone(_env), function (err, clu) {
157
+ if (err) return next(err);
158
+ God.notify('start', clu, true);
159
+ // here call next wihtout an array because
160
+ // async.times aggregate the result into an array
161
+ return next(null, Utility.clone(clu));
162
+ });
163
+ });
164
+ }, cb);
165
+ };
166
+
167
+ /**
168
+ * Launch the specified script (present in env)
169
+ * @api private
170
+ * @method executeApp
171
+ * @param {Mixed} env
172
+ * @param {Function} cb
173
+ * @return Literal
174
+ */
175
+ God.executeApp = function executeApp(env, cb) {
176
+ var env_copy = Utility.clone(env);
177
+
178
+ Utility.extend(env_copy, env_copy.env);
179
+
180
+ env_copy['status'] = env.autostart ? cst.LAUNCHING_STATUS : cst.STOPPED_STATUS;
181
+ env_copy['pm_uptime'] = Date.now();
182
+ env_copy['axm_actions'] = [];
183
+ env_copy['axm_monitor'] = {};
184
+ env_copy['axm_options'] = {};
185
+ env_copy['axm_dynamic'] = {};
186
+ env_copy['vizion_running'] =
187
+ env_copy['vizion_running'] !== undefined ? env_copy['vizion_running'] : false;
188
+
189
+ if (!env_copy.created_at)
190
+ env_copy['created_at'] = Date.now();
191
+
192
+ /**
193
+ * Enter here when it's the first time that the process is created
194
+ * 1 - Assign a new id
195
+ * 2 - Reset restart time and unstable_restarts
196
+ * 3 - Assign a log file name depending on the id
197
+ * 4 - If watch option is set, look for changes
198
+ */
199
+ if (env_copy['pm_id'] === undefined) {
200
+ env_copy['pm_id'] = God.getNewId();
201
+ env_copy['restart_time'] = 0;
202
+ env_copy['unstable_restarts'] = 0;
203
+
204
+ // add -pm_id to pid file
205
+ env_copy.pm_pid_path = env_copy.pm_pid_path.replace(/-[0-9]+\.pid$|\.pid$/g, '-' + env_copy['pm_id'] + '.pid');
206
+
207
+ // If merge option, dont separate the logs
208
+ if (!env_copy['merge_logs']) {
209
+ ['', '_out', '_err'].forEach(function(k){
210
+ var key = 'pm' + k + '_log_path';
211
+ env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log'));
212
+ });
213
+ }
214
+
215
+ // Initiate watch file
216
+ if (env_copy['watch']) {
217
+ God.watch.enable(env_copy);
218
+ }
219
+ }
220
+
221
+ God.registerCron(env_copy)
222
+
223
+ if (env_copy['autostart'] === false) {
224
+ var clu = {pm2_env: env_copy, process: {pid: 0}};
225
+ God.clusters_db[env_copy.pm_id] = clu;
226
+ return cb(null, clu);
227
+ }
228
+
229
+ var cb_called = false
230
+
231
+ /** Callback when application is launched */
232
+ var readyCb = function ready(proc) {
233
+ cb_called = true
234
+
235
+ proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
236
+ // If vizion enabled run versioning retrieval system
237
+ if (cst.ENABLE_GIT_PARSING === true &&
238
+ proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== "false") {
239
+ God.finalizeProcedure(proc);
240
+ }
241
+ else
242
+ God.notify('online', proc);
243
+
244
+ if (proc.pm2_env.status !== cst.ERRORED_STATUS)
245
+ proc.pm2_env.status = cst.ONLINE_STATUS
246
+
247
+ console.log(`App [${proc.pm2_env.name}:${proc.pm2_env.pm_id}] online`);
248
+ if (cb) cb(null, proc);
249
+ }
250
+
251
+ if (env_copy.exec_mode === 'cluster_mode') {
252
+ /**
253
+ * Cluster mode logic (for NodeJS apps)
254
+ */
255
+ God.nodeApp(env_copy, function nodeApp(err, clu) {
256
+ if (cb && err) return cb(err);
257
+ if (err) return false;
258
+
259
+ var old_env = God.clusters_db[clu.pm2_env.pm_id];
260
+
261
+ if (old_env) {
262
+ old_env = null;
263
+ God.clusters_db[clu.pm2_env.pm_id] = null;
264
+ }
265
+
266
+ God.clusters_db[clu.pm2_env.pm_id] = clu;
267
+
268
+
269
+ if (cst.IS_BUN) {
270
+ // When starting an app that does not listen on a port
271
+ // Bun do not call 'online' event
272
+ // This is a temporary workaround
273
+ var a = setTimeout(() => {
274
+ if (clu.pm2_env)
275
+ God.clusters_db[clu.pm2_env.pm_id].state = 'online'
276
+ return readyCb(clu)
277
+ }, 500)
278
+ }
279
+
280
+ clu.once('error', function(err) {
281
+ if (cst.IS_BUN)
282
+ clearTimeout(a)
283
+
284
+ console.error(err.stack || err);
285
+ try {
286
+ clu.destroy && clu.destroy();
287
+ }
288
+ catch (e) {
289
+ console.error(e.stack || e);
290
+ God.handleExit(clu, cst.ERROR_EXIT);
291
+ }
292
+ });
293
+
294
+ clu.once('disconnect', function() {
295
+ if (cst.IS_BUN)
296
+ clearTimeout(a)
297
+
298
+ console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);
299
+ });
300
+
301
+ clu.once('exit', function cluExit(code, signal) {
302
+ if (cst.IS_BUN) {
303
+ clearTimeout(a)
304
+ if (cb_called == false)
305
+ readyCb(clu);
306
+ }
307
+ //God.writeExitSeparator(clu.pm2_env, code, signal)
308
+
309
+ God.handleExit(clu, code || 0, signal || 'SIGINT');
310
+ });
311
+
312
+ return clu.once('online', function () {
313
+ if (cst.IS_BUN) {
314
+ clearTimeout(a);
315
+ }
316
+
317
+ if (!clu.pm2_env.wait_ready)
318
+ return readyCb(clu);
319
+
320
+ // Timeout if the ready message has not been sent before listen_timeout
321
+ var ready_timeout = setTimeout(function() {
322
+ God.bus.removeListener('process:msg', listener)
323
+ return readyCb(clu)
324
+ }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
325
+
326
+ var listener = function (packet) {
327
+ if (packet.raw === 'ready' &&
328
+ packet.process.name === clu.pm2_env.name &&
329
+ packet.process.pm_id === clu.pm2_env.pm_id) {
330
+ clearTimeout(ready_timeout);
331
+ God.bus.removeListener('process:msg', listener)
332
+ return readyCb(clu)
333
+ }
334
+ }
335
+
336
+ God.bus.on('process:msg', listener);
337
+ });
338
+ });
339
+ }
340
+ else {
341
+ /**
342
+ * Fork mode logic
343
+ */
344
+ God.forkMode(env_copy, function forkMode(err, clu) {
345
+ if (cb && err) return cb(err);
346
+ if (err) return false;
347
+
348
+ var old_env = God.clusters_db[clu.pm2_env.pm_id];
349
+ if (old_env) old_env = null;
350
+
351
+ God.clusters_db[env_copy.pm_id] = clu;
352
+
353
+ clu.once('error', function cluError(err) {
354
+ console.error(err.stack || err);
355
+ try {
356
+ clu.kill && clu.kill();
357
+ }
358
+ catch (e) {
359
+ console.error(e.stack || e);
360
+ God.handleExit(clu, cst.ERROR_EXIT);
361
+ }
362
+ });
363
+
364
+ clu.once('exit', function cluClose(code, signal) {
365
+ //God.writeExitSeparator(clu.pm2_env, code, signal)
366
+
367
+ if (clu.connected === true)
368
+ clu.disconnect && clu.disconnect();
369
+ clu._reloadLogs = null;
370
+ return God.handleExit(clu, code || 0, signal);
371
+ });
372
+
373
+ if (!clu.pm2_env.wait_ready)
374
+ return readyCb(clu);
375
+
376
+ // Timeout if the ready message has not been sent before listen_timeout
377
+ var ready_timeout = setTimeout(function() {
378
+ God.bus.removeListener('process:msg', listener)
379
+ return readyCb(clu)
380
+ }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
381
+
382
+ var listener = function (packet) {
383
+ if (packet.raw === 'ready' &&
384
+ packet.process.name === clu.pm2_env.name &&
385
+ packet.process.pm_id === clu.pm2_env.pm_id) {
386
+ clearTimeout(ready_timeout);
387
+ God.bus.removeListener('process:msg', listener)
388
+ return readyCb(clu)
389
+ }
390
+ }
391
+ God.bus.on('process:msg', listener);
392
+ });
393
+ }
394
+ return false;
395
+ };
396
+
397
+ /**
398
+ * Handle logic when a process exit (Node or Fork)
399
+ * @method handleExit
400
+ * @param {} clu
401
+ * @param {} exit_code
402
+ * @return
403
+ */
404
+ God.handleExit = function handleExit(clu, exit_code, kill_signal) {
405
+ console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] exited with code [${exit_code}] via signal [${kill_signal || 'SIGINT'}]`)
406
+
407
+ var proc = this.clusters_db[clu.pm2_env.pm_id];
408
+
409
+ if (!proc) {
410
+ console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);
411
+ return false;
412
+ }
413
+
414
+ var stopExitCodes = proc.pm2_env.stop_exit_codes !== undefined && proc.pm2_env.stop_exit_codes !== null ? proc.pm2_env.stop_exit_codes : [];
415
+ if (!Array.isArray(stopExitCodes)) {
416
+ stopExitCodes = [stopExitCodes];
417
+ }
418
+
419
+ var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS
420
+ || proc.pm2_env.status == cst.STOPPED_STATUS
421
+ || proc.pm2_env.status == cst.ERRORED_STATUS)
422
+ || (proc.pm2_env.autorestart === false || proc.pm2_env.autorestart === "false")
423
+ || (stopExitCodes.map((strOrNum) => typeof strOrNum === 'string' ? parseInt(strOrNum, 10) : strOrNum)
424
+ .includes(exit_code));
425
+
426
+ var overlimit = false;
427
+
428
+ if (stopping) proc.process.pid = 0;
429
+
430
+ // Reset probes and actions
431
+ if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
432
+ if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};
433
+
434
+ if (proc.pm2_env.status != cst.ERRORED_STATUS &&
435
+ proc.pm2_env.status != cst.STOPPING_STATUS)
436
+ proc.pm2_env.status = cst.STOPPED_STATUS;
437
+
438
+ if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {
439
+ try {
440
+ fs.unlinkSync(proc.pm2_env.pm_pid_path);
441
+ } catch (e) {
442
+ debug('Error when unlinking pid file', e);
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Avoid infinite reloop if an error is present
448
+ */
449
+ // If the process has been created less than 15seconds ago
450
+
451
+ // And if the process has an uptime less than a second
452
+ var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;
453
+ var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 16;
454
+
455
+ if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {
456
+ if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {
457
+ // Increment unstable restart
458
+ proc.pm2_env.unstable_restarts += 1;
459
+ }
460
+ }
461
+
462
+
463
+ if (proc.pm2_env.unstable_restarts >= max_restarts) {
464
+ // Too many unstable restart in less than 15 seconds
465
+ // Set the process as 'ERRORED'
466
+ // And stop restarting it
467
+ proc.pm2_env.status = cst.ERRORED_STATUS;
468
+ proc.process.pid = 0;
469
+
470
+ console.log('Script %s had too many unstable restarts (%d). Stopped. %j',
471
+ proc.pm2_env.pm_exec_path,
472
+ proc.pm2_env.unstable_restarts,
473
+ proc.pm2_env.status);
474
+
475
+ God.notify('restart overlimit', proc);
476
+
477
+ proc.pm2_env.unstable_restarts = 0;
478
+ proc.pm2_env.created_at = null;
479
+ overlimit = true;
480
+ }
481
+
482
+ if (typeof(exit_code) !== 'undefined') proc.pm2_env.exit_code = exit_code;
483
+
484
+ God.notify('exit', proc);
485
+
486
+ if (God.pm2_being_killed) {
487
+ //console.log('[HandleExit] PM2 is being killed, stopping restart procedure...');
488
+ return false;
489
+ }
490
+
491
+ var restart_delay = 0;
492
+
493
+ if (proc.pm2_env.restart_delay !== undefined &&
494
+ !isNaN(parseInt(proc.pm2_env.restart_delay))) {
495
+ proc.pm2_env.status = cst.WAITING_RESTART;
496
+ restart_delay = parseInt(proc.pm2_env.restart_delay);
497
+ }
498
+
499
+ if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
500
+ !isNaN(parseInt(proc.pm2_env.exp_backoff_restart_delay))) {
501
+ proc.pm2_env.status = cst.WAITING_RESTART;
502
+ if (!proc.pm2_env.prev_restart_delay) {
503
+ proc.pm2_env.prev_restart_delay = proc.pm2_env.exp_backoff_restart_delay
504
+ restart_delay = proc.pm2_env.exp_backoff_restart_delay
505
+ }
506
+ else {
507
+ proc.pm2_env.prev_restart_delay = Math.floor(Math.min(15000, proc.pm2_env.prev_restart_delay * 1.5))
508
+ restart_delay = proc.pm2_env.prev_restart_delay
509
+ }
510
+ console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] will restart in ${restart_delay}ms`)
511
+ }
512
+
513
+ if (!stopping && !overlimit) {
514
+ //make this property unenumerable
515
+ Object.defineProperty(proc.pm2_env, 'restart_task', {configurable: true, writable: true});
516
+ proc.pm2_env.restart_task = setTimeout(function() {
517
+ proc.pm2_env.restart_time += 1;
518
+ God.executeApp(proc.pm2_env);
519
+ }, restart_delay);
520
+ }
521
+
522
+ return false;
523
+ };
524
+
525
+ /**
526
+ * @method finalizeProcedure
527
+ * @param proc {Object}
528
+ * @return
529
+ */
530
+ God.finalizeProcedure = function finalizeProcedure(proc) {
531
+ var last_path = '';
532
+ var current_path = proc.pm2_env.cwd || path.dirname(proc.pm2_env.pm_exec_path);
533
+ var proc_id = proc.pm2_env.pm_id;
534
+
535
+ proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
536
+
537
+ if (proc.pm2_env.vizion_running === true) {
538
+ debug('Vizion is already running for proc id: %d, skipping this round', proc_id);
539
+ return God.notify('online', proc);
540
+ }
541
+ proc.pm2_env.vizion_running = true;
542
+
543
+ vizion.analyze({folder : current_path}, function recur_path(err, meta){
544
+ var proc = God.clusters_db[proc_id];
545
+
546
+ if (err)
547
+ debug(err.stack || err);
548
+
549
+ if (!proc ||
550
+ !proc.pm2_env ||
551
+ proc.pm2_env.status == cst.STOPPED_STATUS ||
552
+ proc.pm2_env.status == cst.STOPPING_STATUS ||
553
+ proc.pm2_env.status == cst.ERRORED_STATUS) {
554
+ return console.error('Cancelling versioning data parsing');
555
+ }
556
+
557
+ proc.pm2_env.vizion_running = false;
558
+
559
+ if (!err) {
560
+ proc.pm2_env.versioning = meta;
561
+ proc.pm2_env.versioning.repo_path = current_path;
562
+ God.notify('online', proc);
563
+ }
564
+ else if (err && current_path === last_path) {
565
+ proc.pm2_env.versioning = null;
566
+ God.notify('online', proc);
567
+ }
568
+ else {
569
+ last_path = current_path;
570
+ current_path = path.dirname(current_path);
571
+ proc.pm2_env.vizion_running = true;
572
+ vizion.analyze({folder : current_path}, recur_path);
573
+ }
574
+ return false;
575
+ });
576
+ };
577
+
578
+ /**
579
+ * Inject variables into processes
580
+ * @param {Object} env environnement to be passed to the process
581
+ * @param {Function} cb invoked with <err, env>
582
+ */
583
+ God.injectVariables = function injectVariables (env, cb) {
584
+ // allow to override the key of NODE_APP_INSTANCE if wanted
585
+ var instanceKey = process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var;
586
+
587
+ // we need to find the last NODE_APP_INSTANCE used
588
+ var instances = Object.keys(God.clusters_db)
589
+ .map(function (procId) {
590
+ return God.clusters_db[procId];
591
+ }).filter(function (proc) {
592
+ return proc.pm2_env.name === env.name &&
593
+ typeof proc.pm2_env[instanceKey] !== 'undefined';
594
+ }).map(function (proc) {
595
+ return proc.pm2_env[instanceKey];
596
+ }).sort(function (a, b) {
597
+ return b - a;
598
+ });
599
+ // default to last one + 1
600
+ var instanceNumber = typeof instances[0] === 'undefined' ? 0 : instances[0] + 1;
601
+ // but try to find a one available
602
+ for (var i = 0; i < instances.length; i++) {
603
+ if (instances.indexOf(i) === -1) {
604
+ instanceNumber = i;
605
+ break;
606
+ }
607
+ }
608
+ env[instanceKey] = instanceNumber;
609
+
610
+ // if using increment_var, we need to increment it
611
+ if (env.increment_var) {
612
+ var lastIncrement = Object.keys(God.clusters_db)
613
+ .map(function (procId) {
614
+ return God.clusters_db[procId];
615
+ }).filter(function (proc) {
616
+ return proc.pm2_env.name === env.name &&
617
+ typeof proc.pm2_env[env.increment_var] !== 'undefined';
618
+ }).map(function (proc) {
619
+ return Number(proc.pm2_env[env.increment_var]);
620
+ }).sort(function (a, b) {
621
+ return b - a;
622
+ })[0];
623
+ // inject a incremental variable
624
+ var defaut = Number(env.env[env.increment_var]) || 0;
625
+ env[env.increment_var] = typeof lastIncrement === 'undefined' ? defaut : lastIncrement + 1;
626
+ env.env[env.increment_var] = env[env.increment_var];
627
+ }
628
+
629
+ return cb(null, env);
630
+ };
631
+
632
+ God.init()
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Copyright 2013-2022 the PM2 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
+ var http = require('http');
7
+ var os = require('os');
8
+ var pm2 = require('../index.js');
9
+ var urlT = require('url');
10
+ var cst = require('../constants.js');
11
+
12
+ // Default, attach to default local PM2
13
+
14
+ pm2.connect(function() {
15
+ startWebServer(pm2);
16
+ });
17
+
18
+ function startWebServer(pm2) {
19
+ http.createServer(function (req, res) {
20
+ // Add CORS headers to allow browsers to fetch data directly
21
+ res.setHeader('Access-Control-Allow-Origin', '*');
22
+ res.setHeader('Access-Control-Allow-Headers', 'Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With');
23
+ res.setHeader('Access-Control-Allow-Methods', 'GET');
24
+
25
+ // We always send json
26
+ res.setHeader('Content-Type','application/json');
27
+
28
+ var path = urlT.parse(req.url).pathname;
29
+
30
+ if (path == '/') {
31
+ // Main monit route
32
+ pm2.list(function(err, list) {
33
+ if (err) {
34
+ return res.send(err);
35
+ }
36
+ var data = {
37
+ system_info: { hostname: os.hostname(),
38
+ uptime: os.uptime()
39
+ },
40
+ monit: { loadavg: os.loadavg(),
41
+ total_mem: os.totalmem(),
42
+ free_mem: os.freemem(),
43
+ cpu: os.cpus(),
44
+ interfaces: os.networkInterfaces()
45
+ },
46
+ processes: list
47
+ };
48
+
49
+ if (cst.WEB_STRIP_ENV_VARS === true) {
50
+ for (var i = data.processes.length - 1; i >= 0; i--) {
51
+ var proc = data.processes[i];
52
+
53
+ // Strip important environment variables
54
+ if (typeof proc.pm2_env === 'undefined' && typeof proc.pm2_env.env === 'undefined') return;
55
+
56
+ delete proc.pm2_env.env;
57
+ }
58
+ }
59
+
60
+ res.statusCode = 200;
61
+ res.write(JSON.stringify(data));
62
+ return res.end();
63
+
64
+ })
65
+ }
66
+ else {
67
+ // 404
68
+ res.statusCode = 404;
69
+ res.write(JSON.stringify({err : '404'}));
70
+ return res.end();
71
+ }
72
+ }).listen(process.env.PM2_WEB_PORT || cst.WEB_PORT, cst.WEB_IPADDR, function() {
73
+ console.log('Web interface listening on %s:%s', cst.WEB_IPADDR, cst.WEB_PORT);
74
+ });
75
+
76
+ }