API.js 59 KB

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