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