macgyver 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/README.md +45 -0
  6. data/Rakefile +1 -0
  7. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
  8. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
  9. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
  10. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
  11. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
  12. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
  13. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
  14. data/assets/MacGap.app/Contents/Info.plist +48 -0
  15. data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
  16. data/assets/MacGap.app/Contents/PkgInfo +1 -0
  17. data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
  18. data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
  19. data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
  20. data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
  21. data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
  22. data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
  23. data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
  24. data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
  25. data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
  26. data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
  27. data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
  28. data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
  29. data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
  30. data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
  31. data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
  32. data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
  33. data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
  34. data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
  35. data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
  36. data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
  37. data/assets/MacGap.app/Contents/Resources/console.js +108 -0
  38. data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
  39. data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
  40. data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
  41. data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
  42. data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
  43. data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
  44. data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
  45. data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
  46. data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
  47. data/assets/MacGap.app/Contents/Resources/events.js +312 -0
  48. data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
  49. data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
  50. data/assets/MacGap.app/Contents/Resources/http.js +119 -0
  51. data/assets/MacGap.app/Contents/Resources/https.js +134 -0
  52. data/assets/MacGap.app/Contents/Resources/module.js +529 -0
  53. data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
  54. data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
  55. data/assets/MacGap.app/Contents/Resources/os.js +64 -0
  56. data/assets/MacGap.app/Contents/Resources/path.js +517 -0
  57. data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
  58. data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
  59. data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
  60. data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
  61. data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
  62. data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
  63. data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
  64. data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
  65. data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
  66. data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
  67. data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
  68. data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
  69. data/assets/MacGap.app/Contents/Resources/url.js +693 -0
  70. data/assets/MacGap.app/Contents/Resources/util.js +688 -0
  71. data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
  72. data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
  73. data/assets/index.html +38 -0
  74. data/bin/macgyver +104 -0
  75. data/macgyver.gemspec +19 -0
  76. data/test/public/index.html +27 -0
  77. metadata +121 -0
