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 @ c13354e3

History | View | Annotate | Download (23.9 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

    
161
// This object keep track of the socket there are sended
162
function SocketListSend(slave, key) {
163
  EventEmitter.call(this);
164

    
165
  var self = this;
166

    
167
  this.key = key;
168
  this.slave = slave;
169
}
170
util.inherits(SocketListSend, EventEmitter);
171

    
172
SocketListSend.prototype._request = function(msg, cmd, callback) {
173
  var self = this;
174

    
175
  if (!this.slave.connected) return onclose();
176
  this.slave.send(msg);
177

    
178
  function onclose() {
179
    self.slave.removeListener('internalMessage', onreply);
180
    callback(new Error('Slave closed before reply'));
181
  };
182

    
183
  function onreply(msg) {
184
    if (!(msg.cmd === cmd && msg.key === self.key)) return;
185
    self.slave.removeListener('disconnect', onclose);
186
    self.slave.removeListener('internalMessage', onreply);
187

    
188
    callback(null, msg);
189
  };
190

    
191
  this.slave.once('disconnect', onclose);
192
  this.slave.on('internalMessage', onreply);
193
};
194

    
195
SocketListSend.prototype.close = function close(callback) {
196
  this._request({
197
    cmd: 'NODE_SOCKET_NOTIFY_CLOSE',
198
    key: this.key
199
  }, 'NODE_SOCKET_ALL_CLOSED', callback);
200
};
201

    
202
SocketListSend.prototype.getConnections = function getConnections(callback) {
203
  this._request({
204
    cmd: 'NODE_SOCKET_GET_COUNT',
205
    key: this.key
206
  }, 'NODE_SOCKET_COUNT', function(err, msg) {
207
    if (err) return callback(err);
208
    callback(null, msg.count);
209
  });
210
};
211

    
212
// This object keep track of the socket there are received
213
function SocketListReceive(slave, key) {
214
  EventEmitter.call(this);
215

    
216
  var self = this;
217

    
218
  this.connections = 0;
219
  this.key = key;
220
  this.slave = slave;
221

    
222
  function onempty() {
223
    if (!self.slave.connected) return;
224

    
225
    self.slave.send({
226
      cmd: 'NODE_SOCKET_ALL_CLOSED',
227
      key: self.key
228
    });
229
  }
230

    
231
  this.slave.on('internalMessage', function(msg) {
232
    if (msg.key !== self.key) return;
233

    
234
    if (msg.cmd === 'NODE_SOCKET_NOTIFY_CLOSE') {
235
      // Already empty
236
      if (self.connections === 0) return onempty();
237

    
238
      // Wait for sockets to get closed
239
      self.once('empty', onempty);
240
    } else if (msg.cmd === 'NODE_SOCKET_GET_COUNT') {
241
      if (!self.slave.connected) return;
242
      self.slave.send({
243
        cmd: 'NODE_SOCKET_COUNT',
244
        key: self.key,
245
        count: self.connections
246
      });
247
    }
248
  });
249
}
250
util.inherits(SocketListReceive, EventEmitter);
251

    
252
SocketListReceive.prototype.add = function(obj) {
253
  var self = this;
254

    
255
  this.connections++;
256

    
257
  // Notify previous owner of socket about its state change
258
  obj.socket.once('close', function() {
259
    self.connections--;
260

    
261
    if (self.connections === 0) self.emit('empty');
262
  });
263
};
264

    
265
function getSocketList(type, slave, key) {
266
  var sockets = slave._channel.sockets[type];
267
  var socketList = sockets[key];
268
  if (!socketList) {
269
    var Construct = type === 'send' ? SocketListSend : SocketListReceive;
270
    socketList = sockets[key] = new Construct(slave, key);
271
  }
272
  return socketList;
273
}
274

    
275
function handleMessage(target, message, handle) {
276
  //Filter out internal messages
277
  //if cmd property begin with "_NODE"
278
  if (message !== null &&
279
      typeof message === 'object' &&
280
      typeof message.cmd === 'string' &&
281
      message.cmd.indexOf('NODE_') === 0) {
282
    target.emit('internalMessage', message, handle);
283
  }
284
  //Non-internal message
285
  else {
286
    target.emit('message', message, handle);
287
  }
288
}
289

    
290
function setupChannel(target, channel) {
291
  target._channel = channel;
292

    
293
  var jsonBuffer = '';
294
  channel.buffering = false;
295
  channel.onread = function(pool, offset, length, recvHandle) {
296
    if (pool) {
297
      jsonBuffer += pool.toString('ascii', offset, offset + length);
298

    
299
      var i, start = 0;
300

    
301
      //Linebreak is used as a message end sign
302
      while ((i = jsonBuffer.indexOf('\n', start)) >= 0) {
303
        var json = jsonBuffer.slice(start, i);
304
        var message = JSON.parse(json);
305

    
306
        handleMessage(target, message, recvHandle);
307

    
308
        start = i + 1;
309
      }
310
      jsonBuffer = jsonBuffer.slice(start);
311
      this.buffering = jsonBuffer.length !== 0;
312

    
313
    } else {
314
      this.buffering = false;
315
      target.disconnect();
316
      channel.onread = nop;
317
      channel.close();
318
      maybeClose(target);
319
    }
320
  };
321

    
322
  // object where socket lists will live
323
  channel.sockets = { got: {}, send: {} };
324

    
325
  // handlers will go through this
326
  target.on('internalMessage', function(message, handle) {
327
    if (message.cmd !== 'NODE_HANDLE') return;
328

    
329
    var obj = handleConversion[message.type];
330

    
331
    // Update simultaneous accepts on Windows
332
    if (process.platform === 'win32') {
333
      handle._simultaneousAccepts = false;
334
      net._setSimultaneousAccepts(handle);
335
    }
336

    
337
    // Convert handle object
338
    obj.got.call(this, message, handle, function(handle) {
339
      handleMessage(target, message.msg, handle);
340
    });
341
  });
342

    
343
  target.send = function(message, handle) {
344
    if (typeof message === 'undefined') {
345
      throw new TypeError('message cannot be undefined');
346
    }
347

    
348
    if (!this.connected) {
349
      this.emit('error', new Error('channel closed'));
350
      return;
351
    }
352

    
353
    // package messages with a handle object
354
    if (handle) {
355
      // this message will be handled by an internalMessage event handler
356
      message = {
357
        cmd: 'NODE_HANDLE',
358
        type: 'net.',
359
        msg: message
360
      };
361

    
362
      switch (handle.constructor.name) {
363
        case 'Socket':
364
          message.type += 'Socket'; break;
365
        case 'Server':
366
          message.type += 'Server'; break;
367
        case 'Pipe':
368
        case 'TCP':
369
          message.type += 'Native'; break;
370
      }
371

    
372
      var obj = handleConversion[message.type];
373

    
374
      // convert TCP object to native handle object
375
      handle = handleConversion[message.type].send.apply(target, arguments);
376

    
377
      // Update simultaneous accepts on Windows
378
      if (obj.simultaneousAccepts) {
379
        net._setSimultaneousAccepts(handle);
380
      }
381
    }
382

    
383
    var string = JSON.stringify(message) + '\n';
384
    var writeReq = channel.writeUtf8String(string, handle);
385

    
386
    if (!writeReq) {
387
      var er = errnoException(errno, 'write', 'cannot write to IPC channel.');
388
      this.emit('error', er);
389
    }
390

    
391
    if (obj && obj.postSend) {
392
      writeReq.oncomplete = obj.postSend.bind(null, handle);
393
    } else {
394
      writeReq.oncomplete = nop;
395
    }
396

    
397
    /* If the master is > 2 read() calls behind, please stop sending. */
398
    return channel.writeQueueSize < (65536 * 2);
399
  };
400

    
401
  target.connected = true;
402
  target.disconnect = function() {
403
    if (!this.connected) {
404
      this.emit('error', new Error('IPC channel is already disconnected'));
405
      return;
406
    }
407

    
408
    // do not allow messages to be written
409
    this.connected = false;
410
    this._channel = null;
411

    
412
    var fired = false;
413
    function finish() {
414
      if (fired) return;
415
      fired = true;
416

    
417
      channel.close();
418
      target.emit('disconnect');
419
    }
420

    
421
    // If a message is being read, then wait for it to complete.
422
    if (channel.buffering) {
423
      this.once('message', finish);
424
      this.once('internalMessage', finish);
425

    
426
      return;
427
    }
428

    
429
    finish();
430
  };
431

    
432
  channel.readStart();
433
}
434

    
435

    
436
function nop() { }
437

    
438
exports.fork = function(modulePath /*, args, options*/) {
439

    
440
  // Get options and args arguments.
441
  var options, args, execArgv;
442
  if (Array.isArray(arguments[1])) {
443
    args = arguments[1];
444
    options = util._extend({}, arguments[2]);
445
  } else {
446
    args = [];
447
    options = util._extend({}, arguments[1]);
448
  }
449

    
450
  // Prepare arguments for fork:
451
  execArgv = options.execArgv || process.execArgv;
452
  args = execArgv.concat([modulePath], args);
453

    
454
  // Leave stdin open for the IPC channel. stdout and stderr should be the
455
  // same as the parent's if silent isn't set.
456
  options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
457
      [0, 1, 2, 'ipc'];
458

    
459
  options.execPath = options.execPath || process.execPath;
460

    
461
  return spawn(options.execPath, args, options);
462
};
463

    
464

    
465
exports._forkChild = function(fd) {
466
  // set process.send()
467
  var p = createPipe(true);
468
  p.open(fd);
469
  p.unref();
470
  setupChannel(process, p);
471

    
472
  var refs = 0;
473
  process.on('newListener', function(name) {
474
    if (name !== 'message' && name !== 'disconnect') return;
475
    if (++refs === 1) p.ref();
476
  });
477
  process.on('removeListener', function(name) {
478
    if (name !== 'message' && name !== 'disconnect') return;
479
    if (--refs === 0) p.unref();
480
  });
481
};
482

    
483

    
484
exports.exec = function(command /*, options, callback */) {
485
  var file, args, options, callback;
486

    
487
  if (typeof arguments[1] === 'function') {
488
    options = undefined;
489
    callback = arguments[1];
490
  } else {
491
    options = arguments[1];
492
    callback = arguments[2];
493
  }
494

    
495
  if (process.platform === 'win32') {
496
    file = 'cmd.exe';
497
    args = ['/s', '/c', '"' + command + '"'];
498
    // Make a shallow copy before patching so we don't clobber the user's
499
    // options object.
500
    options = util._extend({}, options);
501
    options.windowsVerbatimArguments = true;
502
  } else {
503
    file = '/bin/sh';
504
    args = ['-c', command];
505
  }
506
  return exports.execFile(file, args, options, callback);
507
};
508

    
509

    
510
exports.execFile = function(file /* args, options, callback */) {
511
  var args, optionArg, callback;
512
  var options = {
513
    encoding: 'utf8',
514
    timeout: 0,
515
    maxBuffer: 200 * 1024,
516
    killSignal: 'SIGTERM',
517
    cwd: null,
518
    env: null
519
  };
520

    
521
  // Parse the parameters.
522

    
523
  if (typeof arguments[arguments.length - 1] === 'function') {
524
    callback = arguments[arguments.length - 1];
525
  }
526

    
527
  if (Array.isArray(arguments[1])) {
528
    args = arguments[1];
529
    options = util._extend(options, arguments[2]);
530
  } else {
531
    args = [];
532
    options = util._extend(options, arguments[1]);
533
  }
534

    
535
  var child = spawn(file, args, {
536
    cwd: options.cwd,
537
    env: options.env,
538
    windowsVerbatimArguments: !!options.windowsVerbatimArguments
539
  });
540

    
541
  var stdout = '';
542
  var stderr = '';
543
  var killed = false;
544
  var exited = false;
545
  var timeoutId;
546

    
547
  var err;
548

    
549
  function exithandler(code, signal) {
550
    if (exited) return;
551
    exited = true;
552

    
553
    if (timeoutId) {
554
      clearTimeout(timeoutId);
555
      timeoutId = null;
556
    }
557

    
558
    if (!callback) return;
559

    
560
    if (err) {
561
      callback(err, stdout, stderr);
562
    } else if (code === 0 && signal === null) {
563
      callback(null, stdout, stderr);
564
    } else {
565
      var e = new Error('Command failed: ' + stderr);
566
      e.killed = child.killed || killed;
567
      e.code = code;
568
      e.signal = signal;
569
      callback(e, stdout, stderr);
570
    }
571
  }
572

    
573
  function errorhandler(e) {
574
    err = e;
575
    child.stdout.destroy();
576
    child.stderr.destroy();
577
    exithandler();
578
  }
579

    
580
  function kill() {
581
    child.stdout.destroy();
582
    child.stderr.destroy();
583

    
584
    killed = true;
585
    try {
586
      child.kill(options.killSignal);
587
    } catch (e) {
588
      err = e;
589
      exithandler();
590
    }
591
  }
592

    
593
  if (options.timeout > 0) {
594
    timeoutId = setTimeout(function() {
595
      kill();
596
      timeoutId = null;
597
    }, options.timeout);
598
  }
599

    
600
  child.stdout.setEncoding(options.encoding);
601
  child.stderr.setEncoding(options.encoding);
602

    
603
  child.stdout.addListener('data', function(chunk) {
604
    stdout += chunk;
605
    if (stdout.length > options.maxBuffer) {
606
      err = new Error('stdout maxBuffer exceeded.');
607
      kill();
608
    }
609
  });
610

    
611
  child.stderr.addListener('data', function(chunk) {
612
    stderr += chunk;
613
    if (stderr.length > options.maxBuffer) {
614
      err = new Error('stderr maxBuffer exceeded.');
615
      kill();
616
    }
617
  });
618

    
619
  child.addListener('close', exithandler);
620
  child.addListener('error', errorhandler);
621

    
622
  return child;
623
};
624

    
625

    
626
var spawn = exports.spawn = function(file, args, options) {
627
  args = args ? args.slice(0) : [];
628
  args.unshift(file);
629

    
630
  var env = (options ? options.env : null) || process.env;
631
  var envPairs = [];
632
  for (var key in env) {
633
    envPairs.push(key + '=' + env[key]);
634
  }
635

    
636
  var child = new ChildProcess();
637
  if (options && options.customFds && !options.stdio) {
638
    options.stdio = options.customFds.map(function(fd) {
639
      return fd === -1 ? 'pipe' : fd;
640
    });
641
  }
642

    
643
  child.spawn({
644
    file: file,
645
    args: args,
646
    cwd: options ? options.cwd : null,
647
    windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
648
    detached: !!(options && options.detached),
649
    envPairs: envPairs,
650
    stdio: options ? options.stdio : null,
651
    uid: options ? options.uid : null,
652
    gid: options ? options.gid : null
653
  });
654

    
655
  return child;
656
};
657

    
658

    
659
function maybeClose(subprocess) {
660
  subprocess._closesGot++;
661

    
662
  if (subprocess._closesGot == subprocess._closesNeeded) {
663
    subprocess.emit('close', subprocess.exitCode, subprocess.signalCode);
664
  }
665
}
666

    
667

    
668
function ChildProcess() {
669
  EventEmitter.call(this);
670

    
671
  // Initialize TCPWrap and PipeWrap
672
  process.binding('tcp_wrap');
673
  process.binding('pipe_wrap');
674

    
675
  var self = this;
676

    
677
  this._closesNeeded = 1;
678
  this._closesGot = 0;
679
  this.connected = false;
680

    
681
  this.signalCode = null;
682
  this.exitCode = null;
683
  this.killed = false;
684

    
685
  this._handle = new Process();
686
  this._handle.owner = this;
687

    
688
  this._handle.onexit = function(exitCode, signalCode) {
689
    //
690
    // follow 0.4.x behaviour:
691
    //
692
    // - normally terminated processes don't touch this.signalCode
693
    // - signaled processes don't touch this.exitCode
694
    //
695
    // new in 0.9.x:
696
    //
697
    // - spawn failures are reported with exitCode == -1
698
    //
699
    var err = (exitCode == -1) ? errnoException(errno, 'spawn') : null;
700

    
701
    if (signalCode) {
702
      self.signalCode = signalCode;
703
    } else {
704
      self.exitCode = exitCode;
705
    }
706

    
707
    if (self.stdin) {
708
      self.stdin.destroy();
709
    }
710

    
711
    self._handle.close();
712
    self._handle = null;
713

    
714
    if (exitCode == -1) {
715
      self.emit('error', err);
716
    } else {
717
      self.emit('exit', self.exitCode, self.signalCode);
718
    }
719

    
720
    // if any of the stdio streams have not been touched,
721
    // then pull all the data through so that it can get the
722
    // eof and emit a 'close' event.
723
    // Do it on nextTick so that the user has one last chance
724
    // to consume the output, if for example they only want to
725
    // start reading the data once the process exits.
726
    process.nextTick(function() {
727
      flushStdio(self);
728
    });
729

    
730
    maybeClose(self);
731
  };
732
}
733
util.inherits(ChildProcess, EventEmitter);
734

    
735

    
736
function flushStdio(subprocess) {
737
  subprocess.stdio.forEach(function(stream, fd, stdio) {
738
    if (!stream || !stream.readable || stream._consuming ||
739
        stream._readableState.flowing)
740
      return;
741
    stream.resume();
742
  });
743
}
744

    
745

    
746

    
747
function getHandleWrapType(stream) {
748
  if (stream instanceof handleWraps.Pipe) return 'pipe';
749
  if (stream instanceof handleWraps.TTY) return 'tty';
750
  if (stream instanceof handleWraps.TCP) return 'tcp';
751
  if (stream instanceof handleWraps.UDP) return 'udp';
752

    
753
  return false;
754
}
755

    
756

    
757
ChildProcess.prototype.spawn = function(options) {
758
  var self = this,
759
      ipc,
760
      ipcFd,
761
      // If no `stdio` option was given - use default
762
      stdio = options.stdio || 'pipe';
763

    
764
  // Replace shortcut with an array
765
  if (typeof stdio === 'string') {
766
    switch (stdio) {
767
      case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
768
      case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
769
      case 'inherit': stdio = [0, 1, 2]; break;
770
      default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
771
    }
772
  } else if (!Array.isArray(stdio)) {
773
    throw new TypeError('Incorrect value of stdio option: ' + stdio);
774
  }
775

    
776
  // At least 3 stdio will be created
777
  // Don't concat() a new Array() because it would be sparse, and
778
  // stdio.reduce() would skip the sparse elements of stdio.
779
  // See http://stackoverflow.com/a/5501711/3561
780
  while (stdio.length < 3) stdio.push(undefined);
781

    
782
  // Translate stdio into C++-readable form
783
  // (i.e. PipeWraps or fds)
784
  stdio = stdio.reduce(function(acc, stdio, i) {
785
    function cleanup() {
786
      acc.filter(function(stdio) {
787
        return stdio.type === 'pipe' || stdio.type === 'ipc';
788
      }).forEach(function(stdio) {
789
        stdio.handle.close();
790
      });
791
    }
792

    
793
    // Defaults
794
    if (stdio === undefined || stdio === null) {
795
      stdio = i < 3 ? 'pipe' : 'ignore';
796
    }
797

    
798
    if (stdio === 'ignore') {
799
      acc.push({type: 'ignore'});
800
    } else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) {
801
      acc.push({type: 'pipe', handle: createPipe()});
802
    } else if (stdio === 'ipc') {
803
      if (ipc !== undefined) {
804
        // Cleanup previously created pipes
805
        cleanup();
806
        throw Error('Child process can have only one IPC pipe');
807
      }
808

    
809
      ipc = createPipe(true);
810
      ipcFd = i;
811

    
812
      acc.push({ type: 'pipe', handle: ipc, ipc: true });
813
    } else if (typeof stdio === 'number' || typeof stdio.fd === 'number') {
814
      acc.push({ type: 'fd', fd: stdio.fd || stdio });
815
    } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
816
               getHandleWrapType(stdio._handle)) {
817
      var handle = getHandleWrapType(stdio) ?
818
          stdio :
819
          getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
820

    
821
      acc.push({
822
        type: 'wrap',
823
        wrapType: getHandleWrapType(handle),
824
        handle: handle
825
      });
826
    } else {
827
      // Cleanup
828
      cleanup();
829
      throw new TypeError('Incorrect value for stdio stream: ' + stdio);
830
    }
831

    
832
    return acc;
833
  }, []);
