4840c9e2bac51e2235024b7a6e142f2841325e94
[platform/upstream/nodejs.git] / lib / child_process.js
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 StringDecoder = require('string_decoder').StringDecoder;
23 var EventEmitter = require('events').EventEmitter;
24 var net = require('net');
25 var dgram = require('dgram');
26 var assert = require('assert');
27 var util = require('util');
28
29 var Process = process.binding('process_wrap').Process;
30 var uv = process.binding('uv');
31
32 var spawn_sync; // Lazy-loaded process.binding('spawn_sync')
33 var constants;  // Lazy-loaded process.binding('constants')
34
35 var errnoException = util._errnoException;
36 var handleWraps = {};
37
38 function handleWrapGetter(name, callback) {
39   var cons;
40
41   Object.defineProperty(handleWraps, name, {
42     get: function() {
43       if (!util.isUndefined(cons)) return cons;
44       return cons = callback();
45     }
46   });
47 }
48
49 handleWrapGetter('Pipe', function() {
50   return process.binding('pipe_wrap').Pipe;
51 });
52
53 handleWrapGetter('TTY', function() {
54   return process.binding('tty_wrap').TTY;
55 });
56
57 handleWrapGetter('TCP', function() {
58   return process.binding('tcp_wrap').TCP;
59 });
60
61 handleWrapGetter('UDP', function() {
62   return process.binding('udp_wrap').UDP;
63 });
64
65 // constructors for lazy loading
66 function createPipe(ipc) {
67   return new handleWraps.Pipe(ipc);
68 }
69
70 function createSocket(pipe, readable) {
71   var s = new net.Socket({ handle: pipe });
72
73   if (readable) {
74     s.writable = false;
75     s.readable = true;
76   } else {
77     s.writable = true;
78     s.readable = false;
79   }
80
81   return s;
82 }
83
84
85 // this object contain function to convert TCP objects to native handle objects
86 // and back again.
87 var handleConversion = {
88   'net.Native': {
89     simultaneousAccepts: true,
90
91     send: function(message, handle) {
92       return handle;
93     },
94
95     got: function(message, handle, emit) {
96       emit(handle);
97     }
98   },
99
100   'net.Server': {
101     simultaneousAccepts: true,
102
103     send: function(message, server) {
104       return server._handle;
105     },
106
107     got: function(message, handle, emit) {
108       var server = new net.Server();
109       server.listen(handle, function() {
110         emit(server);
111       });
112     }
113   },
114
115   'net.Socket': {
116     send: function(message, socket) {
117       if (!socket._handle)
118         return;
119
120       // if the socket was created by net.Server
121       if (socket.server) {
122         // the slave should keep track of the socket
123         message.key = socket.server._connectionKey;
124
125         var firstTime = !this._channel.sockets.send[message.key];
126         var socketList = getSocketList('send', this, message.key);
127
128         // the server should no longer expose a .connection property
129         // and when asked to close it should query the socket status from
130         // the slaves
131         if (firstTime) socket.server._setupSlave(socketList);
132
133         // Act like socket is detached
134         socket.server._connections--;
135       }
136
137       // remove handle from socket object, it will be closed when the socket
138       // will be sent
139       var handle = socket._handle;
140       handle.onread = function() {};
141       socket._handle = null;
142
143       return handle;
144     },
145
146     postSend: function(handle) {
147       // Close the Socket handle after sending it
148       if (handle)
149         handle.close();
150     },
151
152     got: function(message, handle, emit) {
153       var socket = new net.Socket({handle: handle});
154       socket.readable = socket.writable = true;
155
156       // if the socket was created by net.Server we will track the socket
157       if (message.key) {
158
159         // add socket to connections list
160         var socketList = getSocketList('got', this, message.key);
161         socketList.add({
162           socket: socket
163         });
164       }
165
166       emit(socket);
167     }
168   },
169
170   'dgram.Native': {
171     simultaneousAccepts: false,
172
173     send: function(message, handle) {
174       return handle;
175     },
176
177     got: function(message, handle, emit) {
178       emit(handle);
179     }
180   },
181
182   'dgram.Socket': {
183     simultaneousAccepts: false,
184
185     send: function(message, socket) {
186       message.dgramType = socket.type;
187
188       return socket._handle;
189     },
190
191     got: function(message, handle, emit) {
192       var socket = new dgram.Socket(message.dgramType);
193
194       socket.bind(handle, function() {
195         emit(socket);
196       });
197     }
198   }
199 };
200
201 // This object keep track of the socket there are sended
202 function SocketListSend(slave, key) {
203   EventEmitter.call(this);
204
205   this.key = key;
206   this.slave = slave;
207 }
208 util.inherits(SocketListSend, EventEmitter);
209
210 SocketListSend.prototype._request = function(msg, cmd, callback) {
211   var self = this;
212
213   if (!this.slave.connected) return onclose();
214   this.slave.send(msg);
215
216   function onclose() {
217     self.slave.removeListener('internalMessage', onreply);
218     callback(new Error('Slave closed before reply'));
219   };
220
221   function onreply(msg) {
222     if (!(msg.cmd === cmd && msg.key === self.key)) return;
223     self.slave.removeListener('disconnect', onclose);
224     self.slave.removeListener('internalMessage', onreply);
225
226     callback(null, msg);
227   };
228
229   this.slave.once('disconnect', onclose);
230   this.slave.on('internalMessage', onreply);
231 };
232
233 SocketListSend.prototype.close = function close(callback) {
234   this._request({
235     cmd: 'NODE_SOCKET_NOTIFY_CLOSE',
236     key: this.key
237   }, 'NODE_SOCKET_ALL_CLOSED', callback);
238 };
239
240 SocketListSend.prototype.getConnections = function getConnections(callback) {
241   this._request({
242     cmd: 'NODE_SOCKET_GET_COUNT',
243     key: this.key
244   }, 'NODE_SOCKET_COUNT', function(err, msg) {
245     if (err) return callback(err);
246     callback(null, msg.count);
247   });
248 };
249
250 // This object keep track of the socket there are received
251 function SocketListReceive(slave, key) {
252   EventEmitter.call(this);
253
254   var self = this;
255
256   this.connections = 0;
257   this.key = key;
258   this.slave = slave;
259
260   function onempty() {
261     if (!self.slave.connected) return;
262
263     self.slave.send({
264       cmd: 'NODE_SOCKET_ALL_CLOSED',
265       key: self.key
266     });
267   }
268
269   this.slave.on('internalMessage', function(msg) {
270     if (msg.key !== self.key) return;
271
272     if (msg.cmd === 'NODE_SOCKET_NOTIFY_CLOSE') {
273       // Already empty
274       if (self.connections === 0) return onempty();
275
276       // Wait for sockets to get closed
277       self.once('empty', onempty);
278     } else if (msg.cmd === 'NODE_SOCKET_GET_COUNT') {
279       if (!self.slave.connected) return;
280       self.slave.send({
281         cmd: 'NODE_SOCKET_COUNT',
282         key: self.key,
283         count: self.connections
284       });
285     }
286   });
287 }
288 util.inherits(SocketListReceive, EventEmitter);
289
290 SocketListReceive.prototype.add = function(obj) {
291   var self = this;
292
293   this.connections++;
294
295   // Notify previous owner of socket about its state change
296   obj.socket.once('close', function() {
297     self.connections--;
298
299     if (self.connections === 0) self.emit('empty');
300   });
301 };
302
303 function getSocketList(type, slave, key) {
304   var sockets = slave._channel.sockets[type];
305   var socketList = sockets[key];
306   if (!socketList) {
307     var Construct = type === 'send' ? SocketListSend : SocketListReceive;
308     socketList = sockets[key] = new Construct(slave, key);
309   }
310   return socketList;
311 }
312
313 var INTERNAL_PREFIX = 'NODE_';
314 function handleMessage(target, message, handle) {
315   var eventName = 'message';
316   if (!util.isNull(message) &&
317       util.isObject(message) &&
318       util.isString(message.cmd) &&
319       message.cmd.length > INTERNAL_PREFIX.length &&
320       message.cmd.slice(0, INTERNAL_PREFIX.length) === INTERNAL_PREFIX) {
321     eventName = 'internalMessage';
322   }
323   target.emit(eventName, message, handle);
324 }
325
326 function setupChannel(target, channel) {
327   target._channel = channel;
328   target._handleQueue = null;
329
330   var decoder = new StringDecoder('utf8');
331   var jsonBuffer = '';
332   channel.buffering = false;
333   channel.onread = function(nread, pool, recvHandle) {
334     // TODO(bnoordhuis) Check that nread > 0.
335     if (pool) {
336       jsonBuffer += decoder.write(pool);
337
338       var i, start = 0;
339
340       //Linebreak is used as a message end sign
341       while ((i = jsonBuffer.indexOf('\n', start)) >= 0) {
342         var json = jsonBuffer.slice(start, i);
343         var message = JSON.parse(json);
344
345         // There will be at most one NODE_HANDLE message in every chunk we
346         // read because SCM_RIGHTS messages don't get coalesced. Make sure
347         // that we deliver the handle with the right message however.
348         if (message && message.cmd === 'NODE_HANDLE')
349           handleMessage(target, message, recvHandle);
350         else
351           handleMessage(target, message, undefined);
352
353         start = i + 1;
354       }
355       jsonBuffer = jsonBuffer.slice(start);
356       this.buffering = jsonBuffer.length !== 0;
357
358     } else {
359       this.buffering = false;
360       target.disconnect();
361       channel.onread = nop;
362       channel.close();
363       maybeClose(target);
364     }
365   };
366
367   // object where socket lists will live
368   channel.sockets = { got: {}, send: {} };
369
370   // handlers will go through this
371   target.on('internalMessage', function(message, handle) {
372     // Once acknowledged - continue sending handles.
373     if (message.cmd === 'NODE_HANDLE_ACK') {
374       assert(util.isArray(target._handleQueue));
375       var queue = target._handleQueue;
376       target._handleQueue = null;
377
378       queue.forEach(function(args) {
379         target._send(args.message, args.handle, false);
380       });
381
382       // Process a pending disconnect (if any).
383       if (!target.connected && target._channel && !target._handleQueue)
384         target._disconnect();
385
386       return;
387     }
388
389     if (message.cmd !== 'NODE_HANDLE') return;
390
391     // Acknowledge handle receival. Don't emit error events (for example if
392     // the other side has disconnected) because this call to send() is not
393     // initiated by the user and it shouldn't be fatal to be unable to ACK
394     // a message.
395     target._send({ cmd: 'NODE_HANDLE_ACK' }, null, true);
396
397     var obj = handleConversion[message.type];
398
399     // Update simultaneous accepts on Windows
400     if (process.platform === 'win32') {
401       handle._simultaneousAccepts = false;
402       net._setSimultaneousAccepts(handle);
403     }
404
405     // Convert handle object
406     obj.got.call(this, message, handle, function(handle) {
407       handleMessage(target, message.msg, handle);
408     });
409   });
410
411   target.send = function(message, handle) {
412     if (!this.connected)
413       this.emit('error', new Error('channel closed'));
414     else
415       this._send(message, handle, false);
416   };
417
418   target._send = function(message, handle, swallowErrors) {
419     assert(this.connected || this._channel);
420
421     if (util.isUndefined(message))
422       throw new TypeError('message cannot be undefined');
423
424     // package messages with a handle object
425     if (handle) {
426       // this message will be handled by an internalMessage event handler
427       message = {
428         cmd: 'NODE_HANDLE',
429         type: null,
430         msg: message
431       };
432
433       if (handle instanceof net.Socket) {
434         message.type = 'net.Socket';
435       } else if (handle instanceof net.Server) {
436         message.type = 'net.Server';
437       } else if (handle instanceof process.binding('tcp_wrap').TCP ||
438                  handle instanceof process.binding('pipe_wrap').Pipe) {
439         message.type = 'net.Native';
440       } else if (handle instanceof dgram.Socket) {
441         message.type = 'dgram.Socket';
442       } else if (handle instanceof process.binding('udp_wrap').UDP) {
443         message.type = 'dgram.Native';
444       } else {
445         throw new TypeError("This handle type can't be sent");
446       }
447
448       // Queue-up message and handle if we haven't received ACK yet.
449       if (this._handleQueue) {
450         this._handleQueue.push({ message: message.msg, handle: handle });
451         return;
452       }
453
454       var obj = handleConversion[message.type];
455
456       // convert TCP object to native handle object
457       handle =
458           handleConversion[message.type].send.call(target, message, handle);
459
460       // If handle was sent twice, or it is impossible to get native handle
461       // out of it - just send a text without the handle.
462       if (!handle)
463         message = message.msg;
464
465       // Update simultaneous accepts on Windows
466       if (obj.simultaneousAccepts) {
467         net._setSimultaneousAccepts(handle);
468       }
469     } else if (this._handleQueue &&
470                !(message && message.cmd === 'NODE_HANDLE_ACK')) {
471       // Queue request anyway to avoid out-of-order messages.
472       this._handleQueue.push({ message: message, handle: null });
473       return;
474     }
475
476     var req = { oncomplete: nop };
477     var string = JSON.stringify(message) + '\n';
478     var err = channel.writeUtf8String(req, string, handle);
479
480     if (err) {
481       if (!swallowErrors)
482         this.emit('error', errnoException(err, 'write'));
483     } else if (handle && !this._handleQueue) {
484       this._handleQueue = [];
485     }
486
487     if (obj && obj.postSend) {
488       req.oncomplete = obj.postSend.bind(null, handle);
489     }
490
491     /* If the master is > 2 read() calls behind, please stop sending. */
492     return channel.writeQueueSize < (65536 * 2);
493   };
494
495   // connected will be set to false immediately when a disconnect() is
496   // requested, even though the channel might still be alive internally to
497   // process queued messages. The three states are distinguished as follows:
498   // - disconnect() never requested: _channel is not null and connected
499   //   is true
500   // - disconnect() requested, messages in the queue: _channel is not null
501   //   and connected is false
502   // - disconnect() requested, channel actually disconnected: _channel is
503   //   null and connected is false
504   target.connected = true;
505
506   target.disconnect = function() {
507     if (!this.connected) {
508       this.emit('error', new Error('IPC channel is already disconnected'));
509       return;
510     }
511
512     // Do not allow any new messages to be written.
513     this.connected = false;
514
515     // If there are no queued messages, disconnect immediately. Otherwise,
516     // postpone the disconnect so that it happens internally after the
517     // queue is flushed.
518     if (!this._handleQueue)
519       this._disconnect();
520   };
521
522   target._disconnect = function() {
523     assert(this._channel);
524
525     // This marks the fact that the channel is actually disconnected.
526     this._channel = null;
527
528     var fired = false;
529     function finish() {
530       if (fired) return;
531       fired = true;
532
533       channel.close();
534       target.emit('disconnect');
535     }
536
537     // If a message is being read, then wait for it to complete.
538     if (channel.buffering) {
539       this.once('message', finish);
540       this.once('internalMessage', finish);
541
542       return;
543     }
544
545     process.nextTick(finish);
546   };
547
548   channel.readStart();
549 }
550
551
552 function nop() { }
553
554 exports.fork = function(modulePath /*, args, options*/) {
555
556   // Get options and args arguments.
557   var options, args, execArgv;
558   if (util.isArray(arguments[1])) {
559     args = arguments[1];
560     options = util._extend({}, arguments[2]);
561   } else {
562     args = [];
563     options = util._extend({}, arguments[1]);
564   }
565
566   // Prepare arguments for fork:
567   execArgv = options.execArgv || process.execArgv;
568   args = execArgv.concat([modulePath], args);
569
570   // Leave stdin open for the IPC channel. stdout and stderr should be the
571   // same as the parent's if silent isn't set.
572   options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
573       [0, 1, 2, 'ipc'];
574
575   options.execPath = options.execPath || process.execPath;
576
577   return spawn(options.execPath, args, options);
578 };
579
580
581 exports._forkChild = function(fd) {
582   // set process.send()
583   var p = createPipe(true);
584   p.open(fd);
585   p.unref();
586   setupChannel(process, p);
587
588   var refs = 0;
589   process.on('newListener', function(name) {
590     if (name !== 'message' && name !== 'disconnect') return;
591     if (++refs === 1) p.ref();
592   });
593   process.on('removeListener', function(name) {
594     if (name !== 'message' && name !== 'disconnect') return;
595     if (--refs === 0) p.unref();
596   });
597 };
598
599
600 function normalizeExecArgs(command /*, options, callback */) {
601   var file, args, options, callback;
602
603   if (util.isFunction(arguments[1])) {
604     options = undefined;
605     callback = arguments[1];
606   } else {
607     options = arguments[1];
608     callback = arguments[2];
609   }
610
611   if (process.platform === 'win32') {
612     file = process.env.comspec || 'cmd.exe';
613     args = ['/s', '/c', '"' + command + '"'];
614     // Make a shallow copy before patching so we don't clobber the user's
615     // options object.
616     options = util._extend({}, options);
617     options.windowsVerbatimArguments = true;
618   } else {
619     file = '/bin/sh';
620     args = ['-c', command];
621   }
622
623   if (options && options.shell)
624     file = options.shell;
625
626   return {
627     cmd: command,
628     file: file,
629     args: args,
630     options: options,
631     callback: callback
632   };
633 }
634
635
636 exports.exec = function(command /*, options, callback */) {
637   var opts = normalizeExecArgs.apply(null, arguments);
638   return exports.execFile(opts.file,
639                           opts.args,
640                           opts.options,
641                           opts.callback);
642 };
643
644
645 exports.execFile = function(file /* args, options, callback */) {
646   var args, callback;
647   var options = {
648     encoding: 'utf8',
649     timeout: 0,
650     maxBuffer: 200 * 1024,
651     killSignal: 'SIGTERM',
652     cwd: null,
653     env: null
654   };
655
656   // Parse the parameters.
657
658   if (util.isFunction(arguments[arguments.length - 1])) {
659     callback = arguments[arguments.length - 1];
660   }
661
662   if (util.isArray(arguments[1])) {
663     args = arguments[1];
664     options = util._extend(options, arguments[2]);
665   } else {
666     args = [];
667     options = util._extend(options, arguments[1]);
668   }
669
670   var child = spawn(file, args, {
671     cwd: options.cwd,
672     env: options.env,
673     gid: options.gid,
674     uid: options.uid,
675     windowsVerbatimArguments: !!options.windowsVerbatimArguments
676   });
677
678   var encoding;
679   var _stdout;
680   var _stderr;
681   if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) {
682     encoding = options.encoding;
683     _stdout = '';
684     _stderr = '';
685   } else {
686     _stdout = [];
687     _stderr = [];
688     encoding = null;
689   }
690   var stdoutLen = 0;
691   var stderrLen = 0;
692   var killed = false;
693   var exited = false;
694   var timeoutId;
695
696   var ex = null;
697
698   function exithandler(code, signal) {
699     if (exited) return;
700     exited = true;
701
702     if (timeoutId) {
703       clearTimeout(timeoutId);
704       timeoutId = null;
705     }
706
707     if (!callback) return;
708
709     // merge chunks
710     var stdout;
711     var stderr;
712     if (!encoding) {
713       stdout = Buffer.concat(_stdout);
714       stderr = Buffer.concat(_stderr);
715     } else {
716       stdout = _stdout;
717       stderr = _stderr;
718     }
719
720     if (ex) {
721       // Will be handled later
722     } else if (code === 0 && signal === null) {
723       callback(null, stdout, stderr);
724       return;
725     }
726
727     var cmd = file;
728     if (args.length !== 0)
729       cmd += ' ' + args.join(' ');
730
731     if (!ex) {
732       ex = new Error('Command failed: ' + cmd + '\n' + stderr);
733       ex.killed = child.killed || killed;
734       ex.code = code < 0 ? uv.errname(code) : code;
735       ex.signal = signal;
736     }
737
738     ex.cmd = cmd;
739     callback(ex, stdout, stderr);
740   }
741
742   function errorhandler(e) {
743     ex = e;
744     child.stdout.destroy();
745     child.stderr.destroy();
746     exithandler();
747   }
748
749   function kill() {
750     child.stdout.destroy();
751     child.stderr.destroy();
752
753     killed = true;
754     try {
755       child.kill(options.killSignal);
756     } catch (e) {
757       ex = e;
758       exithandler();
759     }
760   }
761
762   if (options.timeout > 0) {
763     timeoutId = setTimeout(function() {
764       kill();
765       timeoutId = null;
766     }, options.timeout);
767   }
768
769   child.stdout.addListener('data', function(chunk) {
770     stdoutLen += chunk.length;
771
772     if (stdoutLen > options.maxBuffer) {
773       ex = new Error('stdout maxBuffer exceeded.');
774       kill();
775     } else {
776       if (!encoding)
777         _stdout.push(chunk);
778       else
779         _stdout += chunk;
780     }
781   });
782
783   child.stderr.addListener('data', function(chunk) {
784     stderrLen += chunk.length;
785
786     if (stderrLen > options.maxBuffer) {
787       ex = new Error('stderr maxBuffer exceeded.');
788       kill();
789     } else {
790       if (!encoding)
791         _stderr.push(chunk);
792       else
793         _stderr += chunk;
794     }
795   });
796
797   if (encoding) {
798     child.stderr.setEncoding(encoding);
799     child.stdout.setEncoding(encoding);
800   }
801
802   child.addListener('close', exithandler);
803   child.addListener('error', errorhandler);
804
805   return child;
806 };
807
808 var _deprecatedCustomFds = util.deprecate(function(options) {
809   options.stdio = options.customFds.map(function(fd) {
810     return fd === -1 ? 'pipe' : fd;
811   });
812 }, 'child_process: customFds option is deprecated, use stdio instead.');
813
814 function _convertCustomFds(options) {
815   if (options && options.customFds && !options.stdio) {
816     _deprecatedCustomFds(options);
817   }
818 }
819
820
821 function _validateStdio(stdio, sync) {
822   var ipc,
823       ipcFd;
824
825   // Replace shortcut with an array
826   if (util.isString(stdio)) {
827     switch (stdio) {
828       case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
829       case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
830       case 'inherit': stdio = [0, 1, 2]; break;
831       default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
832     }
833   } else if (!util.isArray(stdio)) {
834     throw new TypeError('Incorrect value of stdio option: ' +
835         util.inspect(stdio));
836   }
837
838   // At least 3 stdio will be created
839   // Don't concat() a new Array() because it would be sparse, and
840   // stdio.reduce() would skip the sparse elements of stdio.
841   // See http://stackoverflow.com/a/5501711/3561
842   while (stdio.length < 3) stdio.push(undefined);
843
844   // Translate stdio into C++-readable form
845   // (i.e. PipeWraps or fds)
846   stdio = stdio.reduce(function(acc, stdio, i) {
847     function cleanup() {
848       acc.filter(function(stdio) {
849         return stdio.type === 'pipe' || stdio.type === 'ipc';
850       }).forEach(function(stdio) {
851         if (stdio.handle)
852           stdio.handle.close();
853       });
854     }
855
856     // Defaults
857     if (util.isNullOrUndefined(stdio)) {
858       stdio = i < 3 ? 'pipe' : 'ignore';
859     }
860
861     if (stdio === null || stdio === 'ignore') {
862       acc.push({type: 'ignore'});
863     } else if (stdio === 'pipe' || util.isNumber(stdio) && stdio < 0) {
864       var a = {
865         type: 'pipe',
866         readable: i === 0,
867         writable: i !== 0
868       };
869
870       if (!sync)
871         a.handle = createPipe();
872
873       acc.push(a);
874     } else if (stdio === 'ipc') {
875       if (sync || !util.isUndefined(ipc)) {
876         // Cleanup previously created pipes
877         cleanup();
878         if (!sync)
879           throw Error('Child process can have only one IPC pipe');
880         else
881           throw Error('You cannot use IPC with synchronous forks');
882       }
883
884       ipc = createPipe(true);
885       ipcFd = i;
886
887       acc.push({
888         type: 'pipe',
889         handle: ipc,
890         ipc: true
891       });
892     } else if (stdio === 'inherit') {
893       acc.push({
894         type: 'inherit',
895         fd: i
896       });
897     } else if (util.isNumber(stdio) || util.isNumber(stdio.fd)) {
898       acc.push({
899         type: 'fd',
900         fd: stdio.fd || stdio
901       });
902     } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
903                getHandleWrapType(stdio._handle)) {
904       var handle = getHandleWrapType(stdio) ?
905           stdio :
906           getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
907
908       acc.push({
909         type: 'wrap',
910         wrapType: getHandleWrapType(handle),
911         handle: handle
912       });
913     } else if (util.isBuffer(stdio) || util.isString(stdio)) {
914       if (!sync) {
915         cleanup();
916         throw new TypeError('Asynchronous forks do not support Buffer input: ' +
917             util.inspect(stdio));
918       }
919     } else {
920       // Cleanup
921       cleanup();
922       throw new TypeError('Incorrect value for stdio stream: ' +
923           util.inspect(stdio));
924     }
925
926     return acc;
927   }, []);
928
929   return {stdio: stdio, ipc: ipc, ipcFd: ipcFd};
930 }
931
932
933 function normalizeSpawnArguments(file /*, args, options*/) {
934   var args, options;
935
936   if (Array.isArray(arguments[1])) {
937     args = arguments[1].slice(0);
938     options = arguments[2];
939   } else if (arguments[1] !== undefined && !util.isObject(arguments[1])) {
940     throw new TypeError('Incorrect value of args option');
941   } else {
942     args = [];
943     options = arguments[1];
944   }
945
946   if (options === undefined)
947     options = {};
948   else if (!util.isObject(options))
949     throw new TypeError('options argument must be an object');
950
951   args.unshift(file);
952
953   var env = options.env || process.env;
954   var envPairs = [];
955
956   for (var key in env) {
957     envPairs.push(key + '=' + env[key]);
958   }
959
960   _convertCustomFds(options);
961
962   return {
963     file: file,
964     args: args,
965     options: options,
966     envPairs: envPairs
967   };
968 }
969
970
971 var spawn = exports.spawn = function(/*file, args, options*/) {
972   var opts = normalizeSpawnArguments.apply(null, arguments);
973   var options = opts.options;
974   var child = new ChildProcess();
975
976   child.spawn({
977     file: opts.file,
978     args: opts.args,
979     cwd: options.cwd,
980     windowsVerbatimArguments: !!options.windowsVerbatimArguments,
981     detached: !!options.detached,
982     envPairs: opts.envPairs,
983     stdio: options.stdio,
984     uid: options.uid,
985     gid: options.gid
986   });
987
988   return child;
989 };
990
991
992 function maybeClose(subprocess) {
993   subprocess._closesGot++;
994
995   if (subprocess._closesGot == subprocess._closesNeeded) {
996     subprocess.emit('close', subprocess.exitCode, subprocess.signalCode);
997   }
998 }
999
1000
1001 function ChildProcess() {
1002   EventEmitter.call(this);
1003
1004   // Initialize TCPWrap and PipeWrap
1005   process.binding('tcp_wrap');
1006   process.binding('pipe_wrap');
1007
1008   var self = this;
1009
1010   this._closesNeeded = 1;
1011   this._closesGot = 0;
1012   this.connected = false;
1013
1014   this.signalCode = null;
1015   this.exitCode = null;
1016   this.killed = false;
1017   this.spawnfile = null;
1018
1019   this._handle = new Process();
1020   this._handle.owner = this;
1021
1022   this._handle.onexit = function(exitCode, signalCode) {
1023     //
1024     // follow 0.4.x behaviour:
1025     //
1026     // - normally terminated processes don't touch this.signalCode
1027     // - signaled processes don't touch this.exitCode
1028     //
1029     // new in 0.9.x:
1030     //
1031     // - spawn failures are reported with exitCode < 0
1032     //
1033     var syscall = self.spawnfile ? 'spawn ' + self.spawnfile : 'spawn';
1034     var err = (exitCode < 0) ? errnoException(exitCode, syscall) : null;
1035
1036     if (signalCode) {
1037       self.signalCode = signalCode;
1038     } else {
1039       self.exitCode = exitCode;
1040     }
1041
1042     if (self.stdin) {
1043       self.stdin.destroy();
1044     }
1045
1046     self._handle.close();
1047     self._handle = null;
1048
1049     if (exitCode < 0) {
1050       if (self.spawnfile)
1051         err.path = self.spawnfile;
1052
1053       self.emit('error', err);
1054     } else {
1055       self.emit('exit', self.exitCode, self.signalCode);
1056     }
1057
1058     // if any of the stdio streams have not been touched,
1059     // then pull all the data through so that it can get the
1060     // eof and emit a 'close' event.
1061     // Do it on nextTick so that the user has one last chance
1062     // to consume the output, if for example they only want to
1063     // start reading the data once the process exits.
1064     process.nextTick(function() {
1065       flushStdio(self);
1066     });
1067
1068     maybeClose(self);
1069   };
1070 }
1071 util.inherits(ChildProcess, EventEmitter);
1072
1073
1074 function flushStdio(subprocess) {
1075   if (subprocess.stdio == null) return;
1076   subprocess.stdio.forEach(function(stream, fd, stdio) {
1077     if (!stream || !stream.readable || stream._consuming ||
1078         stream._readableState.flowing)
1079       return;
1080     stream.resume();
1081   });
1082 }
1083
1084
1085
1086 function getHandleWrapType(stream) {
1087   if (stream instanceof handleWraps.Pipe) return 'pipe';
1088   if (stream instanceof handleWraps.TTY) return 'tty';
1089   if (stream instanceof handleWraps.TCP) return 'tcp';
1090   if (stream instanceof handleWraps.UDP) return 'udp';
1091
1092   return false;
1093 }
1094
1095
1096 ChildProcess.prototype.spawn = function(options) {
1097   var self = this,
1098       ipc,
1099       ipcFd,
1100       // If no `stdio` option was given - use default
1101       stdio = options.stdio || 'pipe';
1102
1103   stdio = _validateStdio(stdio, false);
1104
1105   ipc = stdio.ipc;
1106   ipcFd = stdio.ipcFd;
1107   stdio = options.stdio = stdio.stdio;
1108
1109   if (!util.isUndefined(ipc)) {
1110     // Let child process know about opened IPC channel
1111     options.envPairs = options.envPairs || [];
1112     options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
1113   }
1114
1115   this.spawnfile = options.file;
1116
1117   var err = this._handle.spawn(options);
1118
1119   // Run-time errors should emit an error, not throw an exception.
1120   if (err === uv.UV_EAGAIN ||
1121       err === uv.UV_EMFILE ||
1122       err === uv.UV_ENFILE ||
1123       err === uv.UV_ENOENT) {
1124     process.nextTick(function() {
1125       self._handle.onexit(err);
1126     });
1127     // There is no point in continuing when we've hit EMFILE or ENFILE
1128     // because we won't be able to set up the stdio file descriptors.
1129     // It's kind of silly that the de facto spec for ENOENT (the test suite)
1130     // mandates that stdio _is_ set up, even if there is no process on the
1131     // receiving end, but it is what it is.
1132     if (err !== uv.UV_ENOENT) return err;
1133   } else if (err) {
1134     // Close all opened fds on error
1135     stdio.forEach(function(stdio) {
1136       if (stdio.type === 'pipe') {
1137         stdio.handle.close();
1138       }
1139     });
1140
1141     this._handle.close();
1142     this._handle = null;
1143     throw errnoException(err, 'spawn');
1144   }
1145
1146   this.pid = this._handle.pid;
1147
1148   stdio.forEach(function(stdio, i) {
1149     if (stdio.type === 'ignore') return;
1150
1151     if (stdio.ipc) {
1152       self._closesNeeded++;
1153       return;
1154     }
1155
1156     if (stdio.handle) {
1157       // when i === 0 - we're dealing with stdin
1158       // (which is the only one writable pipe)
1159       stdio.socket = createSocket(self.pid !== 0 ? stdio.handle : null, i > 0);
1160
1161       if (i > 0 && self.pid !== 0) {
1162         self._closesNeeded++;
1163         stdio.socket.on('close', function() {
1164           maybeClose(self);
1165         });
1166       }
1167     }
1168   });
1169
1170   this.stdin = stdio.length >= 1 && !util.isUndefined(stdio[0].socket) ?
1171       stdio[0].socket : null;
1172   this.stdout = stdio.length >= 2 && !util.isUndefined(stdio[1].socket) ?
1173       stdio[1].socket : null;
1174   this.stderr = stdio.length >= 3 && !util.isUndefined(stdio[2].socket) ?
1175       stdio[2].socket : null;
1176
1177   this.stdio = stdio.map(function(stdio) {
1178     return util.isUndefined(stdio.socket) ? null : stdio.socket;
1179   });
1180
1181   // Add .send() method and start listening for IPC data
1182   if (!util.isUndefined(ipc)) setupChannel(this, ipc);
1183
1184   return err;
1185 };
1186
1187
1188 ChildProcess.prototype.kill = function(sig) {
1189   var signal;
1190
1191   if (!constants) {
1192     constants = process.binding('constants');
1193   }
1194
1195   if (sig === 0) {
1196     signal = 0;
1197   } else if (!sig) {
1198     signal = constants['SIGTERM'];
1199   } else {
1200     signal = constants[sig];
1201   }
1202
1203   if (util.isUndefined(signal)) {
1204     throw new Error('Unknown signal: ' + sig);
1205   }
1206
1207   if (this._handle) {
1208     var err = this._handle.kill(signal);
1209     if (err === 0) {
1210       /* Success. */
1211       this.killed = true;
1212       return true;
1213     }
1214     if (err === uv.UV_ESRCH) {
1215       /* Already dead. */
1216     } else if (err === uv.UV_EINVAL || err === uv.UV_ENOSYS) {
1217       /* The underlying platform doesn't support this signal. */
1218       throw errnoException(err, 'kill');
1219     } else {
1220       /* Other error, almost certainly EPERM. */
1221       this.emit('error', errnoException(err, 'kill'));
1222     }
1223   }
1224
1225   /* Kill didn't succeed. */
1226   return false;
1227 };
1228
1229
1230 ChildProcess.prototype.ref = function() {
1231   if (this._handle) this._handle.ref();
1232 };
1233
1234
1235 ChildProcess.prototype.unref = function() {
1236   if (this._handle) this._handle.unref();
1237 };
1238
1239
1240 function lookupSignal(signal) {
1241   if (typeof signal === 'number')
1242     return signal;
1243
1244   if (!constants)
1245     constants = process.binding('constants');
1246
1247   if (!(signal in constants))
1248     throw new Error('Unknown signal: ' + signal);
1249
1250   return constants[signal];
1251 }
1252
1253
1254 function spawnSync(/*file, args, options*/) {
1255   var opts = normalizeSpawnArguments.apply(null, arguments);
1256
1257   var options = opts.options;
1258
1259   var i;
1260
1261   options.file = opts.file;
1262   options.args = opts.args;
1263
1264   if (options.killSignal)
1265     options.killSignal = lookupSignal(options.killSignal);
1266
1267   options.stdio = _validateStdio(options.stdio || 'pipe', true).stdio;
1268
1269   if (options.input) {
1270     var stdin = options.stdio[0] = util._extend({}, options.stdio[0]);
1271     stdin.input = options.input;
1272   }
1273
1274   // We may want to pass data in on any given fd, ensure it is a valid buffer
1275   for (i = 0; i < options.stdio.length; i++) {
1276     var input = options.stdio[i] && options.stdio[i].input;
1277     if (input != null) {
1278       var pipe = options.stdio[i] = util._extend({}, options.stdio[i]);
1279       if (Buffer.isBuffer(input))
1280         pipe.input = input;
1281       else if (util.isString(input))
1282         pipe.input = new Buffer(input, options.encoding);
1283       else
1284         throw new TypeError(util.format(
1285             'stdio[%d] should be Buffer or string not %s',
1286             i,
1287             typeof input));
1288     }
1289   }
1290
1291   if (!spawn_sync)
1292     spawn_sync = process.binding('spawn_sync');
1293
1294   var result = spawn_sync.spawn(options);
1295
1296   if (result.output && options.encoding) {
1297     for (i = 0; i < result.output.length; i++) {
1298       if (!result.output[i])
1299         continue;
1300       result.output[i] = result.output[i].toString(options.encoding);
1301     }
1302   }
1303
1304   result.stdout = result.output && result.output[1];
1305   result.stderr = result.output && result.output[2];
1306
1307   if (result.error)
1308     result.error = errnoException(result.error, 'spawnSync');
1309
1310   util._extend(result, opts);
1311
1312   return result;
1313 }
1314 exports.spawnSync = spawnSync;
1315
1316
1317 function checkExecSyncError(ret) {
1318   if (ret.error || ret.status !== 0) {
1319     var err = ret.error;
1320     ret.error = null;
1321
1322     if (!err) {
1323       var msg = 'Command failed: ' +
1324                 (ret.cmd ? ret.cmd : ret.args.join(' ')) +
1325                 (ret.stderr ? '\n' + ret.stderr.toString() : '');
1326       err = new Error(msg);
1327     }
1328
1329     util._extend(err, ret);
1330     return err;
1331   }
1332
1333   return false;
1334 }
1335
1336
1337 function execFileSync(/*command, options*/) {
1338   var opts = normalizeSpawnArguments.apply(null, arguments);
1339   var inheritStderr = !opts.options.stdio;
1340
1341   var ret = spawnSync(opts.file, opts.args.slice(1), opts.options);
1342
1343   if (inheritStderr)
1344     process.stderr.write(ret.stderr);
1345
1346   var err = checkExecSyncError(ret);
1347
1348   if (err)
1349     throw err;
1350   else
1351     return ret.stdout;
1352 }
1353 exports.execFileSync = execFileSync;
1354
1355
1356 function execSync(/*comand, options*/) {
1357   var opts = normalizeExecArgs.apply(null, arguments);
1358   var inheritStderr = opts.options ? !opts.options.stdio : true;
1359
1360   var ret = spawnSync(opts.file, opts.args, opts.options);
1361   ret.cmd = opts.cmd;
1362
1363   if (inheritStderr)
1364     process.stderr.write(ret.stderr);
1365
1366   var err = checkExecSyncError(ret);
1367
1368   if (err)
1369     throw err;
1370   else
1371     return ret.stdout;
1372 }
1373 exports.execSync = execSync;