@@ -0,0 +1,1107 @@
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 constants; // Lazy-loaded process.binding('constants')
33
+
34
+ var errnoException = util._errnoException;
35
+ var handleWraps = {};
36
+
37
+ function handleWrapGetter(name, callback) {
38
+ var cons;
39
+
40
+ Object.defineProperty(handleWraps, name, {
41
+ get: function() {
42
+ if (!util.isUndefined(cons)) return cons;
43
+ return cons = callback();
44
+ }
45
+ });
46
+ }
47
+
48
+ handleWrapGetter('Pipe', function() {
49
+ return process.binding('pipe_wrap').Pipe;
50
+ });
51
+
52
+ handleWrapGetter('TTY', function() {
53
+ return process.binding('tty_wrap').TTY;
54
+ });
55
+
56
+ handleWrapGetter('TCP', function() {
57
+ return process.binding('tcp_wrap').TCP;
58
+ });
59
+
60
+ handleWrapGetter('UDP', function() {
61
+ return process.binding('udp_wrap').UDP;
62
+ });
63
+
64
+ // constructors for lazy loading
65
+ function createPipe(ipc) {
66
+ return new handleWraps.Pipe(ipc);
67
+ }
68
+
69
+ function createSocket(pipe, readable) {
70
+ var s = new net.Socket({ handle: pipe });
71
+
72
+ if (readable) {
73
+ s.writable = false;
74
+ s.readable = true;
75
+ } else {
76
+ s.writable = true;
77
+ s.readable = false;
78
+ }
79
+
80
+ return s;
81
+ }
82
+
83
+
84
+ // this object contain function to convert TCP objects to native handle objects
85
+ // and back again.
86
+ var handleConversion = {
87
+ 'net.Native': {
88
+ simultaneousAccepts: true,
89
+
90
+ send: function(message, handle) {
91
+ return handle;
92
+ },
93
+
94
+ got: function(message, handle, emit) {
95
+ emit(handle);
96
+ }
97
+ },
98
+
99
+ 'net.Server': {
100
+ simultaneousAccepts: true,
101
+
102
+ send: function(message, server) {
103
+ return server._handle;
104
+ },
105
+
106
+ got: function(message, handle, emit) {
107
+ var server = new net.Server();
108
+ server.listen(handle, function() {
109
+ emit(server);
110
+ });
111
+ }
112
+ },
113
+
114
+ 'net.Socket': {
115
+ send: function(message, socket) {
116
+ // if the socket was created by net.Server
117
+ if (socket.server) {
118
+ // the slave should keep track of the socket
119
+ message.key = socket.server._connectionKey;
120
+
121
+ var firstTime = !this._channel.sockets.send[message.key];
122
+ var socketList = getSocketList('send', this, message.key);
123
+
124
+ // the server should no longer expose a .connection property
125
+ // and when asked to close it should query the socket status from
126
+ // the slaves
127
+ if (firstTime) socket.server._setupSlave(socketList);
128
+
129
+ // Act like socket is detached
130
+ socket.server._connections--;
131
+ }
132
+
133
+ // remove handle from socket object, it will be closed when the socket
134
+ // will be sent
135
+ var handle = socket._handle;
136
+ handle.onread = function() {};
137
+ socket._handle = null;
138
+
139
+ return handle;
140
+ },
141
+
142
+ postSend: function(handle) {
143
+ // Close the Socket handle after sending it
144
+ handle.close();
145
+ },
146
+
147
+ got: function(message, handle, emit) {
148
+ var socket = new net.Socket({handle: handle});
149
+ socket.readable = socket.writable = true;
150
+
151
+ // if the socket was created by net.Server we will track the socket
152
+ if (message.key) {
153
+
154
+ // add socket to connections list
155
+ var socketList = getSocketList('got', this, message.key);
156
+ socketList.add({
157
+ socket: socket
158
+ });
159
+ }
160
+
161
+ emit(socket);
162
+ }
163
+ },
164
+
165
+ 'dgram.Native': {
166
+ simultaneousAccepts: false,
167
+
168
+ send: function(message, handle) {
169
+ return handle;
170
+ },
171
+
172
+ got: function(message, handle, emit) {
173
+ emit(handle);
174
+ }
175
+ },
176
+
177
+ 'dgram.Socket': {
178
+ simultaneousAccepts: false,
179
+
180
+ send: function(message, socket) {
181
+ message.dgramType = socket.type;
182
+
183
+ return socket._handle;
184
+ },
185
+
186
+ got: function(message, handle, emit) {
187
+ var socket = new dgram.Socket(message.dgramType);
188
+
189
+ socket.bind(handle, function() {
190
+ emit(socket);
191
+ });
192
+ }
193
+ }
194
+ };
195
+
196
+ // This object keep track of the socket there are sended
197
+ function SocketListSend(slave, key) {
198
+ EventEmitter.call(this);
199
+
200
+ this.key = key;
201
+ this.slave = slave;
202
+ }
203
+ util.inherits(SocketListSend, EventEmitter);
204
+
205
+ SocketListSend.prototype._request = function(msg, cmd, callback) {
206
+ var self = this;
207
+
208
+ if (!this.slave.connected) return onclose();
209
+ this.slave.send(msg);
210
+
211
+ function onclose() {
212
+ self.slave.removeListener('internalMessage', onreply);
213
+ callback(new Error('Slave closed before reply'));
214
+ };
215
+
216
+ function onreply(msg) {
217
+ if (!(msg.cmd === cmd && msg.key === self.key)) return;
218
+ self.slave.removeListener('disconnect', onclose);
219
+ self.slave.removeListener('internalMessage', onreply);
220
+
221
+ callback(null, msg);
222
+ };
223
+
224
+ this.slave.once('disconnect', onclose);
225
+ this.slave.on('internalMessage', onreply);
226
+ };
227
+
228
+ SocketListSend.prototype.close = function close(callback) {
229
+ this._request({
230
+ cmd: 'NODE_SOCKET_NOTIFY_CLOSE',
231
+ key: this.key
232
+ }, 'NODE_SOCKET_ALL_CLOSED', callback);
233
+ };
234
+
235
+ SocketListSend.prototype.getConnections = function getConnections(callback) {
236
+ this._request({
237
+ cmd: 'NODE_SOCKET_GET_COUNT',
238
+ key: this.key
239
+ }, 'NODE_SOCKET_COUNT', function(err, msg) {
240
+ if (err) return callback(err);
241
+ callback(null, msg.count);
242
+ });
243
+ };
244
+
245
+ // This object keep track of the socket there are received
246
+ function SocketListReceive(slave, key) {
247
+ EventEmitter.call(this);
248
+
249
+ var self = this;
250
+
251
+ this.connections = 0;
252
+ this.key = key;
253
+ this.slave = slave;
254
+
255
+ function onempty() {
256
+ if (!self.slave.connected) return;
257
+
258
+ self.slave.send({
259
+ cmd: 'NODE_SOCKET_ALL_CLOSED',
260
+ key: self.key
261
+ });
262
+ }
263
+
264
+ this.slave.on('internalMessage', function(msg) {
265
+ if (msg.key !== self.key) return;
266
+
267
+ if (msg.cmd === 'NODE_SOCKET_NOTIFY_CLOSE') {
268
+ // Already empty
269
+ if (self.connections === 0) return onempty();
270
+
271
+ // Wait for sockets to get closed
272
+ self.once('empty', onempty);
273
+ } else if (msg.cmd === 'NODE_SOCKET_GET_COUNT') {
274
+ if (!self.slave.connected) return;
275
+ self.slave.send({
276
+ cmd: 'NODE_SOCKET_COUNT',
277
+ key: self.key,
278
+ count: self.connections
279
+ });
280
+ }
281
+ });
282
+ }
283
+ util.inherits(SocketListReceive, EventEmitter);
284
+
285
+ SocketListReceive.prototype.add = function(obj) {
286
+ var self = this;
287
+
288
+ this.connections++;
289
+
290
+ // Notify previous owner of socket about its state change
291
+ obj.socket.once('close', function() {
292
+ self.connections--;
293
+
294
+ if (self.connections === 0) self.emit('empty');
295
+ });
296
+ };
297
+
298
+ function getSocketList(type, slave, key) {
299
+ var sockets = slave._channel.sockets[type];
300
+ var socketList = sockets[key];
301
+ if (!socketList) {
302
+ var Construct = type === 'send' ? SocketListSend : SocketListReceive;
303
+ socketList = sockets[key] = new Construct(slave, key);
304
+ }
305
+ return socketList;
306
+ }
307
+
308
+ var INTERNAL_PREFIX = 'NODE_';
309
+ function handleMessage(target, message, handle) {
310
+ var eventName = 'message';
311
+ if (!util.isNull(message) &&
312
+ util.isObject(message) &&
313
+ util.isString(message.cmd) &&
314
+ message.cmd.length > INTERNAL_PREFIX.length &&
315
+ message.cmd.slice(0, INTERNAL_PREFIX.length) === INTERNAL_PREFIX) {
316
+ eventName = 'internalMessage';
317
+ }
318
+ target.emit(eventName, message, handle);
319
+ }
320
+
321
+ function setupChannel(target, channel) {
322
+ target._channel = channel;
323
+ target._handleQueue = null;
324
+
325
+ var decoder = new StringDecoder('utf8');
326
+ var jsonBuffer = '';
327
+ channel.buffering = false;
328
+ channel.onread = function(nread, pool, recvHandle) {
329
+ // TODO(bnoordhuis) Check that nread > 0.
330
+ if (pool) {
331
+ jsonBuffer += decoder.write(pool);
332
+
333
+ var i, start = 0;
334
+
335
+ //Linebreak is used as a message end sign
336
+ while ((i = jsonBuffer.indexOf('\n', start)) >= 0) {
337
+ var json = jsonBuffer.slice(start, i);
338
+ var message = JSON.parse(json);
339
+
340
+ // There will be at most one NODE_HANDLE message in every chunk we
341
+ // read because SCM_RIGHTS messages don't get coalesced. Make sure
342
+ // that we deliver the handle with the right message however.
343
+ if (message && message.cmd === 'NODE_HANDLE')
344
+ handleMessage(target, message, recvHandle);
345
+ else
346
+ handleMessage(target, message, undefined);
347
+
348
+ start = i + 1;
349
+ }
350
+ jsonBuffer = jsonBuffer.slice(start);
351
+ this.buffering = jsonBuffer.length !== 0;
352
+
353
+ } else {
354
+ this.buffering = false;
355
+ target.disconnect();
356
+ channel.onread = nop;
357
+ channel.close();
358
+ maybeClose(target);
359
+ }
360
+ };
361
+
362
+ // object where socket lists will live
363
+ channel.sockets = { got: {}, send: {} };
364
+
365
+ // handlers will go through this
366
+ target.on('internalMessage', function(message, handle) {
367
+ // Once acknowledged - continue sending handles.
368
+ if (message.cmd === 'NODE_HANDLE_ACK') {
369
+ assert(util.isArray(target._handleQueue));
370
+ var queue = target._handleQueue;
371
+ target._handleQueue = null;
372
+
373
+ queue.forEach(function(args) {
374
+ target._send(args.message, args.handle, false);
375
+ });
376
+
377
+ // Process a pending disconnect (if any).
378
+ if (!target.connected && target._channel && !target._handleQueue)
379
+ target._disconnect();
380
+
381
+ return;
382
+ }
383
+
384
+ if (message.cmd !== 'NODE_HANDLE') return;
385
+
386
+ // Acknowledge handle receival. Don't emit error events (for example if
387
+ // the other side has disconnected) because this call to send() is not
388
+ // initiated by the user and it shouldn't be fatal to be unable to ACK
389
+ // a message.
390
+ target._send({ cmd: 'NODE_HANDLE_ACK' }, null, true);
391
+
392
+ var obj = handleConversion[message.type];
393
+
394
+ // Update simultaneous accepts on Windows
395
+ if (process.platform === 'win32') {
396
+ handle._simultaneousAccepts = false;
397
+ net._setSimultaneousAccepts(handle);
398
+ }
399
+
400
+ // Convert handle object
401
+ obj.got.call(this, message, handle, function(handle) {
402
+ handleMessage(target, message.msg, handle);
403
+ });
404
+ });
405
+
406
+ target.send = function(message, handle) {
407
+ if (!this.connected)
408
+ this.emit('error', new Error('channel closed'));
409
+ else
410
+ this._send(message, handle, false);
411
+ };
412
+
413
+ target._send = function(message, handle, swallowErrors) {
414
+ assert(this.connected || this._channel);
415
+
416
+ if (util.isUndefined(message))
417
+ throw new TypeError('message cannot be undefined');
418
+
419
+ // package messages with a handle object
420
+ if (handle) {
421
+ // this message will be handled by an internalMessage event handler
422
+ message = {
423
+ cmd: 'NODE_HANDLE',
424
+ type: null,
425
+ msg: message
426
+ };
427
+
428
+ if (handle instanceof net.Socket) {
429
+ message.type = 'net.Socket';
430
+ } else if (handle instanceof net.Server) {
431
+ message.type = 'net.Server';
432
+ } else if (handle instanceof process.binding('tcp_wrap').TCP ||
433
+ handle instanceof process.binding('pipe_wrap').Pipe) {
434
+ message.type = 'net.Native';
435
+ } else if (handle instanceof dgram.Socket) {
436
+ message.type = 'dgram.Socket';
437
+ } else if (handle instanceof process.binding('udp_wrap').UDP) {
438
+ message.type = 'dgram.Native';
439
+ } else {
440
+ throw new TypeError("This handle type can't be sent");
441
+ }
442
+
443
+ // Queue-up message and handle if we haven't received ACK yet.
444
+ if (this._handleQueue) {
445
+ this._handleQueue.push({ message: message.msg, handle: handle });
446
+ return;
447
+ }
448
+
449
+ var obj = handleConversion[message.type];
450
+
451
+ // convert TCP object to native handle object
452
+ handle = handleConversion[message.type].send.apply(target, arguments);
453
+
454
+ // Update simultaneous accepts on Windows
455
+ if (obj.simultaneousAccepts) {
456
+ net._setSimultaneousAccepts(handle);
457
+ }
458
+ } else if (this._handleQueue) {
459
+ // Queue request anyway to avoid out-of-order messages.
460
+ this._handleQueue.push({ message: message, handle: null });
461
+ return;
462
+ }
463
+
464
+ var req = { oncomplete: nop };
465
+ var string = JSON.stringify(message) + '\n';
466
+ var err = channel.writeUtf8String(req, string, handle);
467
+
468
+ if (err) {
469
+ if (!swallowErrors)
470
+ this.emit('error', errnoException(err, 'write'));
471
+ } else if (handle && !this._handleQueue) {
472
+ this._handleQueue = [];
473
+ }
474
+
475
+ if (obj && obj.postSend) {
476
+ req.oncomplete = obj.postSend.bind(null, handle);
477
+ }
478
+
479
+ /* If the master is > 2 read() calls behind, please stop sending. */
480
+ return channel.writeQueueSize < (65536 * 2);
481
+ };
482
+
483
+ // connected will be set to false immediately when a disconnect() is
484
+ // requested, even though the channel might still be alive internally to
485
+ // process queued messages. The three states are distinguished as follows:
486
+ // - disconnect() never requested: _channel is not null and connected
487
+ // is true
488
+ // - disconnect() requested, messages in the queue: _channel is not null
489
+ // and connected is false
490
+ // - disconnect() requested, channel actually disconnected: _channel is
491
+ // null and connected is false
492
+ target.connected = true;
493
+
494
+ target.disconnect = function() {
495
+ if (!this.connected) {
496
+ this.emit('error', new Error('IPC channel is already disconnected'));
497
+ return;
498
+ }
499
+
500
+ // Do not allow any new messages to be written.
501
+ this.connected = false;
502
+
503
+ // If there are no queued messages, disconnect immediately. Otherwise,
504
+ // postpone the disconnect so that it happens internally after the
505
+ // queue is flushed.
506
+ if (!this._handleQueue)
507
+ this._disconnect();
508
+ }
509
+
510
+ target._disconnect = function() {
511
+ assert(this._channel);
512
+
513
+ // This marks the fact that the channel is actually disconnected.
514
+ this._channel = null;
515
+
516
+ var fired = false;
517
+ function finish() {
518
+ if (fired) return;
519
+ fired = true;
520
+
521
+ channel.close();
522
+ target.emit('disconnect');
523
+ }
524
+
525
+ // If a message is being read, then wait for it to complete.
526
+ if (channel.buffering) {
527
+ this.once('message', finish);
528
+ this.once('internalMessage', finish);
529
+
530
+ return;
531
+ }
532
+
533
+ process.nextTick(finish);
534
+ };
535
+
536
+ channel.readStart();
537
+ }
538
+
539
+
540
+ function nop() { }
541
+
542
+ exports.fork = function(modulePath /*, args, options*/) {
543
+
544
+ // Get options and args arguments.
545
+ var options, args, execArgv;
546
+ if (util.isArray(arguments[1])) {
547
+ args = arguments[1];
548
+ options = util._extend({}, arguments[2]);
549
+ } else {
550
+ args = [];
551
+ options = util._extend({}, arguments[1]);
552
+ }
553
+
554
+ // Prepare arguments for fork:
555
+ execArgv = options.execArgv || process.execArgv;
556
+ args = execArgv.concat([modulePath], args);
557
+
558
+ // Leave stdin open for the IPC channel. stdout and stderr should be the
559
+ // same as the parent's if silent isn't set.
560
+ options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
561
+ [0, 1, 2, 'ipc'];
562
+
563
+ options.execPath = options.execPath || process.execPath;
564
+
565
+ return spawn(options.execPath, args, options);
566
+ };
567
+
568
+
569
+ exports._forkChild = function(fd) {
570
+ // set process.send()
571
+ var p = createPipe(true);
572
+ p.open(fd);
573
+ p.unref();
574
+ setupChannel(process, p);
575
+
576
+ var refs = 0;
577
+ process.on('newListener', function(name) {
578
+ if (name !== 'message' && name !== 'disconnect') return;
579
+ if (++refs === 1) p.ref();
580
+ });
581
+ process.on('removeListener', function(name) {
582
+ if (name !== 'message' && name !== 'disconnect') return;
583
+ if (--refs === 0) p.unref();
584
+ });
585
+ };
586
+
587
+
588
+ exports.exec = function(command /*, options, callback */) {
589
+ var file, args, options, callback;
590
+
591
+ if (util.isFunction(arguments[1])) {
592
+ options = undefined;
593
+ callback = arguments[1];
594
+ } else {
595
+ options = arguments[1];
596
+ callback = arguments[2];
597
+ }
598
+
599
+ if (process.platform === 'win32') {
600
+ file = 'cmd.exe';
601
+ args = ['/s', '/c', '"' + command + '"'];
602
+ // Make a shallow copy before patching so we don't clobber the user's
603
+ // options object.
604
+ options = util._extend({}, options);
605
+ options.windowsVerbatimArguments = true;
606
+ } else {
607
+ file = '/bin/sh';
608
+ args = ['-c', command];
609
+ }
610
+
611
+ if (options && options.shell)
612
+ file = options.shell;
613
+
614
+ return exports.execFile(file, args, options, callback);
615
+ };
616
+
617
+
618
+ exports.execFile = function(file /* args, options, callback */) {
619
+ var args, callback;
620
+ var options = {
621
+ encoding: 'utf8',
622
+ timeout: 0,
623
+ maxBuffer: 200 * 1024,
624
+ killSignal: 'SIGTERM',
625
+ cwd: null,
626
+ env: null
627
+ };
628
+
629
+ // Parse the parameters.
630
+
631
+ if (util.isFunction(arguments[arguments.length - 1])) {
632
+ callback = arguments[arguments.length - 1];
633
+ }
634
+
635
+ if (util.isArray(arguments[1])) {
636
+ args = arguments[1];
637
+ options = util._extend(options, arguments[2]);
638
+ } else {
639
+ args = [];
640
+ options = util._extend(options, arguments[1]);
641
+ }
642
+
643
+ var child = spawn(file, args, {
644
+ cwd: options.cwd,
645
+ env: options.env,
646
+ windowsVerbatimArguments: !!options.windowsVerbatimArguments
647
+ });
648
+
649
+ var encoding;
650
+ var _stdout;
651
+ var _stderr;
652
+ if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) {
653
+ encoding = options.encoding;
654
+ _stdout = '';
655
+ _stderr = '';
656
+ } else {
657
+ _stdout = [];
658
+ _stderr = [];
659
+ encoding = null;
660
+ }
661
+ var stdoutLen = 0;
662
+ var stderrLen = 0;
663
+ var killed = false;
664
+ var exited = false;
665
+ var timeoutId;
666
+
667
+ var ex = null;
668
+
669
+ function exithandler(code, signal) {
670
+ if (exited) return;
671
+ exited = true;
672
+
673
+ if (timeoutId) {
674
+ clearTimeout(timeoutId);
675
+ timeoutId = null;
676
+ }
677
+
678
+ if (!callback) return;
679
+
680
+ // merge chunks
681
+ var stdout;
682
+ var stderr;
683
+ if (!encoding) {
684
+ stdout = Buffer.concat(_stdout);
685
+ stderr = Buffer.concat(_stderr);
686
+ } else {
687
+ stdout = _stdout;
688
+ stderr = _stderr;
689
+ }
690
+
691
+ if (ex) {
692
+ // Will be handled later
693
+ } else if (code === 0 && signal === null) {
694
+ callback(null, stdout, stderr);
695
+ return;
696
+ }
697
+
698
+ var cmd = file;
699
+ if (args.length !== 0)
700
+ cmd += ' ' + args.join(' ');
701
+
702
+ if (!ex) {
703
+ ex = new Error('Command failed: ' + cmd + '\n' + stderr);
704
+ ex.killed = child.killed || killed;
705
+ ex.code = code < 0 ? uv.errname(code) : code;
706
+ ex.signal = signal;
707
+ }
708
+
709
+ ex.cmd = cmd;
710
+ callback(ex, stdout, stderr);
711
+ }
712
+
713
+ function errorhandler(e) {
714
+ ex = e;
715
+ child.stdout.destroy();
716
+ child.stderr.destroy();
717
+ exithandler();
718
+ }
719
+
720
+ function kill() {
721
+ child.stdout.destroy();
722
+ child.stderr.destroy();
723
+
724
+ killed = true;
725
+ try {
726
+ child.kill(options.killSignal);
727
+ } catch (e) {
728
+ ex = e;
729
+ exithandler();
730
+ }
731
+ }
732
+
733
+ if (options.timeout > 0) {
734
+ timeoutId = setTimeout(function() {
735
+ kill();
736
+ timeoutId = null;
737
+ }, options.timeout);
738
+ }
739
+
740
+ child.stdout.addListener('data', function(chunk) {
741
+ stdoutLen += chunk.length;
742
+
743
+ if (stdoutLen > options.maxBuffer) {
744
+ ex = new Error('stdout maxBuffer exceeded.');
745
+ kill();
746
+ } else {
747
+ if (!encoding)
748
+ _stdout.push(chunk);
749
+ else
750
+ _stdout += chunk;
751
+ }
752
+ });
753
+
754
+ child.stderr.addListener('data', function(chunk) {
755
+ stderrLen += chunk.length;
756
+
757
+ if (stderrLen > options.maxBuffer) {
758
+ ex = new Error('stderr maxBuffer exceeded.');
759
+ kill();
760
+ } else {
761
+ if (!encoding)
762
+ _stderr.push(chunk);
763
+ else
764
+ _stderr += chunk;
765
+ }
766
+ });
767
+
768
+ if (encoding) {
769
+ child.stderr.setEncoding(encoding);
770
+ child.stdout.setEncoding(encoding);
771
+ }
772
+
773
+ child.addListener('close', exithandler);
774
+ child.addListener('error', errorhandler);
775
+
776
+ return child;
777
+ };
778
+
779
+
780
+ var spawn = exports.spawn = function(file, args, options) {
781
+ args = args ? args.slice(0) : [];
782
+ args.unshift(file);
783
+
784
+ var env = (options ? options.env : null) || process.env;
785
+ var envPairs = [];
786
+ for (var key in env) {
787
+ envPairs.push(key + '=' + env[key]);
788
+ }
789
+
790
+ var child = new ChildProcess();
791
+ if (options && options.customFds && !options.stdio) {
792
+ options.stdio = options.customFds.map(function(fd) {
793
+ return fd === -1 ? 'pipe' : fd;
794
+ });
795
+ }
796
+
797
+ child.spawn({
798
+ file: file,
799
+ args: args,
800
+ cwd: options ? options.cwd : null,
801
+ windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
802
+ detached: !!(options && options.detached),
803
+ envPairs: envPairs,
804
+ stdio: options ? options.stdio : null,
805
+ uid: options ? options.uid : null,
806
+ gid: options ? options.gid : null
807
+ });
808
+
809
+ return child;
810
+ };
811
+
812
+
813
+ function maybeClose(subprocess) {
814
+ subprocess._closesGot++;
815
+
816
+ if (subprocess._closesGot == subprocess._closesNeeded) {
817
+ subprocess.emit('close', subprocess.exitCode, subprocess.signalCode);
818
+ }
819
+ }
820
+
821
+
822
+ function ChildProcess() {
823
+ EventEmitter.call(this);
824
+
825
+ // Initialize TCPWrap and PipeWrap
826
+ process.binding('tcp_wrap');
827
+ process.binding('pipe_wrap');
828
+
829
+ var self = this;
830
+
831
+ this._closesNeeded = 1;
832
+ this._closesGot = 0;
833
+ this.connected = false;
834
+
835
+ this.signalCode = null;
836
+ this.exitCode = null;
837
+ this.killed = false;
838
+
839
+ this._handle = new Process();
840
+ this._handle.owner = this;
841
+
842
+ this._handle.onexit = function(exitCode, signalCode) {
843
+ //
844
+ // follow 0.4.x behaviour:
845
+ //
846
+ // - normally terminated processes don't touch this.signalCode
847
+ // - signaled processes don't touch this.exitCode
848
+ //
849
+ // new in 0.9.x:
850
+ //
851
+ // - spawn failures are reported with exitCode < 0
852
+ //
853
+ var err = (exitCode < 0) ? errnoException(exitCode, 'spawn') : null;
854
+
855
+ if (signalCode) {
856
+ self.signalCode = signalCode;
857
+ } else {
858
+ self.exitCode = exitCode;
859
+ }
860
+
861
+ if (self.stdin) {
862
+ self.stdin.destroy();
863
+ }
864
+
865
+ self._handle.close();
866
+ self._handle = null;
867
+
868
+ if (exitCode < 0) {
869
+ self.emit('error', err);
870
+ } else {
871
+ self.emit('exit', self.exitCode, self.signalCode);
872
+ }
873
+
874
+ // if any of the stdio streams have not been touched,
875
+ // then pull all the data through so that it can get the
876
+ // eof and emit a 'close' event.
877
+ // Do it on nextTick so that the user has one last chance
878
+ // to consume the output, if for example they only want to
879
+ // start reading the data once the process exits.
880
+ process.nextTick(function() {
881
+ flushStdio(self);
882
+ });
883
+
884
+ maybeClose(self);
885
+ };
886
+ }
887
+ util.inherits(ChildProcess, EventEmitter);
888
+
889
+
890
+ function flushStdio(subprocess) {
891
+ subprocess.stdio.forEach(function(stream, fd, stdio) {
892
+ if (!stream || !stream.readable || stream._consuming ||
893
+ stream._readableState.flowing)
894
+ return;
895
+ stream.resume();
896
+ });
897
+ }
898
+
899
+
900
+
901
+ function getHandleWrapType(stream) {
902
+ if (stream instanceof handleWraps.Pipe) return 'pipe';
903
+ if (stream instanceof handleWraps.TTY) return 'tty';
904
+ if (stream instanceof handleWraps.TCP) return 'tcp';
905
+ if (stream instanceof handleWraps.UDP) return 'udp';
906
+
907
+ return false;
908
+ }
909
+
910
+
911
+ ChildProcess.prototype.spawn = function(options) {
912
+ var self = this,
913
+ ipc,
914
+ ipcFd,
915
+ // If no `stdio` option was given - use default
916
+ stdio = options.stdio || 'pipe';
917
+
918
+ // Replace shortcut with an array
919
+ if (util.isString(stdio)) {
920
+ switch (stdio) {
921
+ case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
922
+ case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
923
+ case 'inherit': stdio = [0, 1, 2]; break;
924
+ default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
925
+ }
926
+ } else if (!util.isArray(stdio)) {
927
+ throw new TypeError('Incorrect value of stdio option: ' + stdio);
928
+ }
929
+
930
+ // At least 3 stdio will be created
931
+ // Don't concat() a new Array() because it would be sparse, and
932
+ // stdio.reduce() would skip the sparse elements of stdio.
933
+ // See http://stackoverflow.com/a/5501711/3561
934
+ while (stdio.length < 3) stdio.push(undefined);
935
+
936
+ // Translate stdio into C++-readable form
937
+ // (i.e. PipeWraps or fds)
938
+ stdio = stdio.reduce(function(acc, stdio, i) {
939
+ function cleanup() {
940
+ acc.filter(function(stdio) {
941
+ return stdio.type === 'pipe' || stdio.type === 'ipc';
942
+ }).forEach(function(stdio) {
943
+ stdio.handle.close();
944
+ });
945
+ }
946
+
947
+ // Defaults
948
+ if (util.isNullOrUndefined(stdio)) {
949
+ stdio = i < 3 ? 'pipe' : 'ignore';
950
+ }
951
+
952
+ if (stdio === 'ignore') {
953
+ acc.push({type: 'ignore'});
954
+ } else if (stdio === 'pipe' || util.isNumber(stdio) && stdio < 0) {
955
+ acc.push({type: 'pipe', handle: createPipe()});
956
+ } else if (stdio === 'ipc') {
957
+ if (!util.isUndefined(ipc)) {
958
+ // Cleanup previously created pipes
959
+ cleanup();
960
+ throw Error('Child process can have only one IPC pipe');
961
+ }
962
+
963
+ ipc = createPipe(true);
964
+ ipcFd = i;
965
+
966
+ acc.push({ type: 'pipe', handle: ipc, ipc: true });
967
+ } else if (util.isNumber(stdio) || util.isNumber(stdio.fd)) {
968
+ acc.push({ type: 'fd', fd: stdio.fd || stdio });
969
+ } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
970
+ getHandleWrapType(stdio._handle)) {
971
+ var handle = getHandleWrapType(stdio) ?
972
+ stdio :
973
+ getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
974
+
975
+ acc.push({
976
+ type: 'wrap',
977
+ wrapType: getHandleWrapType(handle),
978
+ handle: handle
979
+ });
980
+ } else {
981
+ // Cleanup
982
+ cleanup();
983
+ throw new TypeError('Incorrect value for stdio stream: ' + stdio);
984
+ }
985
+
986
+ return acc;
987
+ }, []);
988
+
989
+ options.stdio = stdio;
990
+
991
+ if (!util.isUndefined(ipc)) {
992
+ // Let child process know about opened IPC channel
993
+ options.envPairs = options.envPairs || [];
994
+ options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
995
+ }
996
+
997
+ var err = this._handle.spawn(options);
998
+
999
+ if (err == uv.UV_ENOENT) {
1000
+ process.nextTick(function() {
1001
+ self._handle.onexit(err);
1002
+ });
1003
+ } else if (err) {
1004
+ // Close all opened fds on error
1005
+ stdio.forEach(function(stdio) {
1006
+ if (stdio.type === 'pipe') {
1007
+ stdio.handle.close();
1008
+ }
1009
+ });
1010
+
1011
+ this._handle.close();
1012
+ this._handle = null;
1013
+ throw errnoException(err, 'spawn');
1014
+ }
1015
+
1016
+ this.pid = this._handle.pid;
1017
+
1018
+ stdio.forEach(function(stdio, i) {
1019
+ if (stdio.type === 'ignore') return;
1020
+
1021
+ if (stdio.ipc) {
1022
+ self._closesNeeded++;
1023
+ return;
1024
+ }
1025
+
1026
+ if (stdio.handle) {
1027
+ // when i === 0 - we're dealing with stdin
1028
+ // (which is the only one writable pipe)
1029
+ stdio.socket = createSocket(self.pid !== 0 ? stdio.handle : null, i > 0);
1030
+
1031
+ if (i > 0 && self.pid !== 0) {
1032
+ self._closesNeeded++;
1033
+ stdio.socket.on('close', function() {
1034
+ maybeClose(self);
1035
+ });
1036
+ }
1037
+ }
1038
+ });
1039
+
1040
+ this.stdin = stdio.length >= 1 && !util.isUndefined(stdio[0].socket) ?
1041
+ stdio[0].socket : null;
1042
+ this.stdout = stdio.length >= 2 && !util.isUndefined(stdio[1].socket) ?
1043
+ stdio[1].socket : null;
1044
+ this.stderr = stdio.length >= 3 && !util.isUndefined(stdio[2].socket) ?
1045
+ stdio[2].socket : null;
1046
+
1047
+ this.stdio = stdio.map(function(stdio) {
1048
+ return util.isUndefined(stdio.socket) ? null : stdio.socket;
1049
+ });
1050
+
1051
+ // Add .send() method and start listening for IPC data
1052
+ if (!util.isUndefined(ipc)) setupChannel(this, ipc);
1053
+
1054
+ return err;
1055
+ };
1056
+
1057
+
1058
+ ChildProcess.prototype.kill = function(sig) {
1059
+ var signal;
1060
+
1061
+ if (!constants) {
1062
+ constants = process.binding('constants');
1063
+ }
1064
+
1065
+ if (sig === 0) {
1066
+ signal = 0;
1067
+ } else if (!sig) {
1068
+ signal = constants['SIGTERM'];
1069
+ } else {
1070
+ signal = constants[sig];
1071
+ }
1072
+
1073
+ if (util.isUndefined(signal)) {
1074
+ throw new Error('Unknown signal: ' + sig);
1075
+ }
1076
+
1077
+ if (this._handle) {
1078
+ var err = this._handle.kill(signal);
1079
+ if (err === 0) {
1080
+ /* Success. */
1081
+ this.killed = true;
1082
+ return true;
1083
+ }
1084
+ if (err === uv.UV_ESRCH) {
1085
+ /* Already dead. */
1086
+ } else if (err === uv.UV_EINVAL || err === uv.UV_ENOSYS) {
1087
+ /* The underlying platform doesn't support this signal. */
1088
+ throw errnoException(err, 'kill');
1089
+ } else {
1090
+ /* Other error, almost certainly EPERM. */
1091
+ this.emit('error', errnoException(err, 'kill'));
1092
+ }
1093
+ }
1094
+
1095
+ /* Kill didn't succeed. */
1096
+ return false;
1097
+ };
1098
+
1099
+
1100
+ ChildProcess.prototype.ref = function() {
1101
+ if (this._handle) this._handle.ref();
1102
+ };
1103
+
1104
+
1105
+ ChildProcess.prototype.unref = function() {
1106
+ if (this._handle) this._handle.unref();
1107
+ };