--- /dev/null
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var EventEmitter = require('events').EventEmitter;
+var net = require('net');
+var Process = process.binding('process_wrap').Process;
+var inherits = require('util').inherits;
+var constants; // if (!constants) constants = process.binding('constants');
+
+var LF = '\n'.charCodeAt(0);
+var Pipe;
+
+
+// constructors for lazy loading
+function createPipe(ipc) {
+ // Lazy load
+ if (!Pipe) {
+ Pipe = new process.binding('pipe_wrap').Pipe;
+ }
+
+ return new Pipe(ipc);
+}
+
+function createSocket(pipe, readable) {
+ var s = new net.Socket({ handle: pipe });
+
+ if (readable) {
+ s.writable = false;
+ s.readable = true;
+ s.resume();
+ } else {
+ s.writable = true;
+ s.readable = false;
+ }
+
+ return s;
+}
+
+function mergeOptions(target, overrides) {
+ if (overrides) {
+ var keys = Object.keys(overrides);
+ for (var i = 0, len = keys.length; i < len; i++) {
+ var k = keys[i];
+ if (overrides[k] !== undefined) {
+ target[k] = overrides[k];
+ }
+ }
+ }
+ return target;
+}
+
+
+function setupChannel(target, channel) {
+ target._channel = channel;
+
+ var jsonBuffer = '';
+
+ channel.onread = function(pool, offset, length, recvHandle) {
+ if (pool) {
+ jsonBuffer += pool.toString('ascii', offset, offset + length);
+
+ var i;
+ while ((i = jsonBuffer.indexOf('\n')) >= 0) {
+ var json = jsonBuffer.slice(0, i);
+ var message = JSON.parse(json);
+ jsonBuffer = jsonBuffer.slice(i + 1);
+
+ target.emit('message', message, recvHandle);
+ }
+
+ } else {
+ channel.close();
+ target._channel = null;
+ }
+ };
+
+ target.send = function(message, sendHandle) {
+ if (!target._channel) throw new Error("channel closed");
+
+ // For overflow protection don't write if channel queue is too deep.
+ if (channel.writeQueueSize > 1024 * 1024) {
+ return false;
+ }
+
+ var buffer = Buffer(JSON.stringify(message) + '\n');
+
+ var writeReq = channel.write(buffer, 0, buffer.length, sendHandle);
+
+ if (!writeReq) {
+ throw new Error(errno + " cannot write to IPC channel.");
+ }
+
+ writeReq.oncomplete = nop;
+
+ return true;
+ };
+
+ channel.readStart();
+}
+
+
+function nop() { }
+
+
+exports.fork = function(modulePath, args, options) {
+ if (!options) options = {};
+
+ if (!args) args = [];
+ args.unshift(modulePath);
+
+ if (options.stdinStream) {
+ throw new Error("stdinStream not allowed for fork()");
+ }
+
+ if (options.customFds) {
+ throw new Error("customFds not allowed for fork()");
+ }
+
+ // Leave stdin open for the IPC channel. stdout and stderr should be the
+ // same as the parent's.
+ options.customFds = [ -1, 1, 2 ];
+
+ // Just need to set this - child process won't actually use the fd.
+ // For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6.
+ if (!options.env) options.env = { };
+ options.env.NODE_CHANNEL_FD = 42;
+
+ // stdin is the IPC channel.
+ options.stdinStream = createPipe(true);
+
+ var child = spawn(process.execPath, args, options);
+
+ setupChannel(child, options.stdinStream);
+
+ child.on('exit', function() {
+ if (child._channel) {
+ child._channel.close();
+ }
+ });
+
+ return child;
+};
+
+
+exports._forkChild = function() {
+ // set process.send()
+ var p = createPipe(true);
+ p.open(0);
+ setupChannel(process, p);
+};
+
+
+exports.exec = function(command /*, options, callback */) {
+ var file, args, options, callback;
+
+ if (typeof arguments[1] === 'function') {
+ options = undefined;
+ callback = arguments[1];
+ } else {
+ options = arguments[1];
+ callback = arguments[2];
+ }
+
+ if (process.platform === 'win32') {
+ file = 'cmd.exe';
+ args = ['/s', '/c', '"' + command + '"'];
+ // Make a shallow copy before patching so we don't clobber the user's
+ // options object.
+ options = mergeOptions({}, options);
+ options.windowsVerbatimArguments = true;
+ } else {
+ file = '/bin/sh';
+ args = ['-c', command];
+ }
+ return exports.execFile(file, args, options, callback);
+};
+
+
+exports.execFile = function(file /* args, options, callback */) {
+ var args, optionArg, callback;
+ var options = {
+ encoding: 'utf8',
+ timeout: 0,
+ maxBuffer: 200 * 1024,
+ killSignal: 'SIGTERM',
+ setsid: false,
+ cwd: null,
+ env: null
+ };
+
+ // Parse the parameters.
+
+ if (typeof arguments[arguments.length - 1] === 'function') {
+ callback = arguments[arguments.length - 1];
+ }
+
+ if (Array.isArray(arguments[1])) {
+ args = arguments[1];
+ if (typeof arguments[2] === 'object') optionArg = arguments[2];
+ } else {
+ args = [];
+ if (typeof arguments[1] === 'object') optionArg = arguments[1];
+ }
+
+ // Merge optionArg into options
+ mergeOptions(options, optionArg);
+
+ var child = spawn(file, args, {
+ cwd: options.cwd,
+ env: options.env,
+ windowsVerbatimArguments: !!options.windowsVerbatimArguments
+ });
+
+ var stdout = '';
+ var stderr = '';
+ var killed = false;
+ var exited = false;
+ var timeoutId;
+
+ var err;
+
+ function exithandler(code, signal) {
+ if (exited) return;
+ exited = true;
+
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ }
+
+ if (!callback) return;
+
+ if (err) {
+ callback(err, stdout, stderr);
+ } else if (code === 0 && signal === null) {
+ callback(null, stdout, stderr);
+ } else {
+ var e = new Error('Command failed: ' + stderr);
+ e.killed = child.killed || killed;
+ e.code = code;
+ e.signal = signal;
+ callback(e, stdout, stderr);
+ }
+ }
+
+ function kill() {
+ killed = true;
+ child.kill(options.killSignal);
+ process.nextTick(function() {
+ exithandler(null, options.killSignal);
+ });
+ }
+
+ if (options.timeout > 0) {
+ timeoutId = setTimeout(function() {
+ kill();
+ timeoutId = null;
+ }, options.timeout);
+ }
+
+ child.stdout.setEncoding(options.encoding);
+ child.stderr.setEncoding(options.encoding);
+
+ child.stdout.addListener('data', function(chunk) {
+ stdout += chunk;
+ if (stdout.length > options.maxBuffer) {
+ err = new Error('maxBuffer exceeded.');
+ kill();
+ }
+ });
+
+ child.stderr.addListener('data', function(chunk) {
+ stderr += chunk;
+ if (stderr.length > options.maxBuffer) {
+ err = new Error('maxBuffer exceeded.');
+ kill();
+ }
+ });
+
+ child.addListener('exit', exithandler);
+
+ return child;
+};
+
+
+var spawn = exports.spawn = function(file, args, options) {
+ var args = args ? args.slice(0) : [];
+ args.unshift(file);
+
+ var env = (options ? options.env : null) || process.env;
+ var envPairs = [];
+ var keys = Object.keys(env);
+ for (var key in env) {
+ envPairs.push(key + '=' + env[key]);
+ }
+
+ var child = new ChildProcess();
+
+ child.spawn({
+ file: file,
+ args: args,
+ cwd: options ? options.cwd : null,
+ windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
+ envPairs: envPairs,
+ customFds: options ? options.customFds : null,
+ stdinStream: options ? options.stdinStream : null
+ });
+
+ return child;
+};
+
+
+function maybeExit(subprocess) {
+ subprocess._closesGot++;
+
+ if (subprocess._closesGot == subprocess._closesNeeded) {
+ subprocess.emit('exit', subprocess.exitCode, subprocess.signalCode);
+ }
+}
+
+
+function ChildProcess() {
+ var self = this;
+
+ this._closesNeeded = 1;
+ this._closesGot = 0;
+
+ this.signalCode = null;
+ this.exitCode = null;
+
+ this._internal = new Process();
+ this._internal.onexit = function(exitCode, signalCode) {
+ //
+ // follow 0.4.x behaviour:
+ //
+ // - normally terminated processes don't touch this.signalCode
+ // - signaled processes don't touch this.exitCode
+ //
+ if (signalCode) {
+ self.signalCode = signalCode;
+ } else {
+ self.exitCode = exitCode;
+ }
+
+ if (self.stdin) {
+ self.stdin.destroy();
+ }
+
+ self._internal.close();
+ self._internal = null;
+
+ maybeExit(self);
+ };
+}
+inherits(ChildProcess, EventEmitter);
+
+
+function setStreamOption(name, index, options) {
+ // Skip if we already have options.stdinStream
+ if (options[name]) return;
+
+ if (options.customFds &&
+ typeof options.customFds[index] == 'number' &&
+ options.customFds[index] !== -1) {
+ if (options.customFds[index] === index) {
+ options[name] = null;
+ } else {
+ throw new Error('customFds not yet supported');
+ }
+ } else {
+ options[name] = createPipe();
+ }
+}
+
+
+ChildProcess.prototype.spawn = function(options) {
+ var self = this;
+
+ setStreamOption('stdinStream', 0, options);
+ setStreamOption('stdoutStream', 1, options);
+ setStreamOption('stderrStream', 2, options);
+
+ var r = this._internal.spawn(options);
+
+ if (r) {
+ if (options.stdinStream) {
+ options.stdinStream.close();
+ }
+
+ if (options.stdoutStream) {
+ options.stdoutStream.close();
+ }
+
+ if (options.stderrStream) {
+ options.stderrStream.close();
+ }
+
+ this._internal.close();
+ this._internal = null;
+ throw errnoException('spawn', errno);
+ }
+
+ this.pid = this._internal.pid;
+
+ if (options.stdinStream) {
+ this.stdin = createSocket(options.stdinStream, false);
+ }
+
+ if (options.stdoutStream) {
+ this.stdout = createSocket(options.stdoutStream, true);
+ this._closesNeeded++;
+ this.stdout.on('close', function() {
+ maybeExit(self);
+ });
+ }
+
+ if (options.stderrStream) {
+ this.stderr = createSocket(options.stderrStream, true);
+ this._closesNeeded++;
+ this.stderr.on('close', function() {
+ maybeExit(self);
+ });
+ }
+
+ return r;
+};
+
+
+function errnoException(errorno, syscall) {
+ // TODO make this more compatible with ErrnoException from src/node.cc
+ // Once all of Node is using this function the ErrnoException from
+ // src/node.cc should be removed.
+ var e = new Error(syscall + ' ' + errorno);
+ e.errno = e.code = errorno;
+ e.syscall = syscall;
+ return e;
+}
+
+
+ChildProcess.prototype.kill = function(sig) {
+ if (!constants) {
+ constants = process.binding('constants');
+ }
+
+ sig = sig || 'SIGTERM';
+ var signal = constants[sig];
+
+ if (!signal) {
+ throw new Error('Unknown signal: ' + sig);
+ }
+
+ if (this._internal) {
+ var r = this._internal.kill(signal);
+ // TODO: raise error if r == -1?
+ }
+};
+++ /dev/null
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-var util = require('util');
-var EventEmitter = require('events').EventEmitter;
-var Stream = require('net_legacy').Stream; // FIXME
-var InternalChildProcess = process.binding('child_process').ChildProcess;
-var constants;
-
-
-var spawn = exports.spawn = function(path, args /*, options, customFds */) {
- var child = new ChildProcess();
- child.spawn.apply(child, arguments);
- return child;
-};
-
-
-function setupChannel(target, fd) {
- target._channel = new Stream(fd);
- target._channel.writable = true;
- target._channel.readable = true;
-
- target._channel.resume();
- target._channel.setEncoding('utf8');
-
- var buffer = '';
- target._channel.on('data', function(d) {
- buffer += d;
- var i;
- while ((i = buffer.indexOf('\n')) >= 0) {
- var json = buffer.slice(0, i);
- buffer = buffer.slice(i + 1);
- var m = JSON.parse(json);
- target.emit('message', m);
- }
- });
-
- target.send = function(m) {
- target._channel.write(JSON.stringify(m) + '\n');
- };
-}
-
-
-exports.fork = function(modulePath, args, options) {
- if (!options) options = {};
- options.wantChannel = true;
-
- if (!args) args = [];
- args.unshift(modulePath);
-
- // Unless they gave up customFds, just use the parent process
- if (!options.customFds) options.customFds = [0, 1, 2];
-
- var child = spawn(process.execPath, args, options);
-
- setupChannel(child, child.fds[3]);
-
- child.on('exit', function() {
- child._channel.destroy();
- });
-
- return child;
-};
-
-
-exports._forkChild = function(fd) {
- setupChannel(process, fd);
-};
-
-
-exports.exec = function(command /*, options, callback */) {
- var _slice = Array.prototype.slice;
- var args = ['/bin/sh', ['-c', command]].concat(_slice.call(arguments, 1));
- return exports.execFile.apply(this, args);
-};
-
-
-// execFile('something.sh', { env: ENV }, function() { })
-
-exports.execFile = function(file /* args, options, callback */) {
- var options = { encoding: 'utf8',
- timeout: 0,
- maxBuffer: 200 * 1024,
- killSignal: 'SIGTERM',
- setsid: false,
- cwd: null,
- env: null };
- var args, optionArg, callback;
-
- // Parse the parameters.
-
- if (typeof arguments[arguments.length - 1] === 'function') {
- callback = arguments[arguments.length - 1];
- }
-
- if (Array.isArray(arguments[1])) {
- args = arguments[1];
- if (typeof arguments[2] === 'object') optionArg = arguments[2];
- } else {
- args = [];
- if (typeof arguments[1] === 'object') optionArg = arguments[1];
- }
-
- // Merge optionArg into options
- if (optionArg) {
- var keys = Object.keys(options);
- for (var i = 0, len = keys.length; i < len; i++) {
- var k = keys[i];
- if (optionArg[k] !== undefined) options[k] = optionArg[k];
- }
- }
-
- var child = spawn(file, args, {cwd: options.cwd, env: options.env});
- var stdout = '';
- var stderr = '';
- var killed = false;
- var exited = false;
- var timeoutId;
-
- var err;
-
- function exithandler(code, signal) {
- if (exited) return;
- exited = true;
-
- if (timeoutId) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
-
- if (!callback) return;
-
- if (err) {
- callback(err, stdout, stderr);
- } else if (code === 0 && signal === null) {
- callback(null, stdout, stderr);
- } else {
- var e = new Error('Command failed: ' + stderr);
- e.killed = child.killed || killed;
- e.code = code;
- e.signal = signal;
- callback(e, stdout, stderr);
- }
- }
-
- function kill() {
- killed = true;
- child.kill(options.killSignal);
- process.nextTick(function() {
- exithandler(null, options.killSignal);
- });
- }
-
- if (options.timeout > 0) {
- timeoutId = setTimeout(function() {
- kill();
- timeoutId = null;
- }, options.timeout);
- }
-
- child.stdout.setEncoding(options.encoding);
- child.stderr.setEncoding(options.encoding);
-
- child.stdout.addListener('data', function(chunk) {
- stdout += chunk;
- if (stdout.length > options.maxBuffer) {
- err = new Error('maxBuffer exceeded.');
- kill();
- }
- });
-
- child.stderr.addListener('data', function(chunk) {
- stderr += chunk;
- if (stderr.length > options.maxBuffer) {
- err = new Error('maxBuffer exceeded.');
- kill();
- }
- });
-
- child.addListener('exit', exithandler);
-
- return child;
-};
-
-
-function ChildProcess() {
- EventEmitter.call(this);
-
- var self = this;
-
- this.killed = false;
-
- var gotCHLD = false;
- var exitCode;
- var termSignal;
- var internal = this._internal = new InternalChildProcess();
-
- var stdin = this.stdin = new Stream();
- var stdout = this.stdout = new Stream();
- var stderr = this.stderr = new Stream();
-
- var stderrClosed = false;
- var stdoutClosed = false;
-
- stderr.addListener('close', function() {
- stderrClosed = true;
- if (gotCHLD && (!self.stdout || stdoutClosed)) {
- self.emit('exit', exitCode, termSignal);
- }
- });
-
- stdout.addListener('close', function() {
- stdoutClosed = true;
- if (gotCHLD && (!self.stderr || stderrClosed)) {
- self.emit('exit', exitCode, termSignal);
- }
- });
-
- internal.onexit = function(code, signal) {
- gotCHLD = true;
- exitCode = code;
- termSignal = signal;
- if (self.stdin) {
- self.stdin.end();
- }
- if ((!self.stdout || !self.stdout.readable) &&
- (!self.stderr || !self.stderr.readable)) {
- self.emit('exit', exitCode, termSignal);
- }
- };
-
- this.__defineGetter__('pid', function() { return internal.pid; });
-}
-util.inherits(ChildProcess, EventEmitter);
-
-
-ChildProcess.prototype.kill = function(sig) {
- if (!this.killed && !this.exited) {
- if (!constants) constants = process.binding('constants');
- sig = sig || 'SIGTERM';
- if (!constants[sig]) throw new Error('Unknown signal: ' + sig);
- this.killed = this._internal.kill(constants[sig]);
- }
-};
-
-
-ChildProcess.prototype.spawn = function(path, args, options, customFds) {
- args = args || [];
-
- var cwd, env, setsid, uid, gid;
- if (!options || options.cwd === undefined &&
- options.env === undefined &&
- options.customFds === undefined &&
- options.gid === undefined &&
- options.uid === undefined) {
- // Deprecated API: (path, args, options, env, customFds)
- cwd = '';
- env = options || process.env;
- customFds = customFds || [-1, -1, -1];
- setsid = false;
- uid = -1;
- gid = -1;
- } else {
- // Recommended API: (path, args, options)
- cwd = options.cwd || '';
- env = options.env || process.env;
- customFds = options.customFds || [-1, -1, -1];
- setsid = options.setsid ? true : false;
- uid = options.hasOwnProperty('uid') ? options.uid : -1;
- gid = options.hasOwnProperty('gid') ? options.gid : -1;
- }
-
- var envPairs = [];
- var keys = Object.keys(env);
- for (var key in env) {
- envPairs.push(key + '=' + env[key]);
- }
-
- if (options && options.wantChannel) {
- // The FILLMEIN will be replaced in C land with an integer!
- // AWFUL! :D
- envPairs.push('NODE_CHANNEL_FD=FILLMEIN');
- }
-
- var fds = this._internal.spawn(path,
- args,
- cwd,
- envPairs,
- customFds,
- setsid,
- uid,
- gid);
- this.fds = fds;
-
- if (customFds[0] === -1 || customFds[0] === undefined) {
- this.stdin.open(fds[0]);
- this.stdin.writable = true;
- this.stdin.readable = false;
- } else {
- this.stdin = null;
- }
-
- if (customFds[1] === -1 || customFds[1] === undefined) {
- this.stdout.open(fds[1]);
- this.stdout.writable = false;
- this.stdout.readable = true;
- this.stdout.resume();
- } else {
- this.stdout = null;
- }
-
- if (customFds[2] === -1 || customFds[2] === undefined) {
- this.stderr.open(fds[2]);
- this.stderr.writable = false;
- this.stderr.readable = true;
- this.stderr.resume();
- } else {
- this.stderr = null;
- }
-};
-
+++ /dev/null
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-var EventEmitter = require('events').EventEmitter;
-var net = require('net');
-var Process = process.binding('process_wrap').Process;
-var inherits = require('util').inherits;
-var constants; // if (!constants) constants = process.binding('constants');
-
-var LF = '\n'.charCodeAt(0);
-var Pipe;
-
-
-// constructors for lazy loading
-function createPipe(ipc) {
- // Lazy load
- if (!Pipe) {
- Pipe = new process.binding('pipe_wrap').Pipe;
- }
-
- return new Pipe(ipc);
-}
-
-function createSocket(pipe, readable) {
- var s = new net.Socket({ handle: pipe });
-
- if (readable) {
- s.writable = false;
- s.readable = true;
- s.resume();
- } else {
- s.writable = true;
- s.readable = false;
- }
-
- return s;
-}
-
-function mergeOptions(target, overrides) {
- if (overrides) {
- var keys = Object.keys(overrides);
- for (var i = 0, len = keys.length; i < len; i++) {
- var k = keys[i];
- if (overrides[k] !== undefined) {
- target[k] = overrides[k];
- }
- }
- }
- return target;
-}
-
-
-function setupChannel(target, channel) {
- target._channel = channel;
-
- var jsonBuffer = '';
-
- channel.onread = function(pool, offset, length, recvHandle) {
- if (pool) {
- jsonBuffer += pool.toString('ascii', offset, offset + length);
-
- var i;
- while ((i = jsonBuffer.indexOf('\n')) >= 0) {
- var json = jsonBuffer.slice(0, i);
- var message = JSON.parse(json);
- jsonBuffer = jsonBuffer.slice(i + 1);
-
- target.emit('message', message, recvHandle);
- }
-
- } else {
- channel.close();
- target._channel = null;
- }
- };
-
- target.send = function(message, sendHandle) {
- if (!target._channel) throw new Error("channel closed");
-
- // For overflow protection don't write if channel queue is too deep.
- if (channel.writeQueueSize > 1024 * 1024) {
- return false;
- }
-
- var buffer = Buffer(JSON.stringify(message) + '\n');
-
- var writeReq = channel.write(buffer, 0, buffer.length, sendHandle);
-
- if (!writeReq) {
- throw new Error(errno + " cannot write to IPC channel.");
- }
-
- writeReq.oncomplete = nop;
-
- return true;
- };
-
- channel.readStart();
-}
-
-
-function nop() { }
-
-
-exports.fork = function(modulePath, args, options) {
- if (!options) options = {};
-
- if (!args) args = [];
- args.unshift(modulePath);
-
- if (options.stdinStream) {
- throw new Error("stdinStream not allowed for fork()");
- }
-
- if (options.customFds) {
- throw new Error("customFds not allowed for fork()");
- }
-
- // Leave stdin open for the IPC channel. stdout and stderr should be the
- // same as the parent's.
- options.customFds = [ -1, 1, 2 ];
-
- // Just need to set this - child process won't actually use the fd.
- // For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6.
- if (!options.env) options.env = { };
- options.env.NODE_CHANNEL_FD = 42;
-
- // stdin is the IPC channel.
- options.stdinStream = createPipe(true);
-
- var child = spawn(process.execPath, args, options);
-
- setupChannel(child, options.stdinStream);
-
- child.on('exit', function() {
- if (child._channel) {
- child._channel.close();
- }
- });
-
- return child;
-};
-
-
-exports._forkChild = function() {
- // set process.send()
- var p = createPipe(true);
- p.open(0);
- setupChannel(process, p);
-};
-
-
-exports.exec = function(command /*, options, callback */) {
- var file, args, options, callback;
-
- if (typeof arguments[1] === 'function') {
- options = undefined;
- callback = arguments[1];
- } else {
- options = arguments[1];
- callback = arguments[2];
- }
-
- if (process.platform === 'win32') {
- file = 'cmd.exe';
- args = ['/s', '/c', '"' + command + '"'];
- // Make a shallow copy before patching so we don't clobber the user's
- // options object.
- options = mergeOptions({}, options);
- options.windowsVerbatimArguments = true;
- } else {
- file = '/bin/sh';
- args = ['-c', command];
- }
- return exports.execFile(file, args, options, callback);
-};
-
-
-exports.execFile = function(file /* args, options, callback */) {
- var args, optionArg, callback;
- var options = {
- encoding: 'utf8',
- timeout: 0,
- maxBuffer: 200 * 1024,
- killSignal: 'SIGTERM',
- setsid: false,
- cwd: null,
- env: null
- };
-
- // Parse the parameters.
-
- if (typeof arguments[arguments.length - 1] === 'function') {
- callback = arguments[arguments.length - 1];
- }
-
- if (Array.isArray(arguments[1])) {
- args = arguments[1];
- if (typeof arguments[2] === 'object') optionArg = arguments[2];
- } else {
- args = [];
- if (typeof arguments[1] === 'object') optionArg = arguments[1];
- }
-
- // Merge optionArg into options
- mergeOptions(options, optionArg);
-
- var child = spawn(file, args, {
- cwd: options.cwd,
- env: options.env,
- windowsVerbatimArguments: !!options.windowsVerbatimArguments
- });
-
- var stdout = '';
- var stderr = '';
- var killed = false;
- var exited = false;
- var timeoutId;
-
- var err;
-
- function exithandler(code, signal) {
- if (exited) return;
- exited = true;
-
- if (timeoutId) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
-
- if (!callback) return;
-
- if (err) {
- callback(err, stdout, stderr);
- } else if (code === 0 && signal === null) {
- callback(null, stdout, stderr);
- } else {
- var e = new Error('Command failed: ' + stderr);
- e.killed = child.killed || killed;
- e.code = code;
- e.signal = signal;
- callback(e, stdout, stderr);
- }
- }
-
- function kill() {
- killed = true;
- child.kill(options.killSignal);
- process.nextTick(function() {
- exithandler(null, options.killSignal);
- });
- }
-
- if (options.timeout > 0) {
- timeoutId = setTimeout(function() {
- kill();
- timeoutId = null;
- }, options.timeout);
- }
-
- child.stdout.setEncoding(options.encoding);
- child.stderr.setEncoding(options.encoding);
-
- child.stdout.addListener('data', function(chunk) {
- stdout += chunk;
- if (stdout.length > options.maxBuffer) {
- err = new Error('maxBuffer exceeded.');
- kill();
- }
- });
-
- child.stderr.addListener('data', function(chunk) {
- stderr += chunk;
- if (stderr.length > options.maxBuffer) {
- err = new Error('maxBuffer exceeded.');
- kill();
- }
- });
-
- child.addListener('exit', exithandler);
-
- return child;
-};
-
-
-var spawn = exports.spawn = function(file, args, options) {
- var args = args ? args.slice(0) : [];
- args.unshift(file);
-
- var env = (options ? options.env : null) || process.env;
- var envPairs = [];
- var keys = Object.keys(env);
- for (var key in env) {
- envPairs.push(key + '=' + env[key]);
- }
-
- var child = new ChildProcess();
-
- child.spawn({
- file: file,
- args: args,
- cwd: options ? options.cwd : null,
- windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
- envPairs: envPairs,
- customFds: options ? options.customFds : null,
- stdinStream: options ? options.stdinStream : null
- });
-
- return child;
-};
-
-
-function maybeExit(subprocess) {
- subprocess._closesGot++;
-
- if (subprocess._closesGot == subprocess._closesNeeded) {
- subprocess.emit('exit', subprocess.exitCode, subprocess.signalCode);
- }
-}
-
-
-function ChildProcess() {
- var self = this;
-
- this._closesNeeded = 1;
- this._closesGot = 0;
-
- this.signalCode = null;
- this.exitCode = null;
-
- this._internal = new Process();
- this._internal.onexit = function(exitCode, signalCode) {
- //
- // follow 0.4.x behaviour:
- //
- // - normally terminated processes don't touch this.signalCode
- // - signaled processes don't touch this.exitCode
- //
- if (signalCode) {
- self.signalCode = signalCode;
- } else {
- self.exitCode = exitCode;
- }
-
- if (self.stdin) {
- self.stdin.destroy();
- }
-
- self._internal.close();
- self._internal = null;
-
- maybeExit(self);
- };
-}
-inherits(ChildProcess, EventEmitter);
-
-
-function setStreamOption(name, index, options) {
- // Skip if we already have options.stdinStream
- if (options[name]) return;
-
- if (options.customFds &&
- typeof options.customFds[index] == 'number' &&
- options.customFds[index] !== -1) {
- if (options.customFds[index] === index) {
- options[name] = null;
- } else {
- throw new Error('customFds not yet supported');
- }
- } else {
- options[name] = createPipe();
- }
-}
-
-
-ChildProcess.prototype.spawn = function(options) {
- var self = this;
-
- setStreamOption('stdinStream', 0, options);
- setStreamOption('stdoutStream', 1, options);
- setStreamOption('stderrStream', 2, options);
-
- var r = this._internal.spawn(options);
-
- if (r) {
- if (options.stdinStream) {
- options.stdinStream.close();
- }
-
- if (options.stdoutStream) {
- options.stdoutStream.close();
- }
-
- if (options.stderrStream) {
- options.stderrStream.close();
- }
-
- this._internal.close();
- this._internal = null;
- throw errnoException('spawn', errno);
- }
-
- this.pid = this._internal.pid;
-
- if (options.stdinStream) {
- this.stdin = createSocket(options.stdinStream, false);
- }
-
- if (options.stdoutStream) {
- this.stdout = createSocket(options.stdoutStream, true);
- this._closesNeeded++;
- this.stdout.on('close', function() {
- maybeExit(self);
- });
- }
-
- if (options.stderrStream) {
- this.stderr = createSocket(options.stderrStream, true);
- this._closesNeeded++;
- this.stderr.on('close', function() {
- maybeExit(self);
- });
- }
-
- return r;
-};
-
-
-function errnoException(errorno, syscall) {
- // TODO make this more compatible with ErrnoException from src/node.cc
- // Once all of Node is using this function the ErrnoException from
- // src/node.cc should be removed.
- var e = new Error(syscall + ' ' + errorno);
- e.errno = e.code = errorno;
- e.syscall = syscall;
- return e;
-}
-
-
-ChildProcess.prototype.kill = function(sig) {
- if (!constants) {
- constants = process.binding('constants');
- }
-
- sig = sig || 'SIGTERM';
- var signal = constants[sig];
-
- if (!signal) {
- throw new Error('Unknown signal: ' + sig);
- }
-
- if (this._internal) {
- var r = this._internal.kill(signal);
- // TODO: raise error if r == -1?
- }
-};
'lib/assert.js',
'lib/buffer.js',
'lib/buffer_ieee754.js',
- 'lib/child_process_legacy.js',
- 'lib/child_process_uv.js',
+ 'lib/child_process.js',
'lib/console.js',
'lib/constants.js',
'lib/crypto.js',
'src/node.h',
'src/node_buffer.h',
'src/node_cares.h',
- 'src/node_child_process.h',
'src/node_constants.h',
'src/node_crypto.h',
'src/node_dtrace.h',
'src/node_stat_watcher.cc',
'src/node_io_watcher.cc',
'src/node_stdio.cc',
- 'src/node_child_process.cc',
]
}],
[ 'OS=="mac"', {
# include <node_signal_watcher.h>
# include <node_stat_watcher.h>
#endif
-#if !defined(_MSC_VER)
-#include <node_child_process.h>
-#endif
#include <node_constants.h>
#include <node_stdio.h>
#include <node_javascript.h>
case 'tty':
return process.features.uv ? 'tty_uv' : 'tty_legacy';
- case 'child_process':
- return process.features.uv ? 'child_process_uv' :
- 'child_process_legacy';
-
case 'dgram':
return process.features.uv ? 'dgram_uv' : 'dgram_legacy';
+++ /dev/null
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-#include <node_child_process.h>
-#include <node.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <pwd.h> /* getpwnam() */
-#include <grp.h> /* getgrnam() */
-#if defined(__FreeBSD__ ) || defined(__OpenBSD__)
-#include <sys/wait.h>
-#endif
-
-#include <sys/socket.h> /* socketpair */
-#include <sys/un.h>
-
-# ifdef __APPLE__
-# include <crt_externs.h>
-# define environ (*_NSGetEnviron())
-# else
-extern char **environ;
-# endif
-
-#include <limits.h> /* PATH_MAX */
-
-namespace node {
-
-using namespace v8;
-
-static Persistent<String> pid_symbol;
-static Persistent<String> onexit_symbol;
-
-
-// TODO share with other modules
-static inline int SetNonBlocking(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- if (r != 0) {
- perror("SetNonBlocking()");
- }
- return r;
-}
-
-
-static inline int SetCloseOnExec(int fd) {
- int flags = fcntl(fd, F_GETFD, 0);
- int r = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
- if (r != 0) {
- perror("SetCloseOnExec()");
- }
- return r;
-}
-
-
-static inline int ResetFlags(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- // blocking
- int r = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
- flags = fcntl(fd, F_GETFD, 0);
- // unset the CLOEXEC
- fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC);
- return r;
-}
-
-
-void ChildProcess::Initialize(Handle<Object> target) {
- HandleScope scope;
-
- Local<FunctionTemplate> t = FunctionTemplate::New(ChildProcess::New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
- t->SetClassName(String::NewSymbol("ChildProcess"));
-
- pid_symbol = NODE_PSYMBOL("pid");
- onexit_symbol = NODE_PSYMBOL("onexit");
-
- NODE_SET_PROTOTYPE_METHOD(t, "spawn", ChildProcess::Spawn);
- NODE_SET_PROTOTYPE_METHOD(t, "kill", ChildProcess::Kill);
-
- target->Set(String::NewSymbol("ChildProcess"), t->GetFunction());
-}
-
-
-Handle<Value> ChildProcess::New(const Arguments& args) {
- HandleScope scope;
- ChildProcess *p = new ChildProcess();
- p->Wrap(args.Holder());
- return args.This();
-}
-
-
-// This is an internal function. The third argument should be an array
-// of key value pairs seperated with '='.
-Handle<Value> ChildProcess::Spawn(const Arguments& args) {
- HandleScope scope;
-
- if (args.Length() < 3 ||
- !args[0]->IsString() ||
- !args[1]->IsArray() ||
- !args[2]->IsString() ||
- !args[3]->IsArray() ||
- !args[4]->IsArray() ||
- !args[5]->IsBoolean() ||
- !(args[6]->IsInt32() || args[6]->IsString()) ||
- !(args[7]->IsInt32() || args[7]->IsString())) {
- return ThrowException(Exception::Error(String::New("Bad argument.")));
- }
-
- ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
-
- String::Utf8Value file(args[0]->ToString());
-
- int i;
-
- // Copy second argument args[1] into a c-string array called argv.
- // The array must be null terminated, and the first element must be
- // the name of the executable -- hence the complication.
- Local<Array> argv_handle = Local<Array>::Cast(args[1]);
- int argc = argv_handle->Length();
- int argv_length = argc + 1 + 1;
- char **argv = new char*[argv_length]; // heap allocated to detect errors
- argv[0] = strdup(*file); // + 1 for file
- argv[argv_length-1] = NULL; // + 1 for NULL;
- for (i = 0; i < argc; i++) {
- String::Utf8Value arg(argv_handle->Get(Integer::New(i))->ToString());
- argv[i+1] = strdup(*arg);
- }
-
- // Copy third argument, args[2], into a c-string called cwd.
- String::Utf8Value arg(args[2]->ToString());
- char *cwd = strdup(*arg);
-
- // Copy fourth argument, args[3], into a c-string array called env.
- Local<Array> env_handle = Local<Array>::Cast(args[3]);
- int envc = env_handle->Length();
- char **env = new char*[envc + 1]; // heap allocated to detect errors
- env[envc] = NULL;
- for (int i = 0; i < envc; i++) {
- String::Utf8Value pair(env_handle->Get(Integer::New(i))->ToString());
- env[i] = strdup(*pair);
- }
-
- int custom_fds[3] = { -1, -1, -1 };
- if (args[4]->IsArray()) {
- // Set the custom file descriptor values (if any) for the child process
- Local<Array> custom_fds_handle = Local<Array>::Cast(args[4]);
-
- int custom_fds_len = custom_fds_handle->Length();
- // Bound by 3.
- if (custom_fds_len > 3) {
- custom_fds_len = 3;
- }
-
- for (int i = 0; i < custom_fds_len; i++) {
- if (custom_fds_handle->Get(i)->IsUndefined()) continue;
- Local<Integer> fd = custom_fds_handle->Get(i)->ToInteger();
- custom_fds[i] = fd->Value();
- }
- }
-
- int do_setsid = false;
- if (args[5]->IsBoolean()) {
- do_setsid = args[5]->BooleanValue();
- }
-
-
- int fds[3];
-
- char *custom_uname = NULL;
- int custom_uid = -1;
- if (args[6]->IsNumber()) {
- custom_uid = args[6]->Int32Value();
- } else if (args[6]->IsString()) {
- String::Utf8Value pwnam(args[6]->ToString());
- custom_uname = (char *)calloc(sizeof(char), pwnam.length() + 1);
- strncpy(custom_uname, *pwnam, pwnam.length() + 1);
- } else {
- return ThrowException(Exception::Error(
- String::New("setuid argument must be a number or a string")));
- }
-
- char *custom_gname = NULL;
- int custom_gid = -1;
- if (args[7]->IsNumber()) {
- custom_gid = args[7]->Int32Value();
- } else if (args[7]->IsString()) {
- String::Utf8Value grnam(args[7]->ToString());
- custom_gname = (char *)calloc(sizeof(char), grnam.length() + 1);
- strncpy(custom_gname, *grnam, grnam.length() + 1);
- } else {
- return ThrowException(Exception::Error(
- String::New("setgid argument must be a number or a string")));
- }
-
- int channel_fd = -1;
-
- int r = child->Spawn(argv[0],
- argv,
- cwd,
- env,
- fds,
- custom_fds,
- do_setsid,
- custom_uid,
- custom_uname,
- custom_gid,
- custom_gname,
- &channel_fd);
-
- if (custom_uname != NULL) free(custom_uname);
- if (custom_gname != NULL) free(custom_gname);
-
- for (i = 0; i < argv_length; i++) free(argv[i]);
- delete [] argv;
-
- free(cwd);
-
- for (i = 0; i < envc; i++) free(env[i]);
- delete [] env;
-
- if (r != 0) {
- return ThrowException(Exception::Error(String::New("Error spawning")));
- }
-
-
- Local<Array> a = Array::New(channel_fd >= 0 ? 4 : 3);
-
- assert(fds[0] >= 0);
- a->Set(0, Integer::New(fds[0])); // stdin
- assert(fds[1] >= 0);
- a->Set(1, Integer::New(fds[1])); // stdout
- assert(fds[2] >= 0);
- a->Set(2, Integer::New(fds[2])); // stderr
-
- if (channel_fd >= 0) {
- a->Set(3, Integer::New(channel_fd));
- }
-
- return scope.Close(a);
-}
-
-
-Handle<Value> ChildProcess::Kill(const Arguments& args) {
- HandleScope scope;
- ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
- assert(child);
-
- if (child->pid_ < 1) {
- // nothing to do
- return False();
- }
-
- int sig = SIGTERM;
-
- if (args.Length() > 0) {
- if (args[0]->IsNumber()) {
- sig = args[0]->Int32Value();
- } else {
- return ThrowException(Exception::TypeError(String::New("Bad argument.")));
- }
- }
-
- if (child->Kill(sig) != 0) {
- return ThrowException(ErrnoException(errno, "Kill"));
- }
-
- return True();
-}
-
-
-void ChildProcess::Stop() {
- if (ev_is_active(&child_watcher_)) {
- ev_child_stop(EV_DEFAULT_UC_ &child_watcher_);
- Unref();
- }
- // Don't kill the PID here. We want to allow for killing the parent
- // process and reparenting to initd. This is perhaps not going the best
- // technique for daemonizing, but I don't want to rule it out.
- pid_ = -1;
-}
-
-
-// Note that args[0] must be the same as the "file" param. This is an
-// execvp() requirement.
-//
-// TODO: The arguments are rediculously long. Needs to be put into a struct.
-//
-int ChildProcess::Spawn(const char *file,
- char *const args[],
- const char *cwd,
- char **env,
- int stdio_fds[3],
- int custom_fds[3],
- bool do_setsid,
- int custom_uid,
- char *custom_uname,
- int custom_gid,
- char *custom_gname,
- int* channel) {
- HandleScope scope;
- assert(pid_ == -1);
- assert(!ev_is_active(&child_watcher_));
-
- int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
-
- /* An implementation of popen(), basically */
- if ((custom_fds[0] == -1 && pipe(stdin_pipe) < 0) ||
- (custom_fds[1] == -1 && pipe(stdout_pipe) < 0) ||
- (custom_fds[2] == -1 && pipe(stderr_pipe) < 0)) {
- perror("pipe()");
- return -1;
- }
-
- // Set the close-on-exec FD flag
- if (custom_fds[0] == -1) {
- SetCloseOnExec(stdin_pipe[0]);
- SetCloseOnExec(stdin_pipe[1]);
- }
-
- if (custom_fds[1] == -1) {
- SetCloseOnExec(stdout_pipe[0]);
- SetCloseOnExec(stdout_pipe[1]);
- }
-
- if (custom_fds[2] == -1) {
- SetCloseOnExec(stderr_pipe[0]);
- SetCloseOnExec(stderr_pipe[1]);
- }
-
-
- // The channel will be used by js-land "fork()" for a little JSON channel.
- // The pointer is used to pass one end of the socket pair back to the
- // parent.
- // channel_fds[0] is for the parent
- // channel_fds[1] is for the child
- int channel_fds[2] = { -1, -1 };
-
-#define NODE_CHANNEL_FD "NODE_CHANNEL_FD"
-
- for (int i = 0; env[i]; i++) {
- if (!strncmp(env[i], NODE_CHANNEL_FD, sizeof NODE_CHANNEL_FD - 1)) {
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel_fds)) {
- perror("socketpair()");
- return -1;
- }
-
- assert(channel_fds[0] >= 0 && channel_fds[1] >= 0);
-
- SetNonBlocking(channel_fds[0]);
- SetNonBlocking(channel_fds[1]);
- // Write over the FILLMEIN :D
- sprintf(env[i], NODE_CHANNEL_FD "=%d", channel_fds[1]);
- }
- }
-
- // Save environ in the case that we get it clobbered
- // by the child process.
- char **save_our_env = environ;
-
- switch (pid_ = fork()) {
- case -1: // Error.
- Stop();
- return -4;
-
- case 0: // Child.
- if (do_setsid && setsid() < 0) {
- perror("setsid");
- _exit(127);
- }
-
- if (custom_fds[0] == -1) {
- close(stdin_pipe[1]); // close write end
- dup2(stdin_pipe[0], STDIN_FILENO);
- } else {
- ResetFlags(custom_fds[0]);
- dup2(custom_fds[0], STDIN_FILENO);
- }
-
- if (custom_fds[1] == -1) {
- close(stdout_pipe[0]); // close read end
- dup2(stdout_pipe[1], STDOUT_FILENO);
- } else {
- ResetFlags(custom_fds[1]);
- dup2(custom_fds[1], STDOUT_FILENO);
- }
-
- if (custom_fds[2] == -1) {
- close(stderr_pipe[0]); // close read end
- dup2(stderr_pipe[1], STDERR_FILENO);
- } else {
- ResetFlags(custom_fds[2]);
- dup2(custom_fds[2], STDERR_FILENO);
- }
-
- if (strlen(cwd) && chdir(cwd)) {
- perror("chdir()");
- _exit(127);
- }
-
-
- static char buf[PATH_MAX + 1];
-
- int gid = -1;
- if (custom_gid != -1) {
- gid = custom_gid;
- } else if (custom_gname != NULL) {
- struct group grp, *grpp = NULL;
- int err = getgrnam_r(custom_gname,
- &grp,
- buf,
- PATH_MAX + 1,
- &grpp);
-
- if (err || grpp == NULL) {
- perror("getgrnam_r()");
- _exit(127);
- }
-
- gid = grpp->gr_gid;
- }
-
-
- int uid = -1;
- if (custom_uid != -1) {
- uid = custom_uid;
- } else if (custom_uname != NULL) {
- struct passwd pwd, *pwdp = NULL;
- int err = getpwnam_r(custom_uname,
- &pwd,
- buf,
- PATH_MAX + 1,
- &pwdp);
-
- if (err || pwdp == NULL) {
- perror("getpwnam_r()");
- _exit(127);
- }
-
- uid = pwdp->pw_uid;
- }
-
-
- if (gid != -1 && setgid(gid)) {
- perror("setgid()");
- _exit(127);
- }
-
- if (uid != -1 && setuid(uid)) {
- perror("setuid()");
- _exit(127);
- }
-
- // Close the parent's end of the channel.
- if (channel_fds[0] >= 0) {
- close(channel_fds[0]);
- channel_fds[0] = -1;
- }
-
- environ = env;
-
- execvp(file, args);
- perror("execvp()");
- _exit(127);
- }
-
- // Parent.
-
- // Restore environment.
- environ = save_our_env;
-
- ev_child_set(&child_watcher_, pid_, 0);
- ev_child_start(EV_DEFAULT_UC_ &child_watcher_);
- Ref();
- handle_->Set(pid_symbol, Integer::New(pid_));
-
- if (custom_fds[0] == -1) {
- close(stdin_pipe[0]);
- stdio_fds[0] = stdin_pipe[1];
- SetNonBlocking(stdin_pipe[1]);
- } else {
- stdio_fds[0] = custom_fds[0];
- }
-
- if (custom_fds[1] == -1) {
- close(stdout_pipe[1]);
- stdio_fds[1] = stdout_pipe[0];
- SetNonBlocking(stdout_pipe[0]);
- } else {
- stdio_fds[1] = custom_fds[1];
- }
-
- if (custom_fds[2] == -1) {
- close(stderr_pipe[1]);
- stdio_fds[2] = stderr_pipe[0];
- SetNonBlocking(stderr_pipe[0]);
- } else {
- stdio_fds[2] = custom_fds[2];
- }
-
- // Close the child's end of the channel.
- if (channel_fds[1] >= 0) {
- close(channel_fds[1]);
- channel_fds[1] = -1;
- assert(channel_fds[0] >= 0);
- assert(channel);
- *channel = channel_fds[0];
- } else {
- *channel = -1;
- }
-
- return 0;
-}
-
-
-void ChildProcess::OnExit(int status) {
- HandleScope scope;
-
- pid_ = -1;
- Stop();
-
- handle_->Set(pid_symbol, Null());
-
- Local<Value> onexit_v = handle_->Get(onexit_symbol);
- assert(onexit_v->IsFunction());
- Local<Function> onexit = Local<Function>::Cast(onexit_v);
-
- TryCatch try_catch;
-
- Local<Value> argv[2];
- if (WIFEXITED(status)) {
- argv[0] = Integer::New(WEXITSTATUS(status));
- } else {
- argv[0] = Local<Value>::New(Null());
- }
-
- if (WIFSIGNALED(status)) {
- argv[1] = String::NewSymbol(signo_string(WTERMSIG(status)));
- } else {
- argv[1] = Local<Value>::New(Null());
- }
-
- onexit->Call(handle_, 2, argv);
-
- if (try_catch.HasCaught()) {
- FatalException(try_catch);
- }
-}
-
-
-int ChildProcess::Kill(int sig) {
- if (pid_ < 1) return -1;
- return kill(pid_, sig);
-}
-
-} // namespace node
-
-NODE_MODULE(node_child_process, node::ChildProcess::Initialize);
+++ /dev/null
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-#ifndef NODE_CHILD_PROCESS_H_
-#define NODE_CHILD_PROCESS_H_
-
-#include <node.h>
-#include <node_object_wrap.h>
-
-#include <v8.h>
-#include <uv.h>
-
-#ifdef __POSIX__
-# include <uv-private/ev.h>
-#endif
-
-#ifdef __MINGW32__
-# include <platform_win32.h> // HANDLE type
-#endif
-
-// ChildProcess is a thin wrapper around ev_child. It has the extra
-// functionality that it can spawn a child process with pipes connected to
-// its stdin, stdout, stderr. This class is not meant to be exposed to but
-// wrapped up in a more friendly EventEmitter with streams for each of the
-// pipes.
-//
-// When the child process exits (when the parent receives SIGCHLD) the
-// callback child.onexit will be called.
-
-namespace node {
-
-class ChildProcess : ObjectWrap {
- public:
- static void Initialize(v8::Handle<v8::Object> target);
-
- protected:
- static v8::Handle<v8::Value> New(const v8::Arguments& args);
- static v8::Handle<v8::Value> Spawn(const v8::Arguments& args);
- static v8::Handle<v8::Value> Kill(const v8::Arguments& args);
-
- ChildProcess() : ObjectWrap() {
-#ifdef __POSIX__
- ev_init(&child_watcher_, ChildProcess::on_chld);
- child_watcher_.data = this;
-#endif // __POSIX__
-
- pid_ = -1;
-
-#ifdef __MINGW32__
- InitializeCriticalSection(&info_lock_);
- kill_me_ = false;
- did_start_ = false;
- exit_signal_ = 0;
-#endif // __MINGW32__
- }
-
- ~ChildProcess() {
-#ifdef __POSIX__
- Stop();
-#endif // __POSIX__
- }
-
- // Returns 0 on success. stdio_fds will contain file desciptors for stdin,
- // stdout, and stderr of the subprocess. stdin is writable; the other two
- // are readable.
- // The user of this class has responsibility to close these pipes after
- // the child process exits.
- int Spawn(const char *file,
- char *const argv[],
- const char *cwd,
- char **env,
- int stdio_fds[3],
- int custom_fds[3],
- bool do_setsid,
- int custom_uid,
- char *custom_uname,
- int custom_gid,
- char *custom_gname,
- int* channel);
-
- // Simple syscall wrapper. Does not disable the watcher. onexit will be
- // called still.
- int Kill(int sig);
-
- private:
- void OnExit(int code);
-
-#ifdef __POSIX__ // Shouldn't this just move to node_child_process.cc?
- void Stop(void);
-
- static void on_chld(EV_P_ ev_child *watcher, int revents) {
- ChildProcess *child = static_cast<ChildProcess*>(watcher->data);
- assert(revents == EV_CHILD);
- assert(child->pid_ == watcher->rpid);
- assert(&child->child_watcher_ == watcher);
- child->OnExit(watcher->rstatus);
- }
-
- ev_child child_watcher_;
- pid_t pid_;
-#endif // __POSIX__
-
-#ifdef __MINGW32__
- static void watch(ChildProcess *child);
- static void CALLBACK watch_wait_callback(void *data, BOOLEAN didTimeout);
- static void notify_spawn_failure(ChildProcess *child);
- static void notify_exit(uv_async_t* watcher, int status);
- static int do_kill(ChildProcess *child, int sig);static void close_stdio_handles(ChildProcess *child);
-
- int pid_;
- int exit_signal_;
-
- WCHAR *application_;
- WCHAR *arguments_;
- WCHAR *env_win_;
- WCHAR *cwd_;
- const WCHAR *path_;
- const WCHAR *path_ext_;
-
- HANDLE stdio_handles_[3];
- bool got_custom_fds_[3];
-
- CRITICAL_SECTION info_lock_;
- bool did_start_;
- bool kill_me_;
- HANDLE wait_handle_;
- HANDLE process_handle_;
-#endif // __MINGW32__
-};
-
-} // namespace node
-#endif // NODE_CHILD_PROCESS_H_
NODE_EXT_LIST_ITEM(node_buffer)
#ifdef __POSIX__
NODE_EXT_LIST_ITEM(node_cares)
-NODE_EXT_LIST_ITEM(node_child_process)
#endif
#if HAVE_OPENSSL
NODE_EXT_LIST_ITEM(node_crypto)
node.source += " src/node_stat_watcher.cc "
node.source += " src/node_io_watcher.cc "
node.source += " src/node_stdio.cc "
- node.source += " src/node_child_process.cc "
node.source += bld.env["PLATFORM_FILE"]
if not product_type_is_lib: