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