Common.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  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. * Common Utilities ONLY USED IN ->CLI<-
  8. */
  9. var fs = require('fs');
  10. var path = require('path');
  11. var os = require('os');
  12. var util = require('util');
  13. var chalk = require('chalk');
  14. var fclone = require('fclone');
  15. var semver = require('semver');
  16. var dayjs = require('dayjs');
  17. var execSync = require('child_process').execSync;
  18. var isBinary = require('./tools/isbinaryfile.js');
  19. var cst = require('../constants.js');
  20. var extItps = require('./API/interpreter.json');
  21. var Config = require('./tools/Config');
  22. var pkg = require('../package.json');
  23. var which = require('./tools/which.js');
  24. var Common = module.exports;
  25. function homedir() {
  26. var env = process.env;
  27. var home = env.HOME;
  28. var user = env.LOGNAME || env.USER || env.LNAME || env.USERNAME;
  29. if (process.platform === 'win32') {
  30. return env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || null;
  31. }
  32. if (process.platform === 'darwin') {
  33. return home || (user ? '/Users/' + user : null);
  34. }
  35. if (process.platform === 'linux') {
  36. return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null));
  37. }
  38. return home || null;
  39. }
  40. function resolveHome(filepath) {
  41. if (filepath[0] === '~') {
  42. return path.join(homedir(), filepath.slice(1));
  43. }
  44. return filepath;
  45. }
  46. Common.determineSilentCLI = function() {
  47. // pm2 should ignore -s --silent -v if they are after '--'
  48. var variadicArgsDashesPos = process.argv.indexOf('--');
  49. var s1opt = process.argv.indexOf('--silent')
  50. var s2opt = process.argv.indexOf('-s')
  51. if (process.env.PM2_SILENT || (variadicArgsDashesPos > -1 &&
  52. (s1opt != -1 && s1opt < variadicArgsDashesPos) &&
  53. (s2opt != -1 != s2opt < variadicArgsDashesPos)) ||
  54. (variadicArgsDashesPos == -1 && (s1opt > -1 || s2opt > -1))) {
  55. for (var key in console){
  56. var code = key.charCodeAt(0);
  57. if (code >= 97 && code <= 122){
  58. console[key] = function(){};
  59. }
  60. }
  61. process.env.PM2_DISCRETE_MODE = true;
  62. }
  63. }
  64. Common.printVersion = function() {
  65. var variadicArgsDashesPos = process.argv.indexOf('--');
  66. if (process.argv.indexOf('-v') > -1 && process.argv.indexOf('-v') < variadicArgsDashesPos) {
  67. console.log(pkg.version);
  68. process.exit(0);
  69. }
  70. }
  71. Common.lockReload = function() {
  72. try {
  73. var t1 = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString();
  74. // Check if content and if time < 30 return locked
  75. // Else if content detected (lock file staled), allow and rewritte
  76. if (t1 && t1 != '') {
  77. var diff = dayjs().diff(parseInt(t1));
  78. if (diff < cst.RELOAD_LOCK_TIMEOUT)
  79. return diff;
  80. }
  81. } catch(e) {}
  82. try {
  83. // Write latest timestamp
  84. fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, dayjs().valueOf().toString());
  85. return 0;
  86. } catch(e) {
  87. console.error(e.message || e);
  88. }
  89. };
  90. Common.unlockReload = function() {
  91. try {
  92. fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, '');
  93. } catch(e) {
  94. console.error(e.message || e);
  95. }
  96. };
  97. /**
  98. * Resolve app paths and replace missing values with defaults.
  99. * @method prepareAppConf
  100. * @param app {Object}
  101. * @param {} cwd
  102. * @param {} outputter
  103. * @return app
  104. */
  105. Common.prepareAppConf = function(opts, app) {
  106. /**
  107. * Minimum validation
  108. */
  109. if (!app.script)
  110. return new Error('No script path - aborting');
  111. var cwd = null;
  112. if (app.cwd) {
  113. cwd = path.resolve(app.cwd);
  114. process.env.PWD = app.cwd;
  115. }
  116. if (!app.node_args) {
  117. app.node_args = [];
  118. }
  119. if (app.port && app.env) {
  120. app.env.PORT = app.port;
  121. }
  122. // CWD option resolving
  123. cwd && (cwd[0] != '/') && (cwd = path.resolve(process.cwd(), cwd));
  124. cwd = cwd || opts.cwd;
  125. // Full path script resolution
  126. app.pm_exec_path = path.resolve(cwd, app.script);
  127. // If script does not exist after resolution
  128. if (!fs.existsSync(app.pm_exec_path)) {
  129. var ckd;
  130. // Try resolve command available in $PATH
  131. if ((ckd = which(app.script))) {
  132. if (typeof(ckd) !== 'string')
  133. ckd = ckd.toString();
  134. app.pm_exec_path = ckd;
  135. }
  136. else
  137. // Throw critical error
  138. return new Error(`Script not found: ${app.pm_exec_path}`);
  139. }
  140. /**
  141. * Auto detect .map file and enable source map support automatically
  142. */
  143. if (app.disable_source_map_support != true) {
  144. try {
  145. fs.accessSync(app.pm_exec_path + '.map', fs.R_OK);
  146. app.source_map_support = true;
  147. } catch(e) {}
  148. delete app.disable_source_map_support;
  149. }
  150. delete app.script;
  151. // Set current env by first adding the process environment and then extending/replacing it
  152. // with env specified on command-line or JSON file.
  153. var env = {};
  154. /**
  155. * Do not copy internal pm2 environment variables if acting on process
  156. * is made from a programmatic script started by PM2 or if a pm_id is present in env
  157. */
  158. if (cst.PM2_PROGRAMMATIC || process.env.pm_id)
  159. Common.safeExtend(env, process.env);
  160. else
  161. env = process.env;
  162. function filterEnv (envObj) {
  163. if (app.filter_env == true)
  164. return {}
  165. if (typeof app.filter_env === 'string') {
  166. delete envObj[app.filter_env]
  167. return envObj
  168. }
  169. var new_env = {};
  170. var allowedKeys = app.filter_env.reduce((acc, current) =>
  171. acc.filter( item => !item.includes(current)), Object.keys(envObj))
  172. allowedKeys.forEach( key => new_env[key] = envObj[key]);
  173. return new_env
  174. }
  175. app.env = [
  176. {}, (app.filter_env && app.filter_env.length > 0) ? filterEnv(process.env) : env, app.env || {}
  177. ].reduce(function(e1, e2){
  178. return Object.assign(e1, e2);
  179. });
  180. app.pm_cwd = cwd;
  181. // Interpreter
  182. try {
  183. Common.sink.resolveInterpreter(app);
  184. } catch(e) {
  185. return e
  186. }
  187. // Exec mode and cluster stuff
  188. Common.sink.determineExecMode(app);
  189. /**
  190. * Scary
  191. */
  192. var formated_app_name = app.name.replace(/[^a-zA-Z0-9\\.\\-]/g, '-');
  193. ['log', 'out', 'error', 'pid'].forEach(function(f){
  194. var af = app[f + '_file'], ps, ext = (f == 'pid' ? 'pid':'log'), isStd = !~['log', 'pid'].indexOf(f);
  195. if (af) af = resolveHome(af);
  196. if ((f == 'log' && typeof af == 'boolean' && af) || (f != 'log' && !af)) {
  197. ps = [cst['DEFAULT_' + ext.toUpperCase() + '_PATH'], formated_app_name + (isStd ? '-' + f : '') + '.' + ext];
  198. } else if ((f != 'log' || (f == 'log' && af)) && af !== 'NULL' && af !== '/dev/null') {
  199. ps = [cwd, af];
  200. var dir = path.dirname(path.resolve(cwd, af));
  201. if (!fs.existsSync(dir)) {
  202. Common.printError(cst.PREFIX_MSG_WARNING + 'Folder does not exist: ' + dir);
  203. Common.printOut(cst.PREFIX_MSG + 'Creating folder: ' + dir);
  204. try {
  205. require('mkdirp').sync(dir);
  206. } catch (err) {
  207. Common.printError(cst.PREFIX_MSG_ERR + 'Could not create folder: ' + path.dirname(af));
  208. throw new Error('Could not create folder');
  209. }
  210. }
  211. }
  212. // PM2 paths
  213. if (af !== 'NULL' && af !== '/dev/null') {
  214. ps && (app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = path.resolve.apply(null, ps));
  215. } else if (path.sep === '\\') {
  216. app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '\\\\.\\NUL';
  217. } else {
  218. app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '/dev/null';
  219. }
  220. delete app[f + '_file'];
  221. });
  222. return app;
  223. };
  224. /**
  225. * Check if filename is a configuration file
  226. * @param {string} filename
  227. * @return {mixed} null if not conf file, json or yaml if conf
  228. */
  229. Common.isConfigFile = function (filename) {
  230. if (typeof (filename) !== 'string')
  231. return null;
  232. if (filename.indexOf('.json') !== -1)
  233. return 'json';
  234. if (filename.indexOf('.yml') > -1 || filename.indexOf('.yaml') > -1)
  235. return 'yaml';
  236. if (filename.indexOf('.config.js') !== -1)
  237. return 'js';
  238. if (filename.indexOf('.config.cjs') !== -1)
  239. return 'js';
  240. if (filename.indexOf('.config.mjs') !== -1)
  241. return 'mjs';
  242. return null;
  243. };
  244. /**
  245. * Parses a config file like ecosystem.config.js. Supported formats: JS, JSON, JSON5, YAML.
  246. * @param {string} confString contents of the config file
  247. * @param {string} filename path to the config file
  248. * @return {Object} config object
  249. */
  250. Common.parseConfig = function(confObj, filename) {
  251. var yamljs = require('yamljs');
  252. var vm = require('vm');
  253. if (!filename ||
  254. filename == 'pipe' ||
  255. filename == 'none' ||
  256. filename.indexOf('.json') > -1) {
  257. var code = '(' + confObj + ')';
  258. var sandbox = {};
  259. return vm.runInThisContext(code, sandbox, {
  260. filename: path.resolve(filename),
  261. displayErrors: false,
  262. timeout: 1000
  263. });
  264. }
  265. else if (filename.indexOf('.yml') > -1 ||
  266. filename.indexOf('.yaml') > -1) {
  267. return yamljs.parse(confObj.toString());
  268. }
  269. else if (filename.indexOf('.config.js') > -1 || filename.indexOf('.config.cjs') > -1 || filename.indexOf('.config.mjs') > -1) {
  270. var confPath = require.resolve(path.resolve(filename));
  271. delete require.cache[confPath];
  272. return require(confPath);
  273. }
  274. };
  275. Common.retErr = function(e) {
  276. if (!e)
  277. return new Error('Unidentified error');
  278. if (e instanceof Error)
  279. return e;
  280. return new Error(e);
  281. }
  282. Common.sink = {};
  283. Common.sink.determineCron = function(app) {
  284. if (app.cron_restart == 0 || app.cron_restart == '0') {
  285. Common.printOut(cst.PREFIX_MSG + 'disabling cron restart');
  286. return
  287. }
  288. if (app.cron_restart) {
  289. const Croner = require('croner');
  290. try {
  291. Common.printOut(cst.PREFIX_MSG + 'cron restart at ' + app.cron_restart);
  292. Croner(app.cron_restart);
  293. } catch(ex) {
  294. return new Error(`Cron pattern error: ${ex.message}`);
  295. }
  296. }
  297. };
  298. /**
  299. * Handle alias (fork <=> fork_mode, cluster <=> cluster_mode)
  300. */
  301. Common.sink.determineExecMode = function(app) {
  302. if (app.exec_mode)
  303. app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');
  304. /**
  305. * Here we put the default exec mode
  306. */
  307. if (!app.exec_mode &&
  308. (app.instances >= 1 || app.instances === 0 || app.instances === -1) &&
  309. app.exec_interpreter.indexOf('node') > -1) {
  310. app.exec_mode = 'cluster_mode';
  311. } else if (!app.exec_mode) {
  312. app.exec_mode = 'fork_mode';
  313. }
  314. if (typeof app.instances == 'undefined')
  315. app.instances = 1;
  316. };
  317. var resolveNodeInterpreter = function(app) {
  318. if (app.exec_mode && app.exec_mode.indexOf('cluster') > -1) {
  319. Common.printError(cst.PREFIX_MSG_WARNING + chalk.bold.yellow('Choosing the Node.js version in cluster mode is not supported'));
  320. return false;
  321. }
  322. var nvm_path = cst.IS_WINDOWS ? process.env.NVM_HOME : process.env.NVM_DIR;
  323. if (!nvm_path) {
  324. Common.printError(cst.PREFIX_MSG_ERR + chalk.red('NVM is not available in PATH'));
  325. Common.printError(cst.PREFIX_MSG_ERR + chalk.red('Fallback to node in PATH'));
  326. var msg = cst.IS_WINDOWS
  327. ? 'https://github.com/coreybutler/nvm-windows/releases/'
  328. : '$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash';
  329. Common.printOut(cst.PREFIX_MSG_ERR + chalk.bold('Install NVM:\n' + msg));
  330. }
  331. else {
  332. var node_version = app.exec_interpreter.split('@')[1];
  333. var path_to_node = cst.IS_WINDOWS
  334. ? '/v' + node_version + '/node.exe'
  335. : semver.satisfies(node_version, '>= 0.12.0')
  336. ? '/versions/node/v' + node_version + '/bin/node'
  337. : '/v' + node_version + '/bin/node';
  338. var nvm_node_path = path.join(nvm_path, path_to_node);
  339. try {
  340. fs.accessSync(nvm_node_path);
  341. } catch(e) {
  342. Common.printOut(cst.PREFIX_MSG + 'Installing Node v%s', node_version);
  343. var nvm_bin = path.join(nvm_path, 'nvm.' + (cst.IS_WINDOWS ? 'exe' : 'sh'));
  344. var nvm_cmd = cst.IS_WINDOWS
  345. ? nvm_bin + ' install ' + node_version
  346. : '. ' + nvm_bin + ' ; nvm install ' + node_version;
  347. Common.printOut(cst.PREFIX_MSG + 'Executing: %s', nvm_cmd);
  348. execSync(nvm_cmd, {
  349. cwd: path.resolve(process.cwd()),
  350. env: process.env,
  351. maxBuffer: 20 * 1024 * 1024
  352. });
  353. // in order to support both arch, nvm for Windows renames 'node.exe' to:
  354. // 'node32.exe' for x32 arch
  355. // 'node64.exe' for x64 arch
  356. if (cst.IS_WINDOWS)
  357. nvm_node_path = nvm_node_path.replace(/node/, 'node' + process.arch.slice(1))
  358. }
  359. Common.printOut(cst.PREFIX_MSG + chalk.green.bold('Setting Node to v%s (path=%s)'),
  360. node_version,
  361. nvm_node_path);
  362. app.exec_interpreter = nvm_node_path;
  363. }
  364. };
  365. /**
  366. * Resolve interpreter
  367. */
  368. Common.sink.resolveInterpreter = function(app) {
  369. var noInterpreter = !app.exec_interpreter;
  370. var extName = path.extname(app.pm_exec_path);
  371. var betterInterpreter = extItps[extName];
  372. // No interpreter defined and correspondance in schema hashmap
  373. if (noInterpreter && betterInterpreter) {
  374. app.exec_interpreter = betterInterpreter;
  375. }
  376. // Else if no Interpreter detect if process is binary
  377. else if (noInterpreter)
  378. app.exec_interpreter = isBinary(app.pm_exec_path) ? 'none' : 'node';
  379. else if (app.exec_interpreter.indexOf('node@') > -1)
  380. resolveNodeInterpreter(app);
  381. if (app.exec_interpreter.indexOf('python') > -1)
  382. app.env.PYTHONUNBUFFERED = '1'
  383. /**
  384. * Specific installed JS transpilers
  385. */
  386. if (app.exec_interpreter == 'ts-node') {
  387. app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/ts-node');
  388. }
  389. if (app.exec_interpreter == 'lsc') {
  390. app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/lsc');
  391. }
  392. if (app.exec_interpreter == 'coffee') {
  393. app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/coffee');
  394. }
  395. if (app.exec_interpreter != 'none' && which(app.exec_interpreter) == null) {
  396. // If node is not present
  397. if (app.exec_interpreter == 'node') {
  398. Common.warn(`Using builtin node.js version on version ${process.version}`)
  399. app.exec_interpreter = cst.BUILTIN_NODE_PATH
  400. }
  401. else
  402. throw new Error(`Interpreter ${app.exec_interpreter} is NOT AVAILABLE in PATH. (type 'which ${app.exec_interpreter}' to double check.)`)
  403. }
  404. return app;
  405. };
  406. Common.deepCopy = Common.serialize = Common.clone = function(obj) {
  407. if (obj === null || obj === undefined) return {};
  408. return fclone(obj);
  409. };
  410. Common.errMod = function(msg) {
  411. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  412. if (msg instanceof Error)
  413. return console.error(msg.message);
  414. return console.error(`${cst.PREFIX_MSG_MOD_ERR}${msg}`);
  415. }
  416. Common.err = function(msg) {
  417. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  418. if (msg instanceof Error)
  419. return console.error(`${cst.PREFIX_MSG_ERR}${msg.message}`);
  420. return console.error(`${cst.PREFIX_MSG_ERR}${msg}`);
  421. }
  422. Common.printError = function(msg) {
  423. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  424. if (msg instanceof Error)
  425. return console.error(msg.message);
  426. return console.error.apply(console, arguments);
  427. };
  428. Common.log = function(msg) {
  429. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  430. return console.log(`${cst.PREFIX_MSG}${msg}`);
  431. }
  432. Common.info = function(msg) {
  433. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  434. return console.log(`${cst.PREFIX_MSG_INFO}${msg}`);
  435. }
  436. Common.warn = function(msg) {
  437. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  438. return console.log(`${cst.PREFIX_MSG_WARNING}${msg}`);
  439. }
  440. Common.logMod = function(msg) {
  441. if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;
  442. return console.log(`${cst.PREFIX_MSG_MOD}${msg}`);
  443. }
  444. Common.printOut = function() {
  445. if (process.env.PM2_SILENT === 'true' || process.env.PM2_PROGRAMMATIC === 'true') return false;
  446. return console.log.apply(console, arguments);
  447. };
  448. /**
  449. * Raw extend
  450. */
  451. Common.extend = function(destination, source) {
  452. if (typeof destination !== 'object') {
  453. destination = {};
  454. }
  455. if (!source || typeof source !== 'object') {
  456. return destination;
  457. }
  458. Object.keys(source).forEach(function(new_key) {
  459. if (source[new_key] != '[object Object]')
  460. destination[new_key] = source[new_key];
  461. });
  462. return destination;
  463. };
  464. /**
  465. * This is useful when starting script programmatically
  466. */
  467. Common.safeExtend = function(origin, add){
  468. if (!add || typeof add != 'object') return origin;
  469. //Ignore PM2's set environment variables from the nested env
  470. var keysToIgnore = ['name', 'exec_mode', 'env', 'args', 'pm_cwd', 'exec_interpreter', 'pm_exec_path', 'node_args', 'pm_out_log_path', 'pm_err_log_path', 'pm_pid_path', 'pm_id', 'status', 'pm_uptime', 'created_at', 'windowsHide', 'username', 'merge_logs', 'kill_retry_time', 'prev_restart_delay', 'instance_var', 'unstable_restarts', 'restart_time', 'axm_actions', 'pmx_module', 'command', 'watch', 'filter_env', 'versioning', 'vizion_runing', 'MODULE_DEBUG', 'pmx', 'axm_options', 'created_at', 'watch', 'vizion', 'axm_dynamic', 'axm_monitor', 'instances', 'automation', 'autorestart', 'unstable_restart', 'treekill', 'exit_code', 'vizion'];
  471. var keys = Object.keys(add);
  472. var i = keys.length;
  473. while (i--) {
  474. //Only copy stuff into the env that we don't have already.
  475. if(keysToIgnore.indexOf(keys[i]) == -1 && add[keys[i]] != '[object Object]')
  476. origin[keys[i]] = add[keys[i]];
  477. }
  478. return origin;
  479. };
  480. /**
  481. * Extend the app.env object of with the properties taken from the
  482. * app.env_[envName] and deploy configuration.
  483. * Also update current json attributes
  484. *
  485. * Used only for Configuration file processing
  486. *
  487. * @param {Object} app The app object.
  488. * @param {string} envName The given environment name.
  489. * @param {Object} deployConf Deployment configuration object (from JSON file or whatever).
  490. * @returns {Object} The app.env variables object.
  491. */
  492. Common.mergeEnvironmentVariables = function(app_env, env_name, deploy_conf) {
  493. var app = fclone(app_env);
  494. var new_conf = {
  495. env : {}
  496. }
  497. // Stringify possible object
  498. for (var key in app.env) {
  499. if (typeof app.env[key] == 'object') {
  500. app.env[key] = JSON.stringify(app.env[key]);
  501. }
  502. }
  503. /**
  504. * Extra configuration update
  505. */
  506. Object.assign(new_conf, app);
  507. if (env_name) {
  508. // First merge variables from deploy.production.env object as least priority.
  509. if (deploy_conf && deploy_conf[env_name] && deploy_conf[env_name]['env']) {
  510. Object.assign(new_conf.env, deploy_conf[env_name]['env']);
  511. }
  512. Object.assign(new_conf.env, app.env);
  513. // Then, last and highest priority, merge the app.env_production object.
  514. if ('env_' + env_name in app) {
  515. Object.assign(new_conf.env, app['env_' + env_name]);
  516. }
  517. else {
  518. Common.printOut(cst.PREFIX_MSG_WARNING + chalk.bold('Environment [%s] is not defined in process file'), env_name);
  519. }
  520. }
  521. delete new_conf.exec_mode
  522. var res = {
  523. current_conf: {}
  524. }
  525. Object.assign(res, new_conf.env);
  526. Object.assign(res.current_conf, new_conf);
  527. // #2541 force resolution of node interpreter
  528. if (app.exec_interpreter &&
  529. app.exec_interpreter.indexOf('@') > -1) {
  530. resolveNodeInterpreter(app);
  531. res.current_conf.exec_interpreter = app.exec_interpreter
  532. }
  533. return res
  534. }
  535. /**
  536. * This function will resolve paths, option and environment
  537. * CALLED before 'prepare' God call (=> PROCESS INITIALIZATION)
  538. * @method resolveAppAttributes
  539. * @param {Object} opts
  540. * @param {Object} opts.cwd
  541. * @param {Object} opts.pm2_home
  542. * @param {Object} appConf application configuration
  543. * @return app
  544. */
  545. Common.resolveAppAttributes = function(opts, conf) {
  546. var conf_copy = fclone(conf);
  547. var app = Common.prepareAppConf(opts, conf_copy);
  548. if (app instanceof Error) {
  549. throw new Error(app.message);
  550. }
  551. return app;
  552. }
  553. /**
  554. * Verify configurations
  555. * Called on EVERY Operation (start/restart/reload/stop...)
  556. * @param {Array} appConfs
  557. * @returns {Array}
  558. */
  559. Common.verifyConfs = function(appConfs) {
  560. if (!appConfs || appConfs.length == 0) {
  561. return [];
  562. }
  563. // Make sure it is an Array.
  564. appConfs = [].concat(appConfs);
  565. var verifiedConf = [];
  566. for (var i = 0; i < appConfs.length; i++) {
  567. var app = appConfs[i];
  568. if (app.exec_mode)
  569. app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');
  570. // JSON conf: alias cmd to script
  571. if (app.cmd && !app.script) {
  572. app.script = app.cmd
  573. delete app.cmd
  574. }
  575. // JSON conf: alias command to script
  576. if (app.command && !app.script) {
  577. app.script = app.command
  578. delete app.command
  579. }
  580. if (!app.env) {
  581. app.env = {}
  582. }
  583. // Render an app name if not existing.
  584. Common.renderApplicationName(app);
  585. if (app.execute_command == true) {
  586. app.exec_mode = 'fork'
  587. delete app.execute_command
  588. }
  589. app.username = Common.getCurrentUsername();
  590. /**
  591. * If command is like pm2 start "python xx.py --ok"
  592. * Then automatically start the script with bash -c and set a name eq to command
  593. */
  594. if (app.script && app.script.indexOf(' ') > -1 && cst.IS_WINDOWS === false) {
  595. var _script = app.script;
  596. if (which('bash')) {
  597. app.script = 'bash';
  598. app.args = ['-c', _script];
  599. if (!app.name) {
  600. app.name = _script
  601. }
  602. }
  603. else if (which('sh')) {
  604. app.script = 'sh';
  605. app.args = ['-c', _script];
  606. if (!app.name) {
  607. app.name = _script
  608. }
  609. }
  610. else {
  611. warn('bash or sh not available in $PATH, keeping script as is')
  612. }
  613. }
  614. /**
  615. * Add log_date_format by default
  616. */
  617. if (app.time || process.env.ASZ_MODE) {
  618. app.log_date_format = 'YYYY-MM-DDTHH:mm:ss'
  619. }
  620. /**
  621. * Checks + Resolve UID/GID
  622. * comes from pm2 --uid <> --gid <> or --user
  623. */
  624. if (app.uid || app.gid || app.user) {
  625. // 1/ Check if windows
  626. if (cst.IS_WINDOWS === true) {
  627. Common.printError(cst.PREFIX_MSG_ERR + '--uid and --git does not works on windows');
  628. return new Error('--uid and --git does not works on windows');
  629. }
  630. // 2/ Verify that user is root (todo: verify if other has right)
  631. if (process.env.NODE_ENV != 'test' && process.getuid && process.getuid() !== 0) {
  632. Common.printError(cst.PREFIX_MSG_ERR + 'To use --uid and --gid please run pm2 as root');
  633. return new Error('To use UID and GID please run PM2 as root');
  634. }
  635. // 3/ Resolve user info via /etc/password
  636. var passwd = require('./tools/passwd.js')
  637. var users
  638. try {
  639. users = passwd.getUsers()
  640. } catch(e) {
  641. Common.printError(e);
  642. return new Error(e);
  643. }
  644. var user_info = users[app.uid || app.user]
  645. if (!user_info) {
  646. Common.printError(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);
  647. return new Error(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);
  648. }
  649. app.env.HOME = user_info.homedir
  650. app.uid = parseInt(user_info.userId)
  651. // 4/ Resolve group id if gid is specified
  652. if (app.gid) {
  653. var groups
  654. try {
  655. groups = passwd.getGroups()
  656. } catch(e) {
  657. Common.printError(e);
  658. return new Error(e);
  659. }
  660. var group_info = groups[app.gid]
  661. if (!group_info) {
  662. Common.printError(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);
  663. return new Error(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);
  664. }
  665. app.gid = parseInt(group_info.id)
  666. } else {
  667. app.gid = parseInt(user_info.groupId)
  668. }
  669. }
  670. /**
  671. * Specific options of PM2.io
  672. */
  673. if (process.env.PM2_DEEP_MONITORING) {
  674. app.deep_monitoring = true;
  675. }
  676. if (app.automation == false) {
  677. app.pmx = false;
  678. }
  679. if (app.disable_trace) {
  680. app.trace = false
  681. delete app.disable_trace;
  682. }
  683. /**
  684. * Instances params
  685. */
  686. if (app.instances == 'max') {
  687. app.instances = 0;
  688. }
  689. if (typeof(app.instances) === 'string') {
  690. app.instances = parseInt(app.instances) || 0;
  691. }
  692. if (app.exec_mode != 'cluster_mode' &&
  693. !app.instances &&
  694. typeof(app.merge_logs) == 'undefined') {
  695. app.merge_logs = true;
  696. }
  697. var ret;
  698. if (app.cron_restart) {
  699. if ((ret = Common.sink.determineCron(app)) instanceof Error)
  700. return ret;
  701. }
  702. /**
  703. * Now validation configuration
  704. */
  705. var ret = Config.validateJSON(app);
  706. if (ret.errors && ret.errors.length > 0){
  707. ret.errors.forEach(function(err) { warn(err) });
  708. return new Error(ret.errors);
  709. }
  710. verifiedConf.push(ret.config);
  711. }
  712. return verifiedConf;
  713. }
  714. /**
  715. * Get current username
  716. * Called on EVERY starting app
  717. *
  718. * @returns {String}
  719. */
  720. Common.getCurrentUsername = function(){
  721. var current_user = '';
  722. if (os.userInfo) {
  723. try {
  724. current_user = os.userInfo().username;
  725. } catch (err) {
  726. // For the case of unhandled error for uv_os_get_passwd
  727. // https://github.com/Unitech/pm2/issues/3184
  728. }
  729. }
  730. if(current_user === '') {
  731. current_user = process.env.USER || process.env.LNAME || process.env.USERNAME || process.env.SUDO_USER || process.env.C9_USER || process.env.LOGNAME;
  732. }
  733. return current_user;
  734. }
  735. /**
  736. * Render an app name if not existing.
  737. * @param {Object} conf
  738. */
  739. Common.renderApplicationName = function(conf){
  740. if (!conf.name && conf.script){
  741. conf.name = conf.script !== undefined ? path.basename(conf.script) : 'undefined';
  742. var lastDot = conf.name.lastIndexOf('.');
  743. if (lastDot > 0){
  744. conf.name = conf.name.slice(0, lastDot);
  745. }
  746. }
  747. }
  748. /**
  749. * Show warnings
  750. * @param {String} warning
  751. */
  752. function warn(warning){
  753. Common.printOut(cst.PREFIX_MSG_WARNING + warning);
  754. }