834

    
835
  options.stdio = stdio;
836

    
837
  if (ipc !== undefined) {
838
    // Let child process know about opened IPC channel
839
    options.envPairs = options.envPairs || [];
840
    options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
841
  }
842

    
843
  var r = this._handle.spawn(options);
844

    
845
  if (r) {
846
    // Close all opened fds on error
847
    stdio.forEach(function(stdio) {
848
      if (stdio.type === 'pipe') {
849
        stdio.handle.close();
850
      }
851
    });
852

    
853
    this._handle.close();
854
    this._handle = null;
855
    throw errnoException(errno, 'spawn');
856
  }
857

    
858
  this.pid = this._handle.pid;
859

    
860
  stdio.forEach(function(stdio, i) {
861
    if (stdio.type === 'ignore') return;
862

    
863
    if (stdio.ipc) {
864
      self._closesNeeded++;
865
      return;
866
    }
867

    
868
    if (stdio.handle) {
869
      // when i === 0 - we're dealing with stdin
870
      // (which is the only one writable pipe)
871
      stdio.socket = createSocket(stdio.handle, i > 0);
872

    
873
      if (i > 0) {
874
        self._closesNeeded++;
875
        stdio.socket.on('close', function() {
876
          maybeClose(self);
877
        });
878
      }
879
    }
880
  });
