The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / lib / child_process.js @ 5e7e51c2

History | View | Annotate | Download (24.4 KB)

1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21

    
22
var EventEmitter = require('events').EventEmitter;
23
var net = require('net');
24
var Process = process.binding('process_wrap').Process;
25
var util = require('util');
26
var constants; // if (!constants) constants = process.binding('constants');
27

    
28
var handleWraps = {};
29

    
30
function handleWrapGetter(name, callback) {
31
  var cons;
32

    
33
  Object.defineProperty(handleWraps, name, {
34
    get: function() {
35
      if (cons !== undefined) return cons;
36
      return cons = callback();
37
    }
38
  });
39
}
40

    
41
handleWrapGetter('Pipe', function() {
42
  return process.binding('pipe_wrap').Pipe;
43
});
44

    
45
handleWrapGetter('TTY', function() {
46
  return process.binding('tty_wrap').TTY;
47
});
48

    
49
handleWrapGetter('TCP', function() {
50
  return process.binding('tcp_wrap').TCP;
51
});
52

    
53
handleWrapGetter('UDP', function() {
54
  return process.binding('udp_wrap').UDP;
55
});
56

    
57
// constructors for lazy loading
58
function createPipe(ipc) {
59
  return new handleWraps.Pipe(ipc);
60
}
61

    
62
function createSocket(pipe, readable) {
63
  var s = new net.Socket({ handle: pipe });
64

    
65
  if (readable) {
66
    s.writable = false;
67
    s.readable = true;
68
  } else {
69
    s.writable = true;
70
    s.readable = false;
71
  }
72

    
73
  return s;
74
}
75

    
76

    
77
// this object contain function to convert TCP objects to native handle objects
78
// and back again.
79
var handleConversion = {
80
  'net.Native': {
81
    simultaneousAccepts: true,
82

    
83
    send: function(message, handle) {
84
      return handle;
85
    },
86

    
87
    got: function(message, handle, emit) {
88
      emit(handle);
89
    }
90
  },
91

    
92
  'net.Server': {
93
    simultaneousAccepts: true,
94

    
95
    send: function(message, server) {
96
      return server._handle;
97
    },
98

    
99
    got: function(message, handle, emit) {
100
      var self = this;
101

    
102
      var server = new net.Server();
103
      server.listen(handle, function() {
104
        emit(server);
105
      });
106
    }
107
  },
108

    
109
  'net.Socket': {
110
    send: function(message, socket) {
111
      // if the socket was created by net.Server
112
      if (socket.server) {
113
        // the slave should keep track of the socket
114
        message.key = socket.server._connectionKey;
115

    
116
        var firstTime = !this._channel.sockets.send[message.key];
117
        var socketList = getSocketList('send', this, message.key);
118

    
119
        // the server should no longer expose a .connection property
120
        // and when asked to close it should query the socket status from
121
        // the slaves
122
        if (firstTime) socket.server._setupSlave(socketList);
123

    
124
        // Act like socket is detached
125
        socket.server._connections--;
126
      }
127

    
128
      // remove handle from socket object, it will be closed when the socket
129
      // will be sent
130
      var handle = socket._handle;
131
      handle.onread = function() {};
132
      socket._handle = null;
133

    
134
      return handle;
135
    },
136

    
137
    postSend: function(handle) {
138
      // Close the Socket handle after sending it
139
      handle.close();
140
    },
141

    
142
    got: function(message, handle, emit) {
143
      var socket = new net.Socket({handle: handle});
144
      socket.readable = socket.writable = true;
145

    
146
      // if the socket was created by net.Server we will track the socket
147
      if (message.key) {
148

    
149
        // add socket to connections list
150
        var socketList = getSocketList('got', this, message.key);
151
        socketList.add({
152
          socket: socket
153
        });
154
      }
155

    
156
      emit(socket);
157
    }
158
  },
159

    
160
  'dgram.Native': {
161
    simultaneousAccepts: false,
162

    
163
    send: function(message, handle) {
164
      return handle;
165
    },
166

    
167
    got: function(message, handle, emit) {
168
      emit(handle);
169
    }
170
  }
171
};
172

    
173
// This object keep track of the socket there are sended
174
function SocketListSend(slave, key) {
175
  EventEmitter.call(this);
176

    
177
  var self = this;
178

    
179
  this.key = key;
180
  this.slave = slave;
181
}
182
util.inherits(SocketListSend, EventEmitter);
183

    
184
SocketListSend.prototype._request = function(msg, cmd, callback) {
185
  var self = this;
186

    
187
  if (!this.slave.connected) return onclose();
188
  this.slave.send(msg);
189

    
190
  function onclose() {
191
    self.slave.removeListener('internalMessage', onreply);
192
    callback(new Error('Slave closed before reply'));
193
  };
194

    
195
  function onreply(msg) {
196
    if (!(msg.cmd === cmd && msg.key === self.key)) return;
197
    self.slave.removeListener('disconnect', onclose);
198
    self.slave.removeListener('internalMessage', onreply);
199

    
200
    callback(null, msg);
201
  };
202

    
203
  this.slave.once('disconnect', onclose);
204
  this.slave.on('internalMessage', onreply);
205
};
206

    
207
SocketListSend.prototype.close = function close(callback) {
208
  this._request({
209
    cmd: 'NODE_SOCKET_NOTIFY_CLOSE',
210
    key: this.key
211
  }, 'NODE_SOCKET_ALL_CLOSED', callback);
212
};
213

    
214
SocketListSend.prototype.getConnections = function getConnections(callback) {
215
  this._request({
216
    cmd: 'NODE_SOCKET_GET_COUNT',
217
    key: this.key
218
  }, 'NODE_SOCKET_COUNT', function(err, msg) {
219
    if (err) return callback(err);
220
    callback(null, msg.count);
221
  });
222
};
223

    
224
// This object keep track of the socket there are received
225
function SocketListReceive(slave, key) {
226
  EventEmitter.call(this);
227

    
228
  var self = this;
229

    
230
  this.connections = 0;
231
  this.key = key;
232
  this.slave = slave;
233

    
234
  function onempty() {
235
    if (!self.slave.connected) return;
236

    
237
    self.slave.send({
238
      cmd: 'NODE_SOCKET_ALL_CLOSED',
239
      key: self.key
240
    });
241
  }
242

    
243
  this.slave.on('internalMessage', function(msg) {
244
    if (msg.key !== self.key) return;
245

    
246
    if (msg.cmd === 'NODE_SOCKET_NOTIFY_CLOSE') {
247
      // Already empty
248
      if (self.connections === 0) return onempty();
249

    
250
      // Wait for sockets to get closed
251
      self.once('empty', onempty);
252
    } else if (msg.cmd === 'NODE_SOCKET_GET_COUNT') {
253
      if (!self.slave.connected) return;
254
      self.slave.send({
255
        cmd: 'NODE_SOCKET_COUNT',
256
        key: self.key,
257
        count: self.connections
258
      });
259
    }
260
  });
261
}
262
util.inherits(SocketListReceive, EventEmitter);
263

    
264
SocketListReceive.prototype.add = function(obj) {
265
  var self = this;
266

    
267
  this.connections++;
268

    
269
  // Notify previous owner of socket about its state change
270
  obj.socket.once('close', function() {
271
    self.connections--;
272

    
273
    if (self.connections === 0) self.emit('empty');
274
  });
275
};
276

    
277
function getSocketList(type, slave, key) {
278
  var sockets = slave._channel.sockets[type];
279
  var socketList = sockets[key];
280
  if (!socketList) {
281
    var Construct = type === 'send' ? SocketListSend : SocketListReceive;
282
    socketList = sockets[key] = new Construct(slave, key);
283
  }
284
  return socketList;
285
}
286

    
287
function handleMessage(target, message, handle) {
288
  //Filter out internal messages
289
  //if cmd property begin with "_NODE"
290
  if (message !== null &&
291
      typeof message === 'object' &&
292
      typeof message.cmd === 'string' &&
293
      message.cmd.indexOf('NODE_') === 0) {
294
    target.emit('internalMessage', message, handle);
295
  }
296
  //Non-internal message
297
  else {
298
    target.emit('message', message, handle);
299
  }
300
}
301

    
302
function setupChannel(target, channel) {
303
  target._channel = channel;
304

    
305
  var jsonBuffer = '';
306
  channel.buffering = false;
307
  channel.onread = function(pool, offset, length, recvHandle) {
308
    if (pool) {
309
      jsonBuffer += pool.toString('ascii', offset, offset + length);
310

    
311
      var i, start = 0;
312

    
313
      //Linebreak is used as a message end sign
314
      while ((i = jsonBuffer.indexOf('\n', start)) >= 0) {
315
        var json = jsonBuffer.slice(start, i);
316
        var message = JSON.parse(json);
317

    
318
        handleMessage(target, message, recvHandle);
319

    
320
        start = i + 1;
321
      }
322
      jsonBuffer = jsonBuffer.slice(start);
323
      this.buffering = jsonBuffer.length !== 0;
324

    
325
    } else {
326
      this.buffering = false;
327
      target.disconnect();
328
      channel.onread = nop;
329
      channel.close();
330
      maybeClose(target);
331
    }
332
  };
333

    
334
  // object where socket lists will live
335
  channel.sockets = { got: {}, send: {} };
336

    
337
  // handlers will go through this
338
  target.on('internalMessage', function(message, handle) {
339
    if (message.cmd !== 'NODE_HANDLE') return;
340

    
341
    var obj = handleConversion[message.type];
342

    
343
    // Update simultaneous accepts on Windows
344
    if (process.platform === 'win32') {
345
      handle._simultaneousAccepts = false;
346
      net._setSimultaneousAccepts(handle);
347
    }
348

    
349
    // Convert handle object
350
    obj.got.call(this, message, handle, function(handle) {
351
      handleMessage(target, message.msg, handle);
352
    });
353
  });
354

    
355
  target.send = function(message, handle) {
356
    if (typeof message === 'undefined') {
357
      throw new TypeError('message cannot be undefined');
358
    }
359

    
360
    if (!this.connected) {
361
      this.emit('error', new Error('channel closed'));
362
      return;
363
    }
364

    
365
    // package messages with a handle object
366
    if (handle) {
367
      // this message will be handled by an internalMessage event handler
368
      message = {
369
        cmd: 'NODE_HANDLE',
370
        msg: message
371
      };
372

    
373
      if (handle instanceof net.Socket) {
374
        message.type = 'net.Socket';
375
      } else if (handle instanceof net.Server) {
376
        message.type = 'net.Server';
377
      } else if (handle instanceof process.binding('tcp_wrap').TCP ||
378
                 handle instanceof process.binding('pipe_wrap').Pipe) {
379
        message.type = 'net.Native';
380
      } else if (handle instanceof process.binding('udp_wrap').UDP) {
381
        message.type = 'dgram.Native';
382
      } else {
383
        throw new TypeError("This handle type can't be sent");
384
      }
385

    
386
      var obj = handleConversion[message.type];
387

    
388
      // convert TCP object to native handle object
389
      handle = handleConversion[message.type].send.apply(target, arguments);
390

    
391
      // Update simultaneous accepts on Windows
392
      if (obj.simultaneousAccepts) {
393
        net._setSimultaneousAccepts(handle);
394
      }
395
    }
396

    
397
    var string = JSON.stringify(message) + '\n';
398
    var writeReq = channel.writeUtf8String(string, handle);
399

    
400
    if (!writeReq) {
401
      var er = errnoException(errno, 'write', 'cannot write to IPC channel.');
402
      this.emit('error', er);
403
    }
404

    
405
    if (obj && obj.postSend) {
406
      writeReq.oncomplete = obj.postSend.bind(null, handle);
407
    } else {
408
      writeReq.oncomplete = nop;
409
    }
410

    
411
    /* If the master is > 2 read() calls behind, please stop sending. */
412
    return channel.writeQueueSize < (65536 * 2);
413
  };
414

    
415
  target.connected = true;
416
  target.disconnect = function() {
417
    if (!this.connected) {
418
      this.emit('error', new Error('IPC channel is already disconnected'));
419
      return;
420
    }
421

    
422
    // do not allow messages to be written
423
    this.connected = false;
424
    this._channel = null;
425

    
426
    var fired = false;
427
    function finish() {
428
      if (fired) return;
429
      fired = true;
430

    
431
      channel.close();
432
      target.emit('disconnect');
433
    }
434

    
435
    // If a message is being read, then wait for it to complete.
436
    if (channel.buffering) {
437
      this.once('message', finish);
438
      this.once('internalMessage', finish);
439

    
440
      return;
441
    }
442

    
443
    finish();
444
  };
445

    
446
  channel.readStart();
447
}
448

    
449

    
450
function nop() { }
451

    
452
exports.fork = function(modulePath /*, args, options*/) {
453

    
454
  // Get options and args arguments.
455
  var options, args, execArgv;
456
  if (Array.isArray(arguments[1])) {
457
    args = arguments[1];
458
    options = util._extend({}, arguments[2]);
459
  } else {
460
    args = [];
461
    options = util._extend({}, arguments[1]);
462
  }
463

    
464
  // Prepare arguments for fork:
465
  execArgv = options.execArgv || process.execArgv;
466
  args = execArgv.concat([modulePath], args);
467

    
468
  // Leave stdin open for the IPC channel. stdout and stderr should be the
469
  // same as the parent's if silent isn't set.
470
  options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
471
      [0, 1, 2, 'ipc'];
472

    
473
  options.execPath = options.execPath || process.execPath;
474

    
475
  return spawn(options.execPath, args, options);
476
};
477

    
478

    
479
exports._forkChild = function(fd) {
480
  // set process.send()
481
  var p = createPipe(true);
482
  p.open(fd);
483
  p.unref();
484
  setupChannel(process, p);
485

    
486
  var refs = 0;
487
  process.on('newListener', function(name) {
488
    if (name !== 'message' && name !== 'disconnect') return;
489
    if (++refs === 1) p.ref();
490
  });
491
  process.on('removeListener', function(name) {
492
    if (name !== 'message' && name !== 'disconnect') return;
493
    if (--refs === 0) p.unref();
494
  });
495
};
496

    
497

    
498
exports.exec = function(command /*, options, callback */) {
499
  var file, args, options, callback;
500

    
501
  if (typeof arguments[1] === 'function') {
502
    options = undefined;
503
    callback = arguments[1];
504
  } else {
505
    options = arguments[1];
506
    callback = arguments[2];
507
  }
508

    
509
  if (process.platform === 'win32') {
510
    file = 'cmd.exe';
511
    args = ['/s', '/c', '"' + command + '"'];
512
    // Make a shallow copy before patching so we don't clobber the user's
513
    // options object.
514
    options = util._extend({}, options);
515
    options.windowsVerbatimArguments = true;
516
  } else {
517
    file = '/bin/sh';
518
    args = ['-c', command];
519
  }
520
  return exports.execFile(file, args, options, callback);
521
};
522

    
523

    
524
exports.execFile = function(file /* args, options, callback */) {
525
  var args, optionArg, callback;
526
  var options = {
527
    encoding: 'utf8',
528
    timeout: 0,
529
    maxBuffer: 200 * 1024,
530
    killSignal: 'SIGTERM',
531
    cwd: null,
532
    env: null
533
  };
534

    
535
  // Parse the parameters.
536

    
537
  if (typeof arguments[arguments.length - 1] === 'function') {
538
    callback = arguments[arguments.length - 1];
539
  }
540

    
541
  if (Array.isArray(arguments[1])) {
542
    args = arguments[1];
543
    options = util._extend(options, arguments[2]);
544
  } else {
545
    args = [];
546
    options = util._extend(options, arguments[1]);
547
  }
548

    
549
  var child = spawn(file, args, {
550
    cwd: options.cwd,
551
    env: options.env,
552
    windowsVerbatimArguments: !!options.windowsVerbatimArguments
553
  });
554

    
555
  var stdout = '';
556
  var stderr = '';
557
  var killed = false;
558
  var exited = false;
559
  var timeoutId;
560

    
561
  var err;
562

    
563
  function exithandler(code, signal) {
564
    if (exited) return;
565
    exited = true;
566

    
567
    if (timeoutId) {
568
      clearTimeout(timeoutId);
569
      timeoutId = null;
570
    }
571

    
572
    if (!callback) return;
573

    
574
    if (err) {
575
      callback(err, stdout, stderr);
576
    } else if (code === 0 && signal === null) {
577
      callback(null, stdout, stderr);
578
    } else {
579
      var e = new Error('Command failed: ' + stderr);
580
      e.killed = child.killed || killed;
581
      e.code = code;
582
      e.signal = signal;
583
      callback(e, stdout, stderr);
584
    }
585
  }
586

    
587
  function errorhandler(e) {
588
    err = e;
589
    child.stdout.destroy();
590
    child.stderr.destroy();
591
    exithandler();
592
  }
593

    
594
  function kill() {
595
    child.stdout.destroy();
596
    child.stderr.destroy();
597

    
598
    killed = true;
599
    try {
600
      child.kill(options.killSignal);
601
    } catch (e) {
602
      err = e;
603
      exithandler();
604
    }
605
  }
606

    
607
  if (options.timeout > 0) {
608
    timeoutId = setTimeout(function() {
609
      kill();
610
      timeoutId = null;
611
    }, options.timeout);
612
  }
613

    
614
  child.stdout.setEncoding(options.encoding);
615
  child.stderr.setEncoding(options.encoding);
616

    
617
  child.stdout.addListener('data', function(chunk) {
618
    stdout += chunk;
619
    if (stdout.length > options.maxBuffer) {
620
      err = new Error('stdout maxBuffer exceeded.');
621
      kill();
622
    }
623
  });
624

    
625
  child.stderr.addListener('data', function(chunk) {
626
    stderr += chunk;
627
    if (stderr.length > options.maxBuffer) {
628
      err = new Error('stderr maxBuffer exceeded.');
629
      kill();
630
    }
631
  });
632

    
633
  child.addListener('close', exithandler);
634
  child.addListener('error', errorhandler);
635

    
636
  return child;
637
};
638

    
639

    
640
var spawn = exports.spawn = function(file, args, options) {
641
  args = args ? args.slice(0) : [];
642
  args.unshift(file);
643

    
644
  var env = (options ? options.env : null) || process.env;
645
  var envPairs = [];
646
  for (var key in env) {
647
    envPairs.push(key + '=' + env[key]);
648
  }
649

    
650
  var child = new ChildProcess();
651
  if (options && options.customFds && !options.stdio) {
652
    options.stdio = options.customFds.map(function(fd) {
653
      return fd === -1 ? 'pipe' : fd;
654
    });
655
  }
656

    
657
  child.spawn({
658
    file: file,
659
    args: args,
660
    cwd: options ? options.cwd : null,
661
    windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
662
    detached: !!(options && options.detached),
663
    envPairs: envPairs,
664
    stdio: options ? options.stdio : null,
665
    uid: options ? options.uid : null,
666
    gid: options ? options.gid : null
667
  });
668

    
669
  return child;
670
};
671

    
672

    
673
function maybeClose(subprocess) {
674
  subprocess._closesGot++;
675

    
676
  if (subprocess._closesGot == subprocess._closesNeeded) {
677
    subprocess.emit('close', subprocess.exitCode, subprocess.signalCode);
678
  }
679
}
680

    
681

    
682
function ChildProcess() {
683
  EventEmitter.call(this);
684

    
685
  // Initialize TCPWrap and PipeWrap
686
  process.binding('tcp_wrap');
687
  process.binding('pipe_wrap');
688

    
689
  var self = this;
690

    
691
  this._closesNeeded = 1;
692
  this._closesGot = 0;
693
  this.connected = false;
694

    
695
  this.signalCode = null;
696
  this.exitCode = null;
697
  this.killed = false;
698

    
699
  this._handle = new Process();
700
  this._handle.owner = this;
701

    
702
  this._handle.onexit = function(exitCode, signalCode) {
703
    //
704
    // follow 0.4.x behaviour:
705
    //
706
    // - normally terminated processes don't touch this.signalCode
707
    // - signaled processes don't touch this.exitCode
708
    //
709
    // new in 0.9.x:
710
    //
711
    // - spawn failures are reported with exitCode == -1
712
    //
713
    var err = (exitCode == -1) ? errnoException(errno, 'spawn') : null;
714

    
715
    if (signalCode) {
716
      self.signalCode = signalCode;
717
    } else {
718
      self.exitCode = exitCode;
719
    }
720

    
721
    if (self.stdin) {
722
      self.stdin.destroy();
723
    }
724

    
725
    self._handle.close();
726
    self._handle = null;
727

    
728
    if (exitCode == -1) {
729
      self.emit('error', err);
730
    } else {
731
      self.emit('exit', self.exitCode, self.signalCode);
732
    }
733

    
734
    // if any of the stdio streams have not been touched,
735
    // then pull all the data through so that it can get the
736
    // eof and emit a 'close' event.
737
    // Do it on nextTick so that the user has one last chance
738
    // to consume the output, if for example they only want to
739
    // start reading the data once the process exits.
740
    process.nextTick(function() {
741
      flushStdio(self);
742
    });
743

    
744
    maybeClose(self);
745
  };
746
}
747
util.inherits(ChildProcess, EventEmitter);
748

    
749

    
750
function flushStdio(subprocess) {
751
  subprocess.stdio.forEach(function(stream, fd, stdio) {
752
    if (!stream || !stream.readable || stream._consuming ||
753
        stream._readableState.flowing)
754
      return;
755
    stream.resume();
756
  });
757
}
758

    
759

    
760

    
761
function getHandleWrapType(stream) {
762
  if (stream instanceof handleWraps.Pipe) return 'pipe';
763
  if (stream instanceof handleWraps.TTY) return 'tty';
764
  if (stream instanceof handleWraps.TCP) return 'tcp';
765
  if (stream instanceof handleWraps.UDP) return 'udp';
766

    
767
  return false;
768
}
769

    
770

    
771
ChildProcess.prototype.spawn = function(options) {
772
  var self = this,
773
      ipc,
774
      ipcFd,
775
      // If no `stdio` option was given - use default
776
      stdio = options.stdio || 'pipe';
777

    
778
  // Replace shortcut with an array
779
  if (typeof stdio === 'string') {
780
    switch (stdio) {
781
      case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
782
      case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
783
      case 'inherit': stdio = [0, 1, 2]; break;
784
      default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
785
    }
786
  } else if (!Array.isArray(stdio)) {
787
    throw new TypeError('Incorrect value of stdio option: ' + stdio);
788
  }
789

    
790
  // At least 3 stdio will be created
791
  // Don't concat() a new Array() because it would be sparse, and
792
  // stdio.reduce() would skip the sparse elements of stdio.
793
  // See http://stackoverflow.com/a/5501711/3561
794
  while (stdio.length < 3) stdio.push(undefined);
795

    
796
  // Translate stdio into C++-readable form
797
  // (i.e. PipeWraps or fds)
798
  stdio = stdio.reduce(function(acc, stdio, i) {
799
    function cleanup() {
800
      acc.filter(function(stdio) {
801
        return stdio.type === 'pipe' || stdio.type === 'ipc';
802
      }).forEach(function(stdio) {
803
        stdio.handle.close();
804
      });
805
    }
806

    
807
    // Defaults
808
    if (stdio === undefined || stdio === null) {
809
      stdio = i < 3 ? 'pipe' : 'ignore';
810
    }
811

    
812
    if (stdio === 'ignore') {
813
      acc.push({type: 'ignore'});
814
    } else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) {
815
      acc.push({type: 'pipe', handle: createPipe()});
816
    } else if (stdio === 'ipc') {
817
      if (ipc !== undefined) {
818
        // Cleanup previously created pipes
819
        cleanup();
820
        throw Error('Child process can have only one IPC pipe');
821
      }
822

    
823
      ipc = createPipe(true);
824
      ipcFd = i;
825

    
826
      acc.push({ type: 'pipe', handle: ipc, ipc: true });
827
    } else if (typeof stdio === 'number' || typeof stdio.fd === 'number') {
828
      acc.push({ type: 'fd', fd: stdio.fd || stdio });
829
    } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
830
               getHandleWrapType(stdio._handle)) {
831
      var handle = getHandleWrapType(stdio) ?
832
          stdio :
833
          getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
834

    
835
      acc.push({
836
        type: 'wrap',
837
        wrapType: getHandleWrapType(handle),
838
        handle: handle
839
      });
840
    } else {
841
      // Cleanup
842
      cleanup();
843
      throw new TypeError('Incorrect value for stdio stream: ' + stdio);
844
    }
845

    
846
    return acc;
847
  }, []);
