UNPKG

8.96 kBJavaScriptView Raw
1/**
2 * Copyright 2013-2021 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 if (semver.lt(process.version, '10.0.0')) {
59 args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerForkLegacy.js'));
60 }
61 else {
62 args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerFork.js'));
63 }
64 }
65 else
66 args.push(pm2_env.pm_exec_path);
67 }
68 else {
69 command = pm2_env.pm_exec_path;
70 args = [ ];
71 }
72
73 if (pm2_env.args) {
74 args = args.concat(pm2_env.args);
75 }
76
77 // piping stream o file
78 var stds = {
79 out: pm2_env.pm_out_log_path,
80 err: pm2_env.pm_err_log_path
81 };
82
83 // entire log std if necessary.
84 if ('pm_log_path' in pm2_env){
85 stds.std = pm2_env.pm_log_path;
86 }
87
88 log("stds: %j", stds);
89
90 Utility.startLogging(stds, function(err, result) {
91 if (err) {
92 God.logAndGenerateError(err);
93 return cb(err);
94 };
95
96 try {
97 var options = {
98 env : pm2_env,
99 detached : true,
100 cwd : pm2_env.pm_cwd || process.cwd(),
101 stdio : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core
102 }
103
104 if (typeof(pm2_env.windowsHide) === "boolean") {
105 options.windowsHide = pm2_env.windowsHide;
106 } else {
107 options.windowsHide = true;
108 }
109
110 if (pm2_env.uid) {
111 options.uid = pm2_env.uid
112 }
113
114 if (pm2_env.gid) {
115 options.gid = pm2_env.gid
116 }
117
118 var cspr = spawn(command, args, options);
119 } catch(e) {
120 God.logAndGenerateError(e);
121 return cb(e);
122 }
123
124 if (!cspr || !cspr.stderr || !cspr.stdout) {
125 var fatalError = new Error('Process could not be forked properly, check your system health')
126 God.logAndGenerateError(fatalError);
127 return cb(fatalError);
128 }
129
130 cspr.process = {};
131 cspr.process.pid = cspr.pid;
132 cspr.pm2_env = pm2_env;
133
134 function transformLogToJson(pm2_env, type, data) {
135 return JSON.stringify({
136 message : data.toString(),
137 timestamp : pm2_env.log_date_format ? dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),
138 type : type,
139 process_id : cspr.pm2_env.pm_id,
140 app_name : cspr.pm2_env.name
141 }) + '\n'
142 }
143
144 function prefixLogWithDate(pm2_env, data) {
145 var log_data = []
146 log_data = data.toString().split('\n')
147 if (log_data.length > 1)
148 log_data.pop()
149 log_data = log_data.map(line => `${dayjs().format(pm2_env.log_date_format)}: ${line}\n`)
150 log_data = log_data.join('')
151 return log_data
152 }
153
154 cspr.stderr.on('data', function forkErrData(data) {
155 var log_data = null;
156
157 // via --out /dev/null --err /dev/null
158 if (pm2_env.disable_logs === true) return false;
159
160 if (pm2_env.log_type && pm2_env.log_type === 'json')
161 log_data = transformLogToJson(pm2_env, 'err', data)
162 else if (pm2_env.log_date_format)
163 log_data = prefixLogWithDate(pm2_env, data)
164 else
165 log_data = data.toString();
166
167 God.bus.emit('log:err', {
168 process : {
169 pm_id : cspr.pm2_env.pm_id,
170 name : cspr.pm2_env.name,
171 rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
172 namespace : cspr.pm2_env.namespace
173 },
174 at : Utility.getDate(),
175 data : log_data
176 });
177
178 if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&
179 (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path))) {
180 return false;
181 }
182
183 stds.std && stds.std.write && stds.std.write(log_data);
184 stds.err && stds.err.write && stds.err.write(log_data);
185 });
186
187 cspr.stdout.on('data', function forkOutData(data) {
188 var log_data = null;
189
190 if (pm2_env.disable_logs === true)
191 return false;
192
193 if (pm2_env.log_type && pm2_env.log_type === 'json')
194 log_data = transformLogToJson(pm2_env, 'out', data)
195 else if (pm2_env.log_date_format)
196 log_data = prefixLogWithDate(pm2_env, data)
197 else
198 log_data = data.toString()
199
200 God.bus.emit('log:out', {
201 process : {
202 pm_id : cspr.pm2_env.pm_id,
203 name : cspr.pm2_env.name,
204 rev : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,
205 namespace : cspr.pm2_env.namespace
206 },
207 at : Utility.getDate(),
208 data : log_data
209 });
210
211 if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&
212 (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))
213 return false;
214
215 stds.std && stds.std.write && stds.std.write(log_data);
216 stds.out && stds.out.write && stds.out.write(log_data);
217 });
218
219 /**
220 * Broadcast message to God
221 */
222 cspr.on('message', function forkMessage(msg) {
223 /*********************************
224 * If you edit this function
225 * Do the same in ClusterMode.js !
226 *********************************/
227 if (msg.data && msg.type) {
228 process.nextTick(function() {
229 return God.bus.emit(msg.type ? msg.type : 'process:msg', {
230 at : Utility.getDate(),
231 data : msg.data,
232 process : {
233 pm_id : cspr.pm2_env.pm_id,
234 name : cspr.pm2_env.name,
235 versioning : cspr.pm2_env.versioning,
236 namespace : cspr.pm2_env.namespace
237 }
238 });
239 });
240 }
241 else {
242
243 if (typeof msg == 'object' && 'node_version' in msg) {
244 cspr.pm2_env.node_version = msg.node_version;
245 return false;
246 }
247
248 return God.bus.emit('process:msg', {
249 at : Utility.getDate(),
250 raw : msg,
251 process : {
252 pm_id : cspr.pm2_env.pm_id,
253 name : cspr.pm2_env.name,
254 namespace : cspr.pm2_env.namespace
255 }
256 });
257 }
258 });
259
260 try {
261 var pid = cspr.pid
262 if (typeof(pid) !== 'undefined')
263 fs.writeFileSync(pidFile, pid.toString());
264 } catch (e) {
265 console.error(e.stack || e);
266 }
267
268 cspr.once('exit', function forkClose(status) {
269 try {
270 for(var k in stds){
271 if (stds[k] && stds[k].destroy) stds[k].destroy();
272 else if (stds[k] && stds[k].end) stds[k].end();
273 else if (stds[k] && stds[k].close) stds[k].close();
274 stds[k] = stds[k]._file;
275 }
276 } catch(e) { God.logAndGenerateError(e);}
277 });
278
279 cspr._reloadLogs = function(cb) {
280 try {
281 for (var k in stds){
282 if (stds[k] && stds[k].destroy) stds[k].destroy();
283 else if (stds[k] && stds[k].end) stds[k].end();
284 else if (stds[k] && stds[k].close) stds[k].close();
285 stds[k] = stds[k]._file;
286 }
287 } catch(e) { God.logAndGenerateError(e);}
288 //cspr.removeAllListeners();
289 Utility.startLogging(stds, cb);
290 };
291
292 cspr.unref();
293
294 return cb(null, cspr);
295 });
296
297 };
298};