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