-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
Expand file tree
/
Copy pathchild_process.js
More file actions
924 lines (756 loc) Β· 25.7 KB
/
child_process.js
File metadata and controls
924 lines (756 loc) Β· 25.7 KB
Edit and raw actions
OlderNewer
Β
1
'use strict';
2
3
const errors = require('internal/errors');
4
const { StringDecoder } = require('string_decoder');
5
const EventEmitter = require('events');
6
const net = require('net');
7
const dgram = require('dgram');
8
const util = require('util');
9
const assert = require('assert');
10
const uv = process.binding('uv');
11
const { Process } = process.binding('process_wrap');
12
const { WriteWrap } = process.binding('stream_wrap');
13
const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap');
14
const { TTY } = process.binding('tty_wrap');
15
const { TCP } = process.binding('tcp_wrap');
16
const { UDP } = process.binding('udp_wrap');
17
const SocketList = require('internal/socket_list');
18
const { convertToValidSignal } = require('internal/util');
19
const { isUint8Array } = require('internal/util/types');
20
21
const errnoException = errors.errnoException;
22
const { SocketListSend, SocketListReceive } = SocketList;
23
24
const MAX_HANDLE_RETRANSMISSIONS = 3;
25
26
// this object contain function to convert TCP objects to native handle objects
27
// and back again.
28
const handleConversion = {
29
'net.Native': {
30
simultaneousAccepts: true,
31
32
send: function(message, handle, options) {
33
return handle;
34
},
35
36
got: function(message, handle, emit) {
37
emit(handle);
38
}
39
},
40
41
'net.Server': {
42
simultaneousAccepts: true,
43
44
send: function(message, server, options) {
45
return server._handle;
46
},
47
48
got: function(message, handle, emit) {
49
var server = new net.Server();
50
server.listen(handle, function() {
51
emit(server);
52
});
53
}
54
},
55
56
'net.Socket': {
57
send: function(message, socket, options) {
58
if (!socket._handle)
59
return;
60
61
// if the socket was created by net.Server
62
if (socket.server) {
63
// the slave should keep track of the socket
64
message.key = socket.server._connectionKey;
65
66
var firstTime = !this.channel.sockets.send[message.key];
67
var socketList = getSocketList('send', this, message.key);
68
69
// the server should no longer expose a .connection property
70
// and when asked to close it should query the socket status from
71
// the slaves
72
if (firstTime) socket.server._setupSlave(socketList);
73
74
// Act like socket is detached
75
if (!options.keepOpen)
76
socket.server._connections--;
77
}
78
79
var handle = socket._handle;
80
81
// remove handle from socket object, it will be closed when the socket
82
// will be sent
83
if (!options.keepOpen) {
84
handle.onread = nop;
85
socket._handle = null;
86
}
87
88
return handle;
89
},
90
91
postSend: function(message, handle, options, callback, target) {
92
// Store the handle after successfully sending it, so it can be closed
93
// when the NODE_HANDLE_ACK is received. If the handle could not be sent,
94
// just close it.
95
if (handle && !options.keepOpen) {
96
if (target) {
97
// There can only be one _pendingMessage as passing handles are
98
// processed one at a time: handles are stored in _handleQueue while
99
// waiting for the NODE_HANDLE_ACK of the current passing handle.
100
assert(!target._pendingMessage);
101
target._pendingMessage =
102
{ callback, message, handle, options, retransmissions: 0 };
103
} else {
104
handle.close();
105
}
106
}
107
},
108
109
got: function(message, handle, emit) {
110
var socket = new net.Socket({ handle: handle });
111
socket.readable = socket.writable = true;
112
113
// if the socket was created by net.Server we will track the socket
114
if (message.key) {
115
116
// add socket to connections list
117
var socketList = getSocketList('got', this, message.key);
118
socketList.add({
119
socket: socket
120
});
121
}
122
123
emit(socket);
124
}
125
},
126
127
'dgram.Native': {
128
simultaneousAccepts: false,
129
130
send: function(message, handle, options) {
131
return handle;
132
},
133
134
got: function(message, handle, emit) {
135
emit(handle);
136
}
137
},
138
139
'dgram.Socket': {
140
simultaneousAccepts: false,
141
142
send: function(message, socket, options) {
143
message.dgramType = socket.type;
144
145
return socket._handle;
146
},
147
148
got: function(message, handle, emit) {
149
var socket = new dgram.Socket(message.dgramType);
150
151
socket.bind(handle, function() {
152
emit(socket);
153
});
154
}
155
}
156
};
157
158
159
function ChildProcess() {
160
EventEmitter.call(this);
161
162
this._closesNeeded = 1;
163
this._closesGot = 0;
164
this.connected = false;
165
166
this.signalCode = null;
167
this.exitCode = null;
168
this.killed = false;
169
this.spawnfile = null;
170
171
this._handle = new Process();
172
this._handle.owner = this;
173
174
this._handle.onexit = (exitCode, signalCode) => {
175
if (signalCode) {
176
this.signalCode = signalCode;
177
} else {
178
this.exitCode = exitCode;
179
}
180
181
if (this.stdin) {
182
this.stdin.destroy();
183
}
184
185
this._handle.close();
186
this._handle = null;
187
188
if (exitCode < 0) {
189
var syscall = this.spawnfile ? 'spawn ' + this.spawnfile : 'spawn';
190
const err = errnoException(exitCode, syscall);
191
192
if (this.spawnfile)
193
err.path = this.spawnfile;
194
195
err.spawnargs = this.spawnargs.slice(1);
196
this.emit('error', err);
197
} else {
198
this.emit('exit', this.exitCode, this.signalCode);
199
}
200
201
// if any of the stdio streams have not been touched,
202
// then pull all the data through so that it can get the
203
// eof and emit a 'close' event.
204
// Do it on nextTick so that the user has one last chance
205
// to consume the output, if for example they only want to
206
// start reading the data once the process exits.
207
process.nextTick(flushStdio, this);
208
209
maybeClose(this);
210
};
211
}
212
util.inherits(ChildProcess, EventEmitter);
213
214
215
function flushStdio(subprocess) {
216
const stdio = subprocess.stdio;
217
218
if (stdio == null) return;
219
220
for (var i = 0; i < stdio.length; i++) {
221
const stream = stdio[i];
222
if (!stream || !stream.readable || stream._readableState.readableListening)
223
continue;
224
stream.resume();
225
}
226
}
227
228
229
function createSocket(pipe, readable) {
230
return net.Socket({ handle: pipe, readable, writable: !readable });
231
}
232
233
234
function getHandleWrapType(stream) {
235
if (stream instanceof Pipe) return 'pipe';
236
if (stream instanceof TTY) return 'tty';
237
if (stream instanceof TCP) return 'tcp';
238
if (stream instanceof UDP) return 'udp';
239
240
return false;
241
}
242
243
function closePendingHandle(target) {
244
target._pendingMessage.handle.close();
245
target._pendingMessage = null;
246
}
247
248
249
ChildProcess.prototype.spawn = function(options) {
250
var ipc;
251
var ipcFd;
252
var i;
253
254
if (options === null || typeof options !== 'object')
255
throw new TypeError('"options" must be an object');
256
257
// If no `stdio` option was given - use default
258
var stdio = options.stdio || 'pipe';
259
260
stdio = _validateStdio(stdio, false);
261
262
ipc = stdio.ipc;
263
ipcFd = stdio.ipcFd;
264
stdio = options.stdio = stdio.stdio;
265
266
if (ipc !== undefined) {
267
// Let child process know about opened IPC channel
268
if (options.envPairs === undefined)
269
options.envPairs = [];
270
else if (!Array.isArray(options.envPairs))
271
throw new TypeError('"envPairs" must be an array');
272
273
options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
274
}
275
276
if (typeof options.file === 'string')
277
this.spawnfile = options.file;
278
else
279
throw new TypeError('"file" must be a string');
280
281
if (Array.isArray(options.args))
282
this.spawnargs = options.args;
283
else if (options.args === undefined)
284
this.spawnargs = [];
285
else
286
throw new TypeError('"args" must be an array');
287
288
var err = this._handle.spawn(options);
289
290
// Run-time errors should emit an error, not throw an exception.
291
if (err === uv.UV_EAGAIN ||
292
err === uv.UV_EMFILE ||
293
err === uv.UV_ENFILE ||
294
err === uv.UV_ENOENT) {
295
process.nextTick(onErrorNT, this, err);
296
// There is no point in continuing when we've hit EMFILE or ENFILE
297
// because we won't be able to set up the stdio file descriptors.
298
// It's kind of silly that the de facto spec for ENOENT (the test suite)
299
// mandates that stdio _is_ set up, even if there is no process on the
300
// receiving end, but it is what it is.
301
if (err !== uv.UV_ENOENT) return err;
302
} else if (err) {
303
// Close all opened fds on error
304
for (i = 0; i < stdio.length; i++) {
305
const stream = stdio[i];
306
if (stream.type === 'pipe') {
307
stream.handle.close();
308
}
309
}
310
311
this._handle.close();
312
this._handle = null;
313
throw errnoException(err, 'spawn');
314
}
315
316
this.pid = this._handle.pid;
317
318
for (i = 0; i < stdio.length; i++) {
319
const stream = stdio[i];
320
if (stream.type === 'ignore') continue;
321
322
if (stream.ipc) {
323
this._closesNeeded++;
324
continue;
325
}
326
327
if (stream.handle) {
328
// when i === 0 - we're dealing with stdin
329
// (which is the only one writable pipe)
330
stream.socket = createSocket(this.pid !== 0 ?
331
stream.handle : null, i > 0);
332
333
if (i > 0 && this.pid !== 0) {
334
this._closesNeeded++;
335
stream.socket.on('close', () => {
336
maybeClose(this);
337
});
338
}
339
}
340
}
341
342
this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ?
343
stdio[0].socket : null;
344
this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ?
345
stdio[1].socket : null;
346
this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ?
347
stdio[2].socket : null;
348
349
this.stdio = [];
350
351
for (i = 0; i < stdio.length; i++)
352
this.stdio.push(stdio[i].socket === undefined ? null : stdio[i].socket);
353
354
// Add .send() method and start listening for IPC data
355
if (ipc !== undefined) setupChannel(this, ipc);
356
357
return err;
358
};
359
360
361
function onErrorNT(self, err) {
362
self._handle.onexit(err);
363
}
364
365
366
ChildProcess.prototype.kill = function(sig) {
367
368
const signal = sig === 0 ? sig :
369
convertToValidSignal(sig === undefined ? 'SIGTERM' : sig);
370
371
if (this._handle) {
372
var err = this._handle.kill(signal);
373
if (err === 0) {
374
/* Success. */
375
this.killed = true;
376
return true;
377
}
378
if (err === uv.UV_ESRCH) {
379
/* Already dead. */
380
} else if (err === uv.UV_EINVAL || err === uv.UV_ENOSYS) {
381
/* The underlying platform doesn't support this signal. */
382
throw errnoException(err, 'kill');
383
} else {
384
/* Other error, almost certainly EPERM. */
385
this.emit('error', errnoException(err, 'kill'));
386
}
387
}
388
389
/* Kill didn't succeed. */
390
return false;
391
};
392
393
394
ChildProcess.prototype.ref = function() {
395
if (this._handle) this._handle.ref();
396
};
397
398
399
ChildProcess.prototype.unref = function() {
400
if (this._handle) this._handle.unref();
401
};
402
403
class Control extends EventEmitter {
404
constructor(channel) {
405
super();
406
this.channel = channel;
407
this.refs = 0;
408
}
409
ref() {
410
if (++this.refs === 1) {
411
this.channel.ref();
412
}
413
}
414
unref() {
415
if (--this.refs === 0) {
416
this.channel.unref();
417
this.emit('unref');
418
}
419
}
420
}
421
422
function setupChannel(target, channel) {
423
target.channel = channel;
424
425
// _channel can be deprecated in version 8
426
Object.defineProperty(target, '_channel', {
427
get() { return target.channel; },
428
set(val) { target.channel = val; },
429
enumerable: true
430
});
431
432
target._handleQueue = null;
433
target._pendingMessage = null;
434
435
const control = new Control(channel);
436
437
var decoder = new StringDecoder('utf8');
438
var jsonBuffer = '';
439
var pendingHandle = null;
440
channel.buffering = false;
441
channel.onread = function(nread, pool, recvHandle) {
442
// TODO(bnoordhuis) Check that nread > 0.
443
if (pool) {
444
if (recvHandle)
445
pendingHandle = recvHandle;
446
447
// Linebreak is used as a message end sign
448
var chunks = decoder.write(pool).split('\n');
449
var numCompleteChunks = chunks.length - 1;
450
// Last line does not have trailing linebreak
451
var incompleteChunk = chunks[numCompleteChunks];
452
if (numCompleteChunks === 0) {
453
jsonBuffer += incompleteChunk;
454
this.buffering = jsonBuffer.length !== 0;
455
return;
456
}
457
chunks[0] = jsonBuffer + chunks[0];
458
459
for (var i = 0; i < numCompleteChunks; i++) {
460
var message = JSON.parse(chunks[i]);
461
462
// There will be at most one NODE_HANDLE message in every chunk we
463
// read because SCM_RIGHTS messages don't get coalesced. Make sure
464
// that we deliver the handle with the right message however.
465
if (isInternal(message)) {
466
if (message.cmd === 'NODE_HANDLE') {
467
handleMessage(message, pendingHandle, true);
468
pendingHandle = null;
469
} else {
470
handleMessage(message, undefined, true);
471
}
472
} else {
473
handleMessage(message, undefined, false);
474
}
475
}
476
jsonBuffer = incompleteChunk;
477
this.buffering = jsonBuffer.length !== 0;
478
479
} else {
480
this.buffering = false;
481
target.disconnect();
482
channel.onread = nop;
483
channel.close();
484
target.channel = null;
485
maybeClose(target);
486
}
487
};
488
489
// object where socket lists will live
490
channel.sockets = { got: {}, send: {} };
491
492
// handlers will go through this
493
target.on('internalMessage', function(message, handle) {
494
// Once acknowledged - continue sending handles.
495
if (message.cmd === 'NODE_HANDLE_ACK' ||
496
message.cmd === 'NODE_HANDLE_NACK') {
497
498
if (target._pendingMessage) {
499
if (message.cmd === 'NODE_HANDLE_ACK') {
500
closePendingHandle(target);
501
} else if (target._pendingMessage.retransmissions++ ===
502
MAX_HANDLE_RETRANSMISSIONS) {
503
closePendingHandle(target);
504
process.emitWarning('Handle did not reach the receiving process ' +
505
'correctly', 'SentHandleNotReceivedWarning');
506
}
507
}
508
509
assert(Array.isArray(target._handleQueue));
510
var queue = target._handleQueue;
511
target._handleQueue = null;
512
513
if (target._pendingMessage) {
514
target._send(target._pendingMessage.message,
515
target._pendingMessage.handle,
516
target._pendingMessage.options,
517
target._pendingMessage.callback);
518
}
519
520
for (var i = 0; i < queue.length; i++) {
521
var args = queue[i];
522
target._send(args.message, args.handle, args.options, args.callback);
523
}
524
525
// Process a pending disconnect (if any).
526
if (!target.connected && target.channel && !target._handleQueue)
527
target._disconnect();
528
529
return;
530
}
531
532
if (message.cmd !== 'NODE_HANDLE') return;
533
534
// It is possible that the handle is not received because of some error on
535
// ancillary data reception such as MSG_CTRUNC. In this case, report the
536
// sender about it by sending a NODE_HANDLE_NACK message.
537
if (!handle)
538
return target._send({ cmd: 'NODE_HANDLE_NACK' }, null, true);
539
540
// Acknowledge handle receival. Don't emit error events (for example if
541
// the other side has disconnected) because this call to send() is not
542
// initiated by the user and it shouldn't be fatal to be unable to ACK
543
// a message.
544
target._send({ cmd: 'NODE_HANDLE_ACK' }, null, true);
545
546
var obj = handleConversion[message.type];
547
548
// Update simultaneous accepts on Windows
549
if (process.platform === 'win32') {
550
handle._simultaneousAccepts = false;
551
net._setSimultaneousAccepts(handle);
552
}
553
554
// Convert handle object
555
obj.got.call(this, message, handle, function(handle) {
556
handleMessage(message.msg, handle, isInternal(message.msg));
557
});
558
});
559
560
target.send = function(message, handle, options, callback) {
561
if (typeof handle === 'function') {
562
callback = handle;
563
handle = undefined;
564
options = undefined;
565
} else if (typeof options === 'function') {
566
callback = options;
567
options = undefined;
568
} else if (options !== undefined &&
569
(options === null || typeof options !== 'object')) {
570
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options', 'Object');
571
}
572
573
options = Object.assign({ swallowErrors: false }, options);
574
575
if (this.connected) {
576
return this._send(message, handle, options, callback);
577
}
578
const ex = new errors.Error('ERR_IPC_CHANNEL_CLOSED');
579
if (typeof callback === 'function') {
580
process.nextTick(callback, ex);
581
} else {
582
this.emit('error', ex); // FIXME(bnoordhuis) Defer to next tick.
583
}
584
return false;
585
};
586
587
target._send = function(message, handle, options, callback) {
588
assert(this.connected || this.channel);
589
590
if (message === undefined)
591
throw new errors.TypeError('ERR_MISSING_ARGS', 'message');
592
593
// Support legacy function signature
594
if (typeof options === 'boolean') {
595
options = { swallowErrors: options };
596
}
597
598
// package messages with a handle object
599
if (handle) {
600
// this message will be handled by an internalMessage event handler
601
message = {
602
cmd: 'NODE_HANDLE',
603
type: null,
604
msg: message
605
};
606
607
if (handle instanceof net.Socket) {
608
message.type = 'net.Socket';
609
} else if (handle instanceof net.Server) {
610
message.type = 'net.Server';
611
} else if (handle instanceof TCP || handle instanceof Pipe) {
612
message.type = 'net.Native';
613
} else if (handle instanceof dgram.Socket) {
614
message.type = 'dgram.Socket';
615
} else if (handle instanceof UDP) {
616
message.type = 'dgram.Native';
617
} else {
618
throw new errors.TypeError('ERR_INVALID_HANDLE_TYPE');
619
}
620
621
// Queue-up message and handle if we haven't received ACK yet.
622
if (this._handleQueue) {
623
this._handleQueue.push({
624
callback: callback,
625
handle: handle,
626
options: options,
627
message: message.msg,
628
});
629
return this._handleQueue.length === 1;
630
}
631
632
var obj = handleConversion[message.type];
633
634
// convert TCP object to native handle object
635
handle = handleConversion[message.type].send.call(target,
636
message,
637
handle,
638
options);
639
640
// If handle was sent twice, or it is impossible to get native handle
641
// out of it - just send a text without the handle.
642
if (!handle)
643
message = message.msg;
644
645
// Update simultaneous accepts on Windows
646
if (obj.simultaneousAccepts) {
647
net._setSimultaneousAccepts(handle);
648
}
649
} else if (this._handleQueue &&
650
!(message && (message.cmd === 'NODE_HANDLE_ACK' ||
651
message.cmd === 'NODE_HANDLE_NACK'))) {
652
// Queue request anyway to avoid out-of-order messages.
653
this._handleQueue.push({
654
callback: callback,
655
handle: null,
656
options: options,
657
message: message,
658
});
659
return this._handleQueue.length === 1;
660
}
661
662
var req = new WriteWrap();
663
req.async = false;
664
665
var string = JSON.stringify(message) + '\n';
666
var err = channel.writeUtf8String(req, string, handle);
667
668
if (err === 0) {
669
if (handle) {
670
if (!this._handleQueue)
671
this._handleQueue = [];
672
if (obj && obj.postSend)
673
obj.postSend(message, handle, options, callback, target);
674
}
675
676
if (req.async) {
677
req.oncomplete = function() {
678
control.unref();
679
if (typeof callback === 'function')
680
callback(null);
681
};
682
control.ref();
683
} else if (typeof callback === 'function') {
684
process.nextTick(callback, null);
685
}
686
} else {
687
// Cleanup handle on error
688
if (obj && obj.postSend)
689
obj.postSend(message, handle, options, callback);
690
691
if (!options.swallowErrors) {
692
const ex = errnoException(err, 'write');
693
if (typeof callback === 'function') {
694
process.nextTick(callback, ex);
695
} else {
696
this.emit('error', ex); // FIXME(bnoordhuis) Defer to next tick.
697
}
698
}
699
}
700
701
/* If the master is > 2 read() calls behind, please stop sending. */
702
return channel.writeQueueSize < (65536 * 2);
703
};
704
705
// connected will be set to false immediately when a disconnect() is
706
// requested, even though the channel might still be alive internally to
707
// process queued messages. The three states are distinguished as follows:
708
// - disconnect() never requested: channel is not null and connected
709
// is true
710
// - disconnect() requested, messages in the queue: channel is not null
711
// and connected is false
712
// - disconnect() requested, channel actually disconnected: channel is
713
// null and connected is false
714
target.connected = true;
715
716
target.disconnect = function() {
717
if (!this.connected) {
718
this.emit('error', new errors.Error('ERR_IPC_DISCONNECTED'));
719
return;
720
}
721
722
// Do not allow any new messages to be written.
723
this.connected = false;
724
725
// If there are no queued messages, disconnect immediately. Otherwise,
726
// postpone the disconnect so that it happens internally after the
727
// queue is flushed.
728
if (!this._handleQueue)
729
this._disconnect();
730
};
731
732
target._disconnect = function() {
733
assert(this.channel);
734
735
// This marks the fact that the channel is actually disconnected.
736
this.channel = null;
737
738
if (this._pendingMessage)
739
closePendingHandle(this);
740
741
var fired = false;
742
function finish() {
743
if (fired) return;
744
fired = true;
745
746
channel.close();
747
target.emit('disconnect');
748
}
749
750
// If a message is being read, then wait for it to complete.
751
if (channel.buffering) {
752
this.once('message', finish);
753
this.once('internalMessage', finish);
754
755
return;
756
}
757
758
process.nextTick(finish);
759
};
760
761
function emit(event, message, handle) {
762
target.emit(event, message, handle);
763
}
764
765
function handleMessage(message, handle, internal) {
766
if (!target.channel)
767
return;
768
769
var eventName = (internal ? 'internalMessage' : 'message');
770
771
process.nextTick(emit, eventName, message, handle);
772
}
773
774
channel.readStart();
775
return control;
776
}
777
778
const INTERNAL_PREFIX = 'NODE_';
779
function isInternal(message) {
780
return (message !== null &&
781
typeof message === 'object' &&
782
typeof message.cmd === 'string' &&
783
message.cmd.length > INTERNAL_PREFIX.length &&
784
message.cmd.slice(0, INTERNAL_PREFIX.length) === INTERNAL_PREFIX);
785
}
786
787
function nop() { }
788
789
function _validateStdio(stdio, sync) {
790
var ipc;
791
var ipcFd;
792
793
// Replace shortcut with an array
794
if (typeof stdio === 'string') {
795
switch (stdio) {
796
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
797
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
798
case 'inherit': stdio = [0, 1, 2]; break;
799
default:
800
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio', stdio);
801
}
802
} else if (!Array.isArray(stdio)) {
803
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
804
'stdio', util.inspect(stdio));
805
}
806
807
// At least 3 stdio will be created
808
// Don't concat() a new Array() because it would be sparse, and
809
// stdio.reduce() would skip the sparse elements of stdio.
810
// See http://stackoverflow.com/a/5501711/3561
811
while (stdio.length < 3) stdio.push(undefined);
812
813
// Translate stdio into C++-readable form
814
// (i.e. PipeWraps or fds)
815
stdio = stdio.reduce(function(acc, stdio, i) {
816
function cleanup() {
817
for (var i = 0; i < acc.length; i++) {
818
if ((acc[i].type === 'pipe' || acc[i].type === 'ipc') && acc[i].handle)
819
acc[i].handle.close();
820
}
821
}
822
823
// Defaults
824
if (stdio == null) {
825
stdio = i < 3 ? 'pipe' : 'ignore';
826
}
827
828
if (stdio === 'ignore') {
829
acc.push({ type: 'ignore' });
830
} else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) {
831
var a = {
832
type: 'pipe',
833
readable: i === 0,
834
writable: i !== 0
835
};
836
837
if (!sync)
838
a.handle = new Pipe(PipeConstants.SOCKET);
839
840
acc.push(a);
841
} else if (stdio === 'ipc') {
842
if (sync || ipc !== undefined) {
843
// Cleanup previously created pipes
844
cleanup();
845
if (!sync)
846
throw new errors.Error('ERR_IPC_ONE_PIPE');
847
else
848
throw new errors.Error('ERR_IPC_SYNC_FORK');
849
}
850
851
ipc = new Pipe(PipeConstants.IPC);
852
ipcFd = i;
853
854
acc.push({
855
type: 'pipe',
856
handle: ipc,
857
ipc: true
858
});
859
} else if (stdio === 'inherit') {
860
acc.push({
861
type: 'inherit',
862
fd: i
863
});
864
} else if (typeof stdio === 'number' || typeof stdio.fd === 'number') {
865
acc.push({
866
type: 'fd',
867
fd: typeof stdio === 'number' ? stdio : stdio.fd
868
});
869
} else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
870
getHandleWrapType(stdio._handle)) {
871
var handle = getHandleWrapType(stdio) ?
872
stdio :
873
getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
874
875
acc.push({
876
type: 'wrap',
877
wrapType: getHandleWrapType(handle),
878
handle: handle
879
});
880
} else if (isUint8Array(stdio) || typeof stdio === 'string') {
881
if (!sync) {
882
cleanup();
883
throw new errors.TypeError('ERR_INVALID_SYNC_FORK_INPUT',
884
util.inspect(stdio));
885
}
886
} else {
887
// Cleanup
888
cleanup();
889
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'stdio',
890
util.inspect(stdio));
891
}
892
893
return acc;
894
}, []);
895
896
return { stdio, ipc, ipcFd };
897
}
898
899
900
function getSocketList(type, slave, key) {
901
var sockets = slave.channel.sockets[type];
902
var socketList = sockets[key];
903
if (!socketList) {
904
var Construct = type === 'send' ? SocketListSend : SocketListReceive;
905
socketList = sockets[key] = new Construct(slave, key);
906
}
907
return socketList;
908
}
909
910
911
function maybeClose(subprocess) {
912
subprocess._closesGot++;
913
914
if (subprocess._closesGot === subprocess._closesNeeded) {
915
subprocess.emit('close', subprocess.exitCode, subprocess.signalCode);
916
}
917
}
918
919
module.exports = {
920
ChildProcess,
921
setupChannel,
922
_validateStdio,
923
getSocketList
924
};