UNPKG

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