848

    
849
  options.stdio = stdio;
850

    
851
  if (ipc !== undefined) {
852
    // Let child process know about opened IPC channel
853
    options.envPairs = options.envPairs || [];
854
    options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
855
  }
856

    
857
  var r = this._handle.spawn(options);
858

    
859
  if (r) {
860
    // Close all opened fds on error
861
    stdio.forEach(function(stdio) {
862
      if (stdio.type === 'pipe') {
863
        stdio.handle.close();
864
      }
865
    });
866

    
867
    this._handle.close();
868
    this._handle = null;
869
    throw errnoException(errno, 'spawn');
870
  }
871

    
872
  this.pid = this._handle.pid;
873

    
874
  stdio.forEach(function(stdio, i) {
875
    if (stdio.type === 'ignore') return;
876

    
877
    if (stdio.ipc) {
878
      self._closesNeeded++;
879
      return;
880
    }
881

    
882
    if (stdio.handle) {
883
      // when i === 0 - we're dealing with stdin
884
      // (which is the only one writable pipe)
885
      stdio.socket = createSocket(stdio.handle, i > 0);
886

    
887
      if (i > 0) {
888
        self._closesNeeded++;
889
        stdio.socket.on('close', function() {
890
          maybeClose(self);
891
        });
892
      }
893
    }
894
  });
