macgyver 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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
+ };