881

    
882
  this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ?
883
      stdio[0].socket : null;
884
  this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ?
885
      stdio[1].socket : null;
886
  this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ?
887
      stdio[2].socket : null;
888

    
889
  this.stdio = stdio.map(function(stdio) {
890
    return stdio.socket === undefined ? null : stdio.socket;
891
  });
892

    
893
  // Add .send() method and start listening for IPC data
894
  if (ipc !== undefined) setupChannel(this, ipc);
895

    
896
  return r;
897
};
898

    
899

    
900
function errnoException(errorno, syscall, errmsg) {
901
  // TODO make this more compatible with ErrnoException from src/node.cc
902
  // Once all of Node is using this function the ErrnoException from
903
  // src/node.cc should be removed.
904
  var message = syscall + ' ' + errorno;
905
  if (errmsg) {
906
    message += ' - ' + errmsg;
907
  }
908
  var e = new Error(message);
909
  e.errno = e.code = errorno;
910
  e.syscall = syscall;
911
  return e;
912
}
913

    
914

    
915
ChildProcess.prototype.kill = function(sig) {
916
  var signal;
917

    
918
  if (!constants) {
919
    constants = process.binding('constants');
920
  }
921

    
922
  if (sig === 0) {
923
    signal = 0;
924
  } else if (!sig) {
925
    signal = constants['SIGTERM'];
926
  } else {
927
    signal = constants[sig];
928
  }
929

    
930
  if (signal === undefined) {
931
    throw new Error('Unknown signal: ' + sig);
932
  }
933

    
934
  if (this._handle) {
935
    var r = this._handle.kill(signal);
936
    if (r == 0) {
937
      /* Success. */
938
      this.killed = true;
939
      return true;
940
    } else if (errno == 'ESRCH') {
941
      /* Already dead. */
942
    } else if (errno == 'EINVAL' || errno == 'ENOSYS') {
943
      /* The underlying platform doesn't support this signal. */
944
      throw errnoException(errno, 'kill');
945
    } else {
946
      /* Other error, almost certainly EPERM. */
947
      this.emit('error', errnoException(errno, 'kill'));
948
    }
949
  }
950

    
951
  /* Kill didn't succeed. */
952
  return false;
953
};
954

    
955

    
956
ChildProcess.prototype.ref = function() {
957
  if (this._handle) this._handle.ref();
958
};
959

    
960

    
961
ChildProcess.prototype.unref = function() {
962
  if (this._handle) this._handle.unref();
963
};