895

    
896
  this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ?
897
      stdio[0].socket : null;
898
  this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ?
899
      stdio[1].socket : null;
900
  this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ?
901
      stdio[2].socket : null;
902

    
903
  this.stdio = stdio.map(function(stdio) {
904
    return stdio.socket === undefined ? null : stdio.socket;
905
  });
906

    
907
  // Add .send() method and start listening for IPC data
908
  if (ipc !== undefined) setupChannel(this, ipc);
909

    
910
  return r;
911
};
912

    
913

    
914
function errnoException(errorno, syscall, errmsg) {
915
  // TODO make this more compatible with ErrnoException from src/node.cc
916
  // Once all of Node is using this function the ErrnoException from
917
  // src/node.cc should be removed.
918
  var message = syscall + ' ' + errorno;
919
  if (errmsg) {
920
    message += ' - ' + errmsg;
921
  }
922
  var e = new Error(message);
923
  e.errno = e.code = errorno;
924
  e.syscall = syscall;
925
  return e;
926
}
927

    
928

    
929
ChildProcess.prototype.kill = function(sig) {
930
  var signal;
931

    
932
  if (!constants) {
933
    constants = process.binding('constants');
934
  }
935

    
936
  if (sig === 0) {
937
    signal = 0;
938
  } else if (!sig) {
939
    signal = constants['SIGTERM'];
940
  } else {
941
    signal = constants[sig];
942
  }
943

    
944
  if (signal === undefined) {
945
    throw new Error('Unknown signal: ' + sig);
946
  }
947

    
948
  if (this._handle) {
949
    var r = this._handle.kill(signal);
950
    if (r == 0) {
951
      /* Success. */
952
      this.killed = true;
953
      return true;
954
    } else if (errno == 'ESRCH') {
955
      /* Already dead. */
956
    } else if (errno == 'EINVAL' || errno == 'ENOSYS') {
957
      /* The underlying platform doesn't support this signal. */
958
      throw errnoException(errno, 'kill');
959
    } else {
960
      /* Other error, almost certainly EPERM. */
961
      this.emit('error', errnoException(errno, 'kill'));
962
    }
963
  }
964

    
965
  /* Kill didn't succeed. */
966
  return false;
967
};
968

    
969

    
970
ChildProcess.prototype.ref = function() {
971
  if (this._handle) this._handle.ref();
972
};
973

    
974

    
975
ChildProcess.prototype.unref = function() {
976
  if (this._handle) this._handle.unref();
977
};