Browse Source

Add timeout and maxBuffer options to child_process.exec

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
9cf2a02d8b
  1. 16
      doc/api.markdown
  2. 52
      lib/child_process.js
  3. 11
      test/simple/test-exec.js

16
doc/api.markdown

@ -907,7 +907,7 @@ Example:
grep.stdin.end(); grep.stdin.end();
### child_process.exec(command, callback) ### child_process.exec(command, [options, ] callback)
High-level way to execute a command as a child process, buffer the High-level way to execute a command as a child process, buffer the
output, and return it all in a callback. output, and return it all in a callback.
@ -928,6 +928,20 @@ The callback gets the arguments `(error, stdout, stderr)`. On success, `error`
will be `null`. On error, `error` will be an instance of `Error` and `err.code` will be `null`. On error, `error` will be an instance of `Error` and `err.code`
will be the exit code of the child process. will be the exit code of the child process.
There is a second optional argument to specify several options. The default options are
{ encoding: 'utf8'
, timeout: 0
, maxBuffer: 200*1024
, killSignal: 'SIGKILL'
}
If `timeout` is greater than 0, then it will kill the child process
if it runs longer than `timeout` milliseconds. The child process is killed with
`killSignal` (default: `'SIGKILL'`). `maxBuffer` specifies the largest
amount of data allowed on stdout or stderr - if this value is exceeded then
the child process is killed.
## File System ## File System

52
lib/child_process.js

@ -11,22 +11,64 @@ var spawn = exports.spawn = function (path, args, env) {
}; };
exports.exec = function (command, callback) { exports.exec = function (command /* , options, callback */) {
var options = { encoding: 'utf8'
, timeout: 0
, maxBuffer: 200*1024
, killSignal: 'SIGKILL'
};
var callback = arguments[arguments.length-1];
if (typeof arguments[1] == 'object') {
var keys = Object.keys(options);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
if (arguments[1][k] !== undefined) options[k] = arguments[1][k];
}
}
var child = spawn("/bin/sh", ["-c", command]); var child = spawn("/bin/sh", ["-c", command]);
var stdout = ""; var stdout = "";
var stderr = ""; var stderr = "";
var killed = false;
var timeoutId;
if (options.timeout > 0) {
timeoutId = setTimeout(function () {
if (!killed) {
child.kill(options.killSignal);
killed = true;
timeoutId = null;
}
}, options.timeout);
}
child.stdout.setEncoding(options.encoding);
child.stderr.setEncoding(options.encoding);
child.stdout.setEncoding('utf8'); child.stdout.addListener("data", function (chunk) {
child.stdout.addListener("data", function (chunk) { stdout += chunk; }); stdout += chunk;
if (!killed && stdout.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true;
}
});
child.stderr.setEncoding('utf8'); child.stderr.addListener("data", function (chunk) {
child.stderr.addListener("data", function (chunk) { stderr += chunk; }); stderr += chunk;
if (!killed && stderr.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true
}
});
child.addListener("exit", function (code) { child.addListener("exit", function (code) {
if (timeoutId) clearTimeout(timeoutId);
if (code == 0) { if (code == 0) {
if (callback) callback(null, stdout, stderr); if (callback) callback(null, stdout, stderr);
} else { } else {
var e = new Error("Command failed: " + stderr); var e = new Error("Command failed: " + stderr);
e.killed = killed;
e.code = code; e.code = code;
if (callback) callback(e, stdout, stderr); if (callback) callback(e, stdout, stderr);
} }

11
test/simple/test-exec.js

@ -9,6 +9,7 @@ exec("ls /", function (err, stdout, stderr) {
puts("error!: " + err.code); puts("error!: " + err.code);
puts("stdout: " + JSON.stringify(stdout)); puts("stdout: " + JSON.stringify(stdout));
puts("stderr: " + JSON.stringify(stderr)); puts("stderr: " + JSON.stringify(stderr));
assert.equal(false, err.killed);
} else { } else {
success_count++; success_count++;
p(stdout); p(stdout);
@ -21,6 +22,7 @@ exec("ls /DOES_NOT_EXIST", function (err, stdout, stderr) {
error_count++; error_count++;
assert.equal("", stdout); assert.equal("", stdout);
assert.equal(true, err.code != 0); assert.equal(true, err.code != 0);
assert.equal(false, err.killed);
puts("error code: " + err.code); puts("error code: " + err.code);
puts("stdout: " + JSON.stringify(stdout)); puts("stdout: " + JSON.stringify(stdout));
puts("stderr: " + JSON.stringify(stderr)); puts("stderr: " + JSON.stringify(stderr));
@ -31,6 +33,15 @@ exec("ls /DOES_NOT_EXIST", function (err, stdout, stderr) {
} }
}); });
exec("sleep 10", { timeout: 50 }, function (err, stdout, stderr) {
assert.ok(err);
assert.ok(err.killed);
});
exec('python -c "print 200000*\'C\'"', { maxBuffer: 1000 }, function (err, stdout, stderr) {
assert.ok(err);
assert.ok(err.killed);
});
process.addListener("exit", function () { process.addListener("exit", function () {
assert.equal(1, success_count); assert.equal(1, success_count);

Loading…
Cancel
Save