ForkMode.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. /**
  8. * @file Fork execution related functions
  9. * @author Alexandre Strzelewicz <as@unitech.io>
  10. * @project PM2
  11. */
  12. var log = require('debug')('pm2:fork_mode');
  13. var fs = require('fs');
  14. var Utility = require('../Utility.js');
  15. var path = require('path');
  16. var dayjs = require('dayjs');
  17. var semver = require('semver')
  18. /**
  19. * Description
  20. * @method exports
  21. * @param {} God
  22. * @return
  23. */
  24. module.exports = function ForkMode(God) {
  25. /**
  26. * For all apps - FORK MODE
  27. * fork the app
  28. * @method forkMode
  29. * @param {} pm2_env
  30. * @param {} cb
  31. * @return
  32. */
  33. God.forkMode = function forkMode(pm2_env, cb) {
  34. var command = '';
  35. var args = [];
  36. console.log(`App [${pm2_env.name}:${pm2_env.pm_id}] starting in -fork mode-`)
  37. var spawn = require('child_process').spawn;
  38. var interpreter = pm2_env.exec_interpreter || 'node';
  39. var pidFile = pm2_env.pm_pid_path;
  40. if (interpreter !== 'none') {
  41. command = interpreter;
  42. if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {
  43. args = args.concat(pm2_env.node_args);
  44. }
  45. // Deprecated - to remove at some point
  46. if (process.env.PM2_NODE_OPTIONS) {
  47. args = args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
  48. }
  49. if (interpreter === 'node' || RegExp('node$').test(interpreter)) {
  50. args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerFork.js'));
  51. }
  52. else
  53. args.push(pm2_env.pm_exec_path);
  54. }
  55. else {
  56. command = pm2_env.pm_exec_path;
  57. args = [ ];
  58. }
  59. if (pm2_env.args) {
  60. args = args.concat(pm2_env.args);
  61. }
  62. // piping stream o file
  63. var stds = {
  64. out: pm2_env.pm_out_log_path,
  65. err: pm2_env.pm_err_log_path
  66. };
  67. // entire log std if necessary.
  68. if ('pm_log_path' in pm2_env){
  69. stds.std = pm2_env.pm_log_path;
  70. }
  71. log("stds: %j", stds);
  72. Utility.startLogging(stds, function(err, result) {
  73. if (err) {
  74. God.logAndGenerateError(err);
  75. return cb(err);
  76. };
  77. try {
  78. var options = {
  79. env : pm2_env,
  80. detached : true,
  81. cwd : pm2_env.pm_cwd || process.cwd(),
  82. stdio : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core
  83. }
  84. if (typeof(pm2_env.windowsHide) === "boolean") {
  85. options.windowsHide = pm2_env.windowsHide;
  86. } else {
  87. options.windowsHide = true;
  88. }
  89. if (pm2_env.uid) {
  90. options.uid = pm2_env.uid
  91. }
  92. if (pm2_env.gid) {
  93. options.gid = pm2_env.gid
  94. }
  95. var cspr = spawn(command, args, options);
  96. } catch(e) {
  97. God.logAndGenerateError(e);
  98. return cb(e);
  99. }
  100. if (!cspr || !cspr.stderr || !cspr.stdout) {
  101. var fatalError = new Error('Process could not be forked properly, check your system health')
  102. God.logAndGenerateError(fatalError);
  103. return cb(fatalError);
  104. }
  105. cspr.process = {};
  106. cspr.process.pid = cspr.pid;
  107. cspr.pm2_env = pm2_env;
  108. function transformLogToJson(pm2_env, type, data) {
  109. return JSON.stringify({
  110. message : data.toString(),
  111. timestamp : pm2_env.log_date_format ? dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
  112. type : type,
  113. process_id : cspr.pm2_env.pm_id,
  114. app_name : cspr.pm2_env.name
  115. }) + '\n'
  116. }
  117. function prefixLogWithDate(pm2_env, data) {
  118. var log_data = []
  119. log_data = data.toString().split('\n')
  120. if (log_data.length > 1)
  121. log_data.pop()
  122. log_data = log_data.map(line => `${dayjs().format(pm2_env.log_date_format)}: ${line}\n`)
  123. log_data = log_data.join('')
  124. return log_data
  125. }
  126. cspr.stderr.on('data', function forkErrData(data) {
  127. var log_data = null;
  128. // via --out /dev/null --err /dev/null
  129. if (pm2_env.disable_logs === true) return false;
  130. if (pm2_env.log_type && pm2_env.log_type === 'json')
  131. log_data = transformLogToJson(pm2_env, 'err', data)
  132. else if (pm2_env.log_date_format)
  133. log_data = prefixLogWithDate(pm2_env, data)
  134. else
  135. log_data = data.toString();
  136. God.bus.emit('log:err', {
  137. process : {
  138. pm_id : cspr.pm2_env.pm_id,
  139. name : cspr.pm2_env.name,
  140. rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
  141. namespace : cspr.pm2_env.namespace
  142. },
  143. at : Utility.getDate(),
  144. data : log_data
  145. });
  146. if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&
  147. (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path))) {
  148. return false;
  149. }
  150. stds.std && stds.std.write && stds.std.write(log_data);
  151. stds.err && stds.err.write && stds.err.write(log_data);
  152. });
  153. cspr.stdout.on('data', function forkOutData(data) {
  154. var log_data = null;
  155. if (pm2_env.disable_logs === true)
  156. return false;
  157. if (pm2_env.log_type && pm2_env.log_type === 'json')
  158. log_data = transformLogToJson(pm2_env, 'out', data)
  159. else if (pm2_env.log_date_format)
  160. log_data = prefixLogWithDate(pm2_env, data)
  161. else
  162. log_data = data.toString()
  163. God.bus.emit('log:out', {
  164. process : {
  165. pm_id : cspr.pm2_env.pm_id,
  166. name : cspr.pm2_env.name,
  167. rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
  168. namespace : cspr.pm2_env.namespace
  169. },
  170. at : Utility.getDate(),
  171. data : log_data
  172. });
  173. if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
  174. (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
  175. return false;
  176. stds.std && stds.std.write && stds.std.write(log_data);
  177. stds.out && stds.out.write && stds.out.write(log_data);
  178. });
  179. /**
  180. * Broadcast message to God
  181. */
  182. cspr.on('message', function forkMessage(msg) {
  183. /*********************************
  184. * If you edit this function
  185. * Do the same in ClusterMode.js !
  186. *********************************/
  187. if (msg.data && msg.type) {
  188. process.nextTick(function() {
  189. return God.bus.emit(msg.type ? msg.type : 'process:msg', {
  190. at : Utility.getDate(),
  191. data : msg.data,
  192. process : {
  193. pm_id : cspr.pm2_env.pm_id,
  194. name : cspr.pm2_env.name,
  195. versioning : cspr.pm2_env.versioning,
  196. namespace : cspr.pm2_env.namespace
  197. }
  198. });
  199. });
  200. }
  201. else {
  202. if (typeof msg == 'object' && 'node_version' in msg) {
  203. cspr.pm2_env.node_version = msg.node_version;
  204. return false;
  205. }
  206. return God.bus.emit('process:msg', {
  207. at : Utility.getDate(),
  208. raw : msg,
  209. process : {
  210. pm_id : cspr.pm2_env.pm_id,
  211. name : cspr.pm2_env.name,
  212. namespace : cspr.pm2_env.namespace
  213. }
  214. });
  215. }
  216. });
  217. try {
  218. var pid = cspr.pid
  219. if (typeof(pid) !== 'undefined')
  220. fs.writeFileSync(pidFile, pid.toString());
  221. } catch (e) {
  222. console.error(e.stack || e);
  223. }
  224. cspr.once('exit', function forkClose(status) {
  225. try {
  226. for(var k in stds){
  227. if (stds[k] && stds[k].destroy) stds[k].destroy();
  228. else if (stds[k] && stds[k].end) stds[k].end();
  229. else if (stds[k] && stds[k].close) stds[k].close();
  230. stds[k] = stds[k]._file;
  231. }
  232. } catch(e) { God.logAndGenerateError(e);}
  233. });
  234. cspr._reloadLogs = function(cb) {
  235. try {
  236. for (var k in stds){
  237. if (stds[k] && stds[k].destroy) stds[k].destroy();
  238. else if (stds[k] && stds[k].end) stds[k].end();
  239. else if (stds[k] && stds[k].close) stds[k].close();
  240. stds[k] = stds[k]._file;
  241. }
  242. } catch(e) { God.logAndGenerateError(e);}
  243. //cspr.removeAllListeners();
  244. Utility.startLogging(stds, cb);
  245. };
  246. cspr.unref();
  247. return cb(null, cspr);
  248. });
  249. };
  250. };