Remove child_process_legacy
authorRyan Dahl <ry@tinyclouds.org>
Tue, 11 Oct 2011 20:16:33 +0000 (13:16 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Tue, 11 Oct 2011 20:16:33 +0000 (13:16 -0700)
lib/child_process.js [new file with mode: 0644]
lib/child_process_legacy.js [deleted file]
lib/child_process_uv.js [deleted file]
node.gyp
src/node.cc
src/node.js
src/node_child_process.cc [deleted file]
src/node_child_process.h [deleted file]
src/node_extensions.h
wscript

diff --git a/lib/child_process.js b/lib/child_process.js
new file mode 100644 (file)
index 0000000..f2a1a7f
--- /dev/null
@@ -0,0 +1,474 @@
+// 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?
+  }
+};
diff --git a/lib/child_process_legacy.js b/lib/child_process_legacy.js
deleted file mode 100644 (file)
index edcbaa0..0000000
+++ /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/lib/child_process_uv.js b/lib/child_process_uv.js
deleted file mode 100644 (file)
index f2a1a7f..0000000
+++ /dev/null
@@ -1,474 +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 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?
-  }
-};
index c87c12e90face8d65c94bd5b92683ea174edec3e..5f85101d8bfb71a49ce6b1b822f7e02bde525803 100644 (file)
--- 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',
         '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"', {
index 8a1eea518512ac263cda8641ac852f21177a5b45..e7bd4ccd3c1cbea5de69b9cd17a50cc2df8556f0 100644 (file)
@@ -84,9 +84,6 @@ extern "C" {
 # 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>
index 28ca90116ee2c41d10143092d300920f4f35d941..427d05cdb154280cb4f474e88b93eaa915e7f78f 100644 (file)
       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 (file)
index 87b5cfa..0000000
+++ /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 <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);
diff --git a/src/node_child_process.h b/src/node_child_process.h
deleted file mode 100644 (file)
index b815a3f..0000000
+++ /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 <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_
index 3b0731605e0dff69a436c092d2b2320c36187c23..f4ff7a8dc0c4c19cdbe820075cc91fcdd2c4d6d4 100644 (file)
@@ -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 e1ef0d0181797ce7b45de9bf8eb11d12f8c51d19..2f1b959c990a3843732fead8ade80729a71ff391 100644 (file)
--- 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: