God.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. * Main Daemon side file
  13. *
  14. ******************************/
  15. var cluster = require('cluster');
  16. var numCPUs = require('os').cpus() ? require('os').cpus().length : 1;
  17. var path = require('path');
  18. var EventEmitter2 = require('eventemitter2').EventEmitter2;
  19. var fs = require('fs');
  20. var vizion = require('vizion');
  21. var debug = require('debug')('pm2:god');
  22. var Utility = require('./Utility');
  23. var cst = require('../constants.js');
  24. var timesLimit = require('async/timesLimit');
  25. var Configuration = require('./Configuration.js');
  26. /**
  27. * Override cluster module configuration
  28. */
  29. cluster.setupMaster({
  30. windowsHide: true,
  31. exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
  32. });
  33. /**
  34. * Expose God
  35. */
  36. var God = module.exports = {
  37. next_id : 0,
  38. clusters_db : {},
  39. configuration: {},
  40. started_at : Date.now(),
  41. system_infos_proc: null,
  42. system_infos: null,
  43. bus : new EventEmitter2({
  44. wildcard: true,
  45. delimiter: ':',
  46. maxListeners: 1000
  47. })
  48. };
  49. Utility.overrideConsole(God.bus);
  50. /**
  51. * Populate God namespace
  52. */
  53. require('./Event.js')(God);
  54. require('./God/Methods.js')(God);
  55. require('./God/ForkMode.js')(God);
  56. require('./God/ClusterMode.js')(God);
  57. require('./God/Reload')(God);
  58. require('./God/ActionMethods')(God);
  59. require('./Watcher')(God);
  60. God.init = function() {
  61. require('./Worker.js')(this)
  62. God.system_infos_proc = null
  63. this.configuration = Configuration.getSync('pm2')
  64. setTimeout(function() {
  65. God.Worker.start()
  66. }, 500)
  67. }
  68. God.writeExitSeparator = function(pm2_env, code, signal) {
  69. try {
  70. var exit_sep = `[PM2][${new Date().toISOString()}] app exited`
  71. if (code)
  72. exit_sep += `itself with exit code: ${code}`
  73. if (signal)
  74. exit_sep += `by an external signal: ${signal}`
  75. exit_sep += '\n'
  76. if (pm2_env.pm_out_log_path)
  77. fs.writeFileSync(pm2_env.pm_out_log_path, exit_sep)
  78. if (pm2_env.pm_err_log_path)
  79. fs.writeFileSync(pm2_env.pm_err_log_path, exit_sep)
  80. if (pm2_env.pm_log_path)
  81. fs.writeFileSync(pm2_env.pm_log_path, exit_sep)
  82. } catch(e) {
  83. }
  84. }
  85. /**
  86. * Init new process
  87. */
  88. God.prepare = function prepare (env, cb) {
  89. // generate a new unique id for each processes
  90. env.env.unique_id = Utility.generateUUID()
  91. // if the app is standalone, no multiple instance
  92. if (typeof env.instances === 'undefined') {
  93. env.vizion_running = false;
  94. if (env.env && env.env.vizion_running) env.env.vizion_running = false;
  95. if (env.status == cst.STOPPED_STATUS) {
  96. env.pm_id = God.getNewId()
  97. var clu = {
  98. pm2_env : env,
  99. process: {
  100. }
  101. }
  102. God.clusters_db[env.pm_id] = clu
  103. return cb(null, [ God.clusters_db[env.pm_id] ])
  104. }
  105. return God.executeApp(env, function (err, clu) {
  106. if (err) return cb(err);
  107. God.notify('start', clu, true);
  108. return cb(null, [ Utility.clone(clu) ]);
  109. });
  110. }
  111. // find how many replicate the user want
  112. env.instances = parseInt(env.instances);
  113. if (env.instances === 0) {
  114. env.instances = numCPUs;
  115. } else if (env.instances < 0) {
  116. env.instances += numCPUs;
  117. }
  118. if (env.instances <= 0) {
  119. env.instances = 1;
  120. }
  121. timesLimit(env.instances, 1, function (n, next) {
  122. env.vizion_running = false;
  123. if (env.env && env.env.vizion_running) {
  124. env.env.vizion_running = false;
  125. }
  126. God.injectVariables(env, function inject (err, _env) {
  127. if (err) return next(err);
  128. return God.executeApp(Utility.clone(_env), function (err, clu) {
  129. if (err) return next(err);
  130. God.notify('start', clu, true);
  131. // here call next wihtout an array because
  132. // async.times aggregate the result into an array
  133. return next(null, Utility.clone(clu));
  134. });
  135. });
  136. }, cb);
  137. };
  138. /**
  139. * Launch the specified script (present in env)
  140. * @api private
  141. * @method executeApp
  142. * @param {Mixed} env
  143. * @param {Function} cb
  144. * @return Literal
  145. */
  146. God.executeApp = function executeApp(env, cb) {
  147. var env_copy = Utility.clone(env);
  148. Utility.extend(env_copy, env_copy.env);
  149. env_copy['status'] = cst.LAUNCHING_STATUS;
  150. env_copy['pm_uptime'] = Date.now();
  151. env_copy['axm_actions'] = [];
  152. env_copy['axm_monitor'] = {};
  153. env_copy['axm_options'] = {};
  154. env_copy['axm_dynamic'] = {};
  155. env_copy['vizion_running'] =
  156. env_copy['vizion_running'] !== undefined ? env_copy['vizion_running'] : false;
  157. if (!env_copy.created_at)
  158. env_copy['created_at'] = Date.now();
  159. /**
  160. * Enter here when it's the first time that the process is created
  161. * 1 - Assign a new id
  162. * 2 - Reset restart time and unstable_restarts
  163. * 3 - Assign a log file name depending on the id
  164. * 4 - If watch option is set, look for changes
  165. */
  166. if (env_copy['pm_id'] === undefined) {
  167. env_copy['pm_id'] = God.getNewId();
  168. env_copy['restart_time'] = 0;
  169. env_copy['unstable_restarts'] = 0;
  170. // add -pm_id to pid file
  171. env_copy.pm_pid_path = env_copy.pm_pid_path.replace(/-[0-9]+\.pid$|\.pid$/g, '-' + env_copy['pm_id'] + '.pid');
  172. // If merge option, dont separate the logs
  173. if (!env_copy['merge_logs']) {
  174. ['', '_out', '_err'].forEach(function(k){
  175. var key = 'pm' + k + '_log_path';
  176. env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log'));
  177. });
  178. }
  179. // Initiate watch file
  180. if (env_copy['watch']) {
  181. God.watch.enable(env_copy);
  182. }
  183. }
  184. God.registerCron(env_copy)
  185. /** Callback when application is launched */
  186. var readyCb = function ready(proc) {
  187. // If vizion enabled run versioning retrieval system
  188. if (proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== "false")
  189. God.finalizeProcedure(proc);
  190. else
  191. God.notify('online', proc);
  192. if (proc.pm2_env.status !== cst.ERRORED_STATUS)
  193. proc.pm2_env.status = cst.ONLINE_STATUS
  194. console.log(`App [${proc.pm2_env.name}:${proc.pm2_env.pm_id}] online`);
  195. if (cb) cb(null, proc);
  196. }
  197. if (env_copy.exec_mode === 'cluster_mode') {
  198. /**
  199. * Cluster mode logic (for NodeJS apps)
  200. */
  201. God.nodeApp(env_copy, function nodeApp(err, clu) {
  202. if (cb && err) return cb(err);
  203. if (err) return false;
  204. var old_env = God.clusters_db[clu.pm2_env.pm_id];
  205. if (old_env) {
  206. old_env = null;
  207. God.clusters_db[clu.pm2_env.pm_id] = null;
  208. }
  209. God.clusters_db[clu.pm2_env.pm_id] = clu;
  210. clu.once('error', function(err) {
  211. console.error(err.stack || err);
  212. clu.pm2_env.status = cst.ERRORED_STATUS;
  213. try {
  214. clu.destroy && clu.destroy();
  215. }
  216. catch (e) {
  217. console.error(e.stack || e);
  218. God.handleExit(clu, cst.ERROR_EXIT);
  219. }
  220. });
  221. clu.once('disconnect', function() {
  222. console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);
  223. });
  224. clu.once('exit', function cluExit(code, signal) {
  225. //God.writeExitSeparator(clu.pm2_env, code, signal)
  226. God.handleExit(clu, code || 0, signal || 'SIGINT');
  227. });
  228. return clu.once('online', function () {
  229. if (!clu.pm2_env.wait_ready)
  230. return readyCb(clu);
  231. // Timeout if the ready message has not been sent before listen_timeout
  232. var ready_timeout = setTimeout(function() {
  233. God.bus.removeListener('process:msg', listener)
  234. return readyCb(clu)
  235. }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
  236. var listener = function (packet) {
  237. if (packet.raw === 'ready' &&
  238. packet.process.name === clu.pm2_env.name &&
  239. packet.process.pm_id === clu.pm2_env.pm_id) {
  240. clearTimeout(ready_timeout);
  241. God.bus.removeListener('process:msg', listener)
  242. return readyCb(clu)
  243. }
  244. }
  245. God.bus.on('process:msg', listener);
  246. });
  247. });
  248. }
  249. else {
  250. /**
  251. * Fork mode logic
  252. */
  253. God.forkMode(env_copy, function forkMode(err, clu) {
  254. if (cb && err) return cb(err);
  255. if (err) return false;
  256. var old_env = God.clusters_db[clu.pm2_env.pm_id];
  257. if (old_env) old_env = null;
  258. God.clusters_db[env_copy.pm_id] = clu;
  259. clu.once('error', function cluError(err) {
  260. console.error(err.stack || err);
  261. clu.pm2_env.status = cst.ERRORED_STATUS;
  262. try {
  263. clu.kill && clu.kill();
  264. }
  265. catch (e) {
  266. console.error(e.stack || e);
  267. God.handleExit(clu, cst.ERROR_EXIT);
  268. }
  269. });
  270. clu.once('exit', function cluClose(code, signal) {
  271. //God.writeExitSeparator(clu.pm2_env, code, signal)
  272. if (clu.connected === true)
  273. clu.disconnect && clu.disconnect();
  274. clu._reloadLogs = null;
  275. return God.handleExit(clu, code || 0, signal);
  276. });
  277. if (!clu.pm2_env.wait_ready)
  278. return readyCb(clu);
  279. // Timeout if the ready message has not been sent before listen_timeout
  280. var ready_timeout = setTimeout(function() {
  281. God.bus.removeListener('process:msg', listener)
  282. return readyCb(clu)
  283. }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
  284. var listener = function (packet) {
  285. if (packet.raw === 'ready' &&
  286. packet.process.name === clu.pm2_env.name &&
  287. packet.process.pm_id === clu.pm2_env.pm_id) {
  288. clearTimeout(ready_timeout);
  289. God.bus.removeListener('process:msg', listener)
  290. return readyCb(clu)
  291. }
  292. }
  293. God.bus.on('process:msg', listener);
  294. });
  295. }
  296. return false;
  297. };
  298. /**
  299. * Handle logic when a process exit (Node or Fork)
  300. * @method handleExit
  301. * @param {} clu
  302. * @param {} exit_code
  303. * @return
  304. */
  305. God.handleExit = function handleExit(clu, exit_code, kill_signal) {
  306. console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] exited with code [${exit_code}] via signal [${kill_signal || 'SIGINT'}]`)
  307. var proc = this.clusters_db[clu.pm2_env.pm_id];
  308. if (!proc) {
  309. console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);
  310. return false;
  311. }
  312. var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS
  313. || proc.pm2_env.status == cst.STOPPED_STATUS
  314. || proc.pm2_env.status == cst.ERRORED_STATUS)
  315. || (proc.pm2_env.autorestart === false || proc.pm2_env.autorestart === "false");
  316. var overlimit = false;
  317. if (stopping) proc.process.pid = 0;
  318. // Reset probes and actions
  319. if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
  320. if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};
  321. if (proc.pm2_env.status != cst.ERRORED_STATUS &&
  322. proc.pm2_env.status != cst.STOPPING_STATUS)
  323. proc.pm2_env.status = cst.STOPPED_STATUS;
  324. if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {
  325. try {
  326. fs.unlinkSync(proc.pm2_env.pm_pid_path);
  327. } catch (e) {
  328. debug('Error when unlinking pid file', e);
  329. }
  330. }
  331. /**
  332. * Avoid infinite reloop if an error is present
  333. */
  334. // If the process has been created less than 15seconds ago
  335. // And if the process has an uptime less than a second
  336. var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;
  337. var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 16;
  338. if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {
  339. if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {
  340. // Increment unstable restart
  341. proc.pm2_env.unstable_restarts += 1;
  342. }
  343. }
  344. if (proc.pm2_env.unstable_restarts >= max_restarts) {
  345. // Too many unstable restart in less than 15 seconds
  346. // Set the process as 'ERRORED'
  347. // And stop restarting it
  348. proc.pm2_env.status = cst.ERRORED_STATUS;
  349. proc.process.pid = 0;
  350. console.log('Script %s had too many unstable restarts (%d). Stopped. %j',
  351. proc.pm2_env.pm_exec_path,
  352. proc.pm2_env.unstable_restarts,
  353. proc.pm2_env.status);
  354. God.notify('restart overlimit', proc);
  355. proc.pm2_env.unstable_restarts = 0;
  356. proc.pm2_env.created_at = null;
  357. overlimit = true;
  358. }
  359. if (typeof(exit_code) !== 'undefined') proc.pm2_env.exit_code = exit_code;
  360. God.notify('exit', proc);
  361. if (God.pm2_being_killed) {
  362. //console.log('[HandleExit] PM2 is being killed, stopping restart procedure...');
  363. return false;
  364. }
  365. var restart_delay = 0;
  366. if (proc.pm2_env.restart_delay !== undefined &&
  367. !isNaN(parseInt(proc.pm2_env.restart_delay))) {
  368. proc.pm2_env.status = cst.WAITING_RESTART;
  369. restart_delay = parseInt(proc.pm2_env.restart_delay);
  370. }
  371. if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
  372. !isNaN(parseInt(proc.pm2_env.exp_backoff_restart_delay))) {
  373. proc.pm2_env.status = cst.WAITING_RESTART;
  374. if (!proc.pm2_env.prev_restart_delay) {
  375. proc.pm2_env.prev_restart_delay = proc.pm2_env.exp_backoff_restart_delay
  376. restart_delay = proc.pm2_env.exp_backoff_restart_delay
  377. }
  378. else {
  379. proc.pm2_env.prev_restart_delay = Math.floor(Math.min(15000, proc.pm2_env.prev_restart_delay * 1.5))
  380. restart_delay = proc.pm2_env.prev_restart_delay
  381. }
  382. console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] will restart in ${restart_delay}ms`)
  383. }
  384. if (!stopping && !overlimit) {
  385. //make this property unenumerable
  386. Object.defineProperty(proc.pm2_env, 'restart_task', {configurable: true, writable: true});
  387. proc.pm2_env.restart_task = setTimeout(function() {
  388. proc.pm2_env.restart_time += 1;
  389. God.executeApp(proc.pm2_env);
  390. }, restart_delay);
  391. }
  392. return false;
  393. };
  394. /**
  395. * @method finalizeProcedure
  396. * @param proc {Object}
  397. * @return
  398. */
  399. God.finalizeProcedure = function finalizeProcedure(proc) {
  400. var last_path = '';
  401. var current_path = proc.pm2_env.cwd || path.dirname(proc.pm2_env.pm_exec_path);
  402. var proc_id = proc.pm2_env.pm_id;
  403. proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
  404. if (proc.pm2_env.vizion_running === true) {
  405. debug('Vizion is already running for proc id: %d, skipping this round', proc_id);
  406. return God.notify('online', proc);
  407. }
  408. proc.pm2_env.vizion_running = true;
  409. vizion.analyze({folder : current_path}, function recur_path(err, meta){
  410. var proc = God.clusters_db[proc_id];
  411. if (err)
  412. debug(err.stack || err);
  413. if (!proc ||
  414. !proc.pm2_env ||
  415. proc.pm2_env.status == cst.STOPPED_STATUS ||
  416. proc.pm2_env.status == cst.STOPPING_STATUS ||
  417. proc.pm2_env.status == cst.ERRORED_STATUS) {
  418. return console.error('Cancelling versioning data parsing');
  419. }
  420. proc.pm2_env.vizion_running = false;
  421. if (!err) {
  422. proc.pm2_env.versioning = meta;
  423. proc.pm2_env.versioning.repo_path = current_path;
  424. God.notify('online', proc);
  425. }
  426. else if (err && current_path === last_path) {
  427. proc.pm2_env.versioning = null;
  428. God.notify('online', proc);
  429. }
  430. else {
  431. last_path = current_path;
  432. current_path = path.dirname(current_path);
  433. proc.pm2_env.vizion_running = true;
  434. vizion.analyze({folder : current_path}, recur_path);
  435. }
  436. return false;
  437. });
  438. };
  439. /**
  440. * Inject variables into processes
  441. * @param {Object} env environnement to be passed to the process
  442. * @param {Function} cb invoked with <err, env>
  443. */
  444. God.injectVariables = function injectVariables (env, cb) {
  445. // allow to override the key of NODE_APP_INSTANCE if wanted
  446. var instanceKey = process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var;
  447. // we need to find the last NODE_APP_INSTANCE used
  448. var instances = Object.keys(God.clusters_db)
  449. .map(function (procId) {
  450. return God.clusters_db[procId];
  451. }).filter(function (proc) {
  452. return proc.pm2_env.name === env.name &&
  453. typeof proc.pm2_env[instanceKey] !== 'undefined';
  454. }).map(function (proc) {
  455. return proc.pm2_env[instanceKey];
  456. }).sort(function (a, b) {
  457. return b - a;
  458. });
  459. // default to last one + 1
  460. var instanceNumber = typeof instances[0] === 'undefined' ? 0 : instances[0] + 1;
  461. // but try to find a one available
  462. for (var i = 0; i < instances.length; i++) {
  463. if (instances.indexOf(i) === -1) {
  464. instanceNumber = i;
  465. break;
  466. }
  467. }
  468. env[instanceKey] = instanceNumber;
  469. // if using increment_var, we need to increment it
  470. if (env.increment_var) {
  471. var lastIncrement = Object.keys(God.clusters_db)
  472. .map(function (procId) {
  473. return God.clusters_db[procId];
  474. }).filter(function (proc) {
  475. return proc.pm2_env.name === env.name &&
  476. typeof proc.pm2_env[env.increment_var] !== 'undefined';
  477. }).map(function (proc) {
  478. return proc.pm2_env[env.increment_var];
  479. }).sort(function (a, b) {
  480. return b - a;
  481. })[0];
  482. // inject a incremental variable
  483. var defaut = env.env[env.increment_var] || 0;
  484. env[env.increment_var] = typeof lastIncrement === 'undefined' ? defaut : lastIncrement + 1;
  485. env.env[env.increment_var] = env[env.increment_var];
  486. }
  487. return cb(null, env);
  488. };
  489. God.init()