1'use strict';
2
3const { ObjectDefineProperty } = primordials;
4const rawMethods = internalBinding('process_methods');
5const {
6  namespace: {
7    addSerializeCallback,
8    isBuildingSnapshot,
9  },
10} = require('internal/v8/startup_snapshot');
11// TODO(joyeecheung): deprecate and remove these underscore methods
12process._debugProcess = rawMethods._debugProcess;
13process._debugEnd = rawMethods._debugEnd;
14
15// See the discussion in https://github.com/nodejs/node/issues/19009 and
16// https://github.com/nodejs/node/pull/34010 for why these are no-ops.
17// Five word summary: they were broken beyond repair.
18process._startProfilerIdleNotifier = () => {};
19process._stopProfilerIdleNotifier = () => {};
20
21function defineStream(name, getter) {
22  ObjectDefineProperty(process, name, {
23    __proto__: null,
24    configurable: true,
25    enumerable: true,
26    get: getter,
27  });
28}
29
30defineStream('stdout', getStdout);
31defineStream('stdin', getStdin);
32defineStream('stderr', getStderr);
33
34// Worker threads don't receive signals.
35const {
36  startListeningIfSignal,
37  stopListeningIfSignal,
38} = require('internal/process/signal');
39process.on('newListener', startListeningIfSignal);
40process.on('removeListener', stopListeningIfSignal);
41
42// ---- keep the attachment of the wrappers above so that it's easier to ----
43// ----              compare the setups side-by-side                    -----
44
45const { guessHandleType } = internalBinding('util');
46
47function createWritableStdioStream(fd) {
48  let stream;
49  // Note stream._type is used for test-module-load-list.js
50  switch (guessHandleType(fd)) {
51    case 'TTY': {
52      const tty = require('tty');
53      stream = new tty.WriteStream(fd);
54      stream._type = 'tty';
55      break;
56    }
57
58    case 'FILE': {
59      const SyncWriteStream = require('internal/fs/sync_write_stream');
60      stream = new SyncWriteStream(fd, { autoClose: false });
61      stream._type = 'fs';
62      break;
63    }
64
65    case 'PIPE':
66    case 'TCP': {
67      const net = require('net');
68
69      // If fd is already being used for the IPC channel, libuv will return
70      // an error when trying to use it again. In that case, create the socket
71      // using the existing handle instead of the fd.
72      if (process.channel && process.channel.fd === fd) {
73        const { kChannelHandle } = require('internal/child_process');
74        stream = new net.Socket({
75          handle: process[kChannelHandle],
76          readable: false,
77          writable: true,
78        });
79      } else {
80        stream = new net.Socket({
81          fd,
82          readable: false,
83          writable: true,
84        });
85      }
86
87      stream._type = 'pipe';
88      break;
89    }
90
91    default: {
92      // Provide a dummy black-hole output for e.g. non-console
93      // Windows applications.
94      const { Writable } = require('stream');
95      stream = new Writable({
96        write(buf, enc, cb) {
97          cb();
98        },
99      });
100    }
101  }
102
103  // For supporting legacy API we put the FD here.
104  stream.fd = fd;
105
106  stream._isStdio = true;
107
108  return stream;
109}
110
111function dummyDestroy(err, cb) {
112  cb(err);
113  this._undestroy();
114
115  // We need to emit 'close' anyway so that the closing
116  // of the stream is observable. We just make sure we
117  // are not going to do it twice.
118  // The 'close' event is needed so that finished and
119  // pipeline work correctly.
120  if (!this._writableState.emitClose) {
121    process.nextTick(() => {
122      this.emit('close');
123    });
124  }
125}
126
127let stdin;
128let stdout;
129let stderr;
130
131let stdoutDestroy;
132let stderrDestroy;
133
134function refreshStdoutOnSigWinch() {
135  stdout._refreshSize();
136}
137
138function refreshStderrOnSigWinch() {
139  stderr._refreshSize();
140}
141
142function addCleanup(fn) {
143  if (isBuildingSnapshot()) {
144    addSerializeCallback(fn);
145  }
146}
147
148function getStdout() {
149  if (stdout) return stdout;
150  stdout = createWritableStdioStream(1);
151  stdout.destroySoon = stdout.destroy;
152  // Override _destroy so that the fd is never actually closed.
153  stdoutDestroy = stdout._destroy;
154  stdout._destroy = dummyDestroy;
155  if (stdout.isTTY) {
156    process.on('SIGWINCH', refreshStdoutOnSigWinch);
157  }
158
159  addCleanup(function cleanupStdout() {
160    stdout._destroy = stdoutDestroy;
161    stdout.destroy();
162    process.removeListener('SIGWINCH', refreshStdoutOnSigWinch);
163    stdout = undefined;
164  });
165  // No need to add deserialize callback because stdout = undefined above
166  // causes the stream to be lazily initialized again later.
167  return stdout;
168}
169
170function getStderr() {
171  if (stderr) return stderr;
172  stderr = createWritableStdioStream(2);
173  stderr.destroySoon = stderr.destroy;
174  stderrDestroy = stderr._destroy;
175  // Override _destroy so that the fd is never actually closed.
176  stderr._destroy = dummyDestroy;
177  if (stderr.isTTY) {
178    process.on('SIGWINCH', refreshStderrOnSigWinch);
179  }
180  addCleanup(function cleanupStderr() {
181    stderr._destroy = stderrDestroy;
182    stderr.destroy();
183    process.removeListener('SIGWINCH', refreshStderrOnSigWinch);
184    stderr = undefined;
185  });
186  // No need to add deserialize callback because stderr = undefined above
187  // causes the stream to be lazily initialized again later.
188  return stderr;
189}
190
191function getStdin() {
192  if (stdin) return stdin;
193  const fd = 0;
194
195  switch (guessHandleType(fd)) {
196    case 'TTY': {
197      const tty = require('tty');
198      stdin = new tty.ReadStream(fd);
199      break;
200    }
201
202    case 'FILE': {
203      const fs = require('fs');
204      stdin = new fs.ReadStream(null, { fd: fd, autoClose: false });
205      break;
206    }
207
208    case 'PIPE':
209    case 'TCP': {
210      const net = require('net');
211
212      // It could be that process has been started with an IPC channel
213      // sitting on fd=0, in such case the pipe for this fd is already
214      // present and creating a new one will lead to the assertion failure
215      // in libuv.
216      if (process.channel && process.channel.fd === fd) {
217        stdin = new net.Socket({
218          handle: process.channel,
219          readable: true,
220          writable: false,
221          manualStart: true,
222        });
223      } else {
224        stdin = new net.Socket({
225          fd: fd,
226          readable: true,
227          writable: false,
228          manualStart: true,
229        });
230      }
231      // Make sure the stdin can't be `.end()`-ed
232      stdin._writableState.ended = true;
233      break;
234    }
235
236    default: {
237      // Provide a dummy contentless input for e.g. non-console
238      // Windows applications.
239      const { Readable } = require('stream');
240      stdin = new Readable({ read() {} });
241      stdin.push(null);
242    }
243  }
244
245  // For supporting legacy API we put the FD here.
246  stdin.fd = fd;
247
248  // `stdin` starts out life in a paused state, but node doesn't
249  // know yet. Explicitly to readStop() it to put it in the
250  // not-reading state.
251  if (stdin._handle && stdin._handle.readStop) {
252    stdin._handle.reading = false;
253    stdin._readableState.reading = false;
254    stdin._handle.readStop();
255  }
256
257  // If the user calls stdin.pause(), then we need to stop reading
258  // once the stream implementation does so (one nextTick later),
259  // so that the process can close down.
260  stdin.on('pause', () => {
261    process.nextTick(onpause);
262  });
263
264  function onpause() {
265    if (!stdin._handle)
266      return;
267    if (stdin._handle.reading && !stdin.readableFlowing) {
268      stdin._readableState.reading = false;
269      stdin._handle.reading = false;
270      stdin._handle.readStop();
271    }
272  }
273
274  addCleanup(function cleanupStdin() {
275    stdin.destroy();
276    stdin = undefined;
277  });
278  // No need to add deserialize callback because stdin = undefined above
279  // causes the stream to be lazily initialized again later.
280  return stdin;
281}
282
283// Used by internal tests.
284rawMethods.resetStdioForTesting = function() {
285  stdin = undefined;
286  stdout = undefined;
287  stderr = undefined;
288};
289
290// Needed by the module loader and generally needed everywhere.
291require('fs');
292require('util');
293require('url');
294
295require('internal/modules/cjs/loader');
296require('internal/modules/esm/utils');
297require('internal/vm/module');
298// Needed to refresh the time origin.
299require('internal/perf/utils');
300// Needed to register the async hooks.
301if (internalBinding('config').hasInspector) {
302  require('internal/inspector_async_hook');
303}
304// Needed to set the wasm web API callbacks.
305internalBinding('wasm_web_api');
306// Needed to detect whether it's on main thread.
307internalBinding('worker');
308// Needed by most execution modes.
309require('internal/modules/run_main');
310// Needed to refresh DNS configurations.
311require('internal/dns/utils');
312// Needed by almost all execution modes. It's fine to
313// load them into the snapshot as long as we don't run
314// any of the initialization.
315require('internal/process/pre_execution');
316