0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-30 07:27:22 +01:00
nodejs/lib/child_process.js
Felix Geisendörfer f8a3cf980f Properly handle child process exit codes
The child process 'exit' was returning the status of the process, rather than
the exit code. This patch properly deconstructs the status into the exit code
and the term signal a process may have received.

See:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Watcher_Specific_Functions_and_Data_-5
and waitpid(2)
2010-04-28 13:54:17 -07:00

155 lines
3.9 KiB
JavaScript

var inherits = require('sys').inherits;
var EventEmitter = require('events').EventEmitter;
var Stream = require('net').Stream;
var InternalChildProcess = process.binding('child_process').ChildProcess;
var spawn = exports.spawn = function (path, args, env) {
var child = new ChildProcess();
child.spawn(path, args, env);
return child;
};
exports.exec = function (command /*, options, callback */) {
if (arguments.length < 3) {
return exports.execFile("/bin/sh", ["-c", command], arguments[1]);
} else {
return exports.execFile("/bin/sh", ["-c", command], arguments[1], arguments[2]);
}
};
exports.execFile = function (file, args /*, options, callback */) {
var options = { encoding: 'utf8'
, timeout: 0
, maxBuffer: 200*1024
, killSignal: 'SIGKILL'
};
var callback = arguments[arguments.length-1];
if (typeof arguments[2] == 'object') {
var keys = Object.keys(options);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
if (arguments[2][k] !== undefined) options[k] = arguments[2][k];
}
}
var child = spawn(file, args);
var stdout = "";
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.addListener("data", function (chunk) {
stdout += chunk;
if (!killed && stdout.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true;
}
});
child.stderr.addListener("data", function (chunk) {
stderr += chunk;
if (!killed && stderr.length > options.maxBuffer) {
child.kill(options.killSignal);
killed = true
}
});
child.addListener("exit", function (code, signal) {
if (timeoutId) clearTimeout(timeoutId);
if (code === 0 && signal === null) {
if (callback) callback(null, stdout, stderr);
} else {
var e = new Error("Command failed: " + stderr);
e.killed = killed;
e.code = code;
e.signal = signal;
if (callback) callback(e, stdout, stderr);
}
});
};
function ChildProcess () {
process.EventEmitter.call(this);
var self = this;
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();
stderr.onend = stdout.onend = function () {
if (gotCHLD && !stdout.readable && !stderr.readable) {
self.emit('exit', exitCode, termSignal);
}
};
internal.onexit = function (code, signal) {
gotCHLD = true;
exitCode = code;
termSignal = signal;
stdin.end();
if (!stdout.readable && !stderr.readable) {
self.emit('exit', exitCode, termSignal);
}
};
this.__defineGetter__('pid', function () { return internal.pid; });
}
inherits(ChildProcess, EventEmitter);
ChildProcess.prototype.kill = function (sig) {
return this._internal.kill(sig);
};
ChildProcess.prototype.spawn = function (path, args, env) {
args = args || [];
env = env || process.env;
var envPairs = [];
var keys = Object.keys(env);
for (var index = 0, keysLength = keys.length; index < keysLength; index++) {
var key = keys[index];
envPairs.push(key + "=" + env[key]);
}
var fds = this._internal.spawn(path, args, envPairs);
this.stdin.open(fds[0]);
this.stdin.writable = true;
this.stdin.readable = false;
this.stdout.open(fds[1]);
this.stdout.writable = false;
this.stdout.readable = true;
this.stdout.resume();
this.stderr.open(fds[2]);
this.stderr.writable = false;
this.stderr.readable = true;
this.stderr.resume();
};