diff --git a/lib/child_process_uv.js b/lib/child_process.js similarity index 100% rename from lib/child_process_uv.js rename to lib/child_process.js diff --git a/lib/child_process_legacy.js b/lib/child_process_legacy.js deleted file mode 100644 index edcbaa0a73c..00000000000 --- a/lib/child_process_legacy.js +++ /dev/null @@ -1,339 +0,0 @@ -// 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; - } -}; - diff --git a/node.gyp b/node.gyp index c87c12e90fa..5f85101d8bf 100644 --- a/node.gyp +++ b/node.gyp @@ -15,8 +15,7 @@ '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', @@ -102,7 +101,6 @@ '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', @@ -175,7 +173,6 @@ 'src/node_stat_watcher.cc', 'src/node_io_watcher.cc', 'src/node_stdio.cc', - 'src/node_child_process.cc', ] }], [ 'OS=="mac"', { diff --git a/src/node.cc b/src/node.cc index 8a1eea51851..e7bd4ccd3c1 100644 --- a/src/node.cc +++ b/src/node.cc @@ -84,9 +84,6 @@ extern "C" { # include # include #endif -#if !defined(_MSC_VER) -#include -#endif #include #include #include diff --git a/src/node.js b/src/node.js index 28ca90116ee..427d05cdb15 100644 --- a/src/node.js +++ b/src/node.js @@ -461,10 +461,6 @@ 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'; diff --git a/src/node_child_process.cc b/src/node_child_process.cc deleted file mode 100644 index 87b5cfa9141..00000000000 --- a/src/node_child_process.cc +++ /dev/null @@ -1,580 +0,0 @@ -// 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 -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* getpwnam() */ -#include /* getgrnam() */ -#if defined(__FreeBSD__ ) || defined(__OpenBSD__) -#include -#endif - -#include /* socketpair */ -#include - -# ifdef __APPLE__ -# include -# define environ (*_NSGetEnviron()) -# else -extern char **environ; -# endif - -#include /* PATH_MAX */ - -namespace node { - -using namespace v8; - -static Persistent pid_symbol; -static Persistent 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 target) { - HandleScope scope; - - Local 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 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 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(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 argv_handle = Local::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 env_handle = Local::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 custom_fds_handle = Local::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 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 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 ChildProcess::Kill(const Arguments& args) { - HandleScope scope; - ChildProcess *child = ObjectWrap::Unwrap(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 onexit_v = handle_->Get(onexit_symbol); - assert(onexit_v->IsFunction()); - Local onexit = Local::Cast(onexit_v); - - TryCatch try_catch; - - Local argv[2]; - if (WIFEXITED(status)) { - argv[0] = Integer::New(WEXITSTATUS(status)); - } else { - argv[0] = Local::New(Null()); - } - - if (WIFSIGNALED(status)) { - argv[1] = String::NewSymbol(signo_string(WTERMSIG(status))); - } else { - argv[1] = Local::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); diff --git a/src/node_child_process.h b/src/node_child_process.h deleted file mode 100644 index b815a3fd2db..00000000000 --- a/src/node_child_process.h +++ /dev/null @@ -1,150 +0,0 @@ -// 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 -#include - -#include -#include - -#ifdef __POSIX__ -# include -#endif - -#ifdef __MINGW32__ -# include // 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 target); - - protected: - static v8::Handle New(const v8::Arguments& args); - static v8::Handle Spawn(const v8::Arguments& args); - static v8::Handle 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(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_ diff --git a/src/node_extensions.h b/src/node_extensions.h index 3b0731605e0..f4ff7a8dc0c 100644 --- a/src/node_extensions.h +++ b/src/node_extensions.h @@ -24,7 +24,6 @@ NODE_EXT_LIST_START 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) diff --git a/wscript b/wscript index e1ef0d01817..2f1b959c990 100644 --- a/wscript +++ b/wscript @@ -907,7 +907,6 @@ def build(bld): 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: