child_process_uv: add exec, fix simple/test-child-process-exec-cwd
authorRyan Dahl <ry@tinyclouds.org>
Sun, 31 Jul 2011 23:24:29 +0000 (16:24 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Sun, 31 Jul 2011 23:24:29 +0000 (16:24 -0700)
Makefile
lib/child_process_uv.js
src/process_wrap.cc
test/simple/test-child-process-exec-env.js

index 5dbcdd1..5d7248a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -234,8 +234,9 @@ UVTEST += simple/test-tls-request-timeout
 UVTEST += simple/test-tls-set-encoding
 
 # child_process
-UVTEST += simple/test-child-process-exit-code.js
-UVTEST += simple/test-child-process-buffering.js
+UVTEST += simple/test-child-process-exit-code
+UVTEST += simple/test-child-process-buffering
+UVTEST += simple/test-child-process-exec-cwd
 
 
 test-uv: all
index 342578c..6352c93 100644 (file)
@@ -47,6 +47,125 @@ function createSocket(pipe, readable) {
 }
 
 
+exports.exec = function(command /*, options, callback */) {
+  var rest = Array.prototype.slice.call(arguments, 1);
+  var args = ['/bin/sh', ['-c', command]].concat(rest);
+  return exports.execFile.apply(this, args);
+};
+
+
+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
+  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;
+};
+
+
 var spawn = exports.spawn = function(file, args, options) {
   var child = new ChildProcess();
 
index 6d9a230..9c56b01 100644 (file)
@@ -106,7 +106,7 @@ class ProcessWrap : public HandleWrap {
     // options.cwd
     Local<Value> cwd_v = js_options->Get(String::New("cwd"));
     if (!cwd_v.IsEmpty() && cwd_v->IsString()) {
-      String::Utf8Value cwd(js_options->ToString());
+      String::Utf8Value cwd(cwd_v->ToString());
       options.cwd = strdup(*cwd);
     }
 
index 64ec327..8ab8de4 100644 (file)
@@ -26,27 +26,28 @@ var success_count = 0;
 var error_count = 0;
 var response = '';
 
-var child = exec('/usr/bin/env', {env: {'HELLO': 'WORLD'}},
-                 function(err, stdout, stderr) {
-                   if (err) {
-                     error_count++;
-                     console.log('error!: ' + err.code);
-                     console.log('stdout: ' + JSON.stringify(stdout));
-                     console.log('stderr: ' + JSON.stringify(stderr));
-                     assert.equal(false, err.killed);
-                   } else {
-                     success_count++;
-                     assert.equal(true, stdout != '');
-                   }
-                 });
+function after(err, stdout, stderr) {
+  if (err) {
+    error_count++;
+    console.log('error!: ' + err.code);
+    console.log('stdout: ' + JSON.stringify(stdout));
+    console.log('stderr: ' + JSON.stringify(stderr));
+    assert.equal(false, err.killed);
+  } else {
+    success_count++;
+    assert.equal(true, stdout != '');
+  }
+}
 
-child.stdout.setEncoding('utf8');
+var child = exec('/usr/bin/env', { env: { 'HELLO': 'WORLD' } }, after);
 
+child.stdout.setEncoding('utf8');
 child.stdout.addListener('data', function(chunk) {
   response += chunk;
 });
 
 process.addListener('exit', function() {
+  console.log("response: ", response);
   assert.equal(1, success_count);
   assert.equal(0, error_count);
   assert.ok(response.indexOf('HELLO=WORLD') >= 0);