Skip to content

Commit 04c06b9

Browse files
committed
child process now use net.Socket
1 parent 953fa3a commit 04c06b9

24 files changed

+518
-520
lines changed

doc/api.txt

+30-38
Original file line numberDiff line numberDiff line change
@@ -192,23 +192,6 @@ The default is to only recurse twice. To make it recurse indefinitely, pass
192192
in +null+ for +depth+.
193193

194194

195-
+exec(command, callback)+::
196-
Executes the command as a child process, buffers the output and returns it
197-
in a callback.
198-
+
199-
----------------------------------------
200-
var sys = require("sys");
201-
sys.exec("ls /", function (err, stdout, stderr) {
202-
if (err) throw err;
203-
sys.puts(stdout);
204-
});
205-
----------------------------------------
206-
+
207-
The callback gets the arguments +(err, stdout, stderr)+. On success +err+
208-
will be +null+. On error +err+ will be an instance of +Error+ and +err.code+
209-
will be the exit code of the child process.
210-
211-
212195
== Events
213196

214197
Many objects in Node emit events: a TCP server emits an event each time
@@ -399,45 +382,37 @@ Stops a interval from triggering.
399382
== Child Processes
400383

401384
Node provides a tridirectional +popen(3)+ facility through the class
402-
+process.ChildProcess+. It is possible to stream data through the child's +stdin+,
403-
+stdout+, and +stderr+ in a fully non-blocking way.
385+
+ChildProcess+ class. It is possible to stream data through the child's
386+
+stdin+, +stdout+, and +stderr+ in a fully non-blocking way.
387+
388+
To create a child process use +require("child_process").spawn()+.
389+
390+
Child processes always have three streams associated with them.
391+
+child.stdin+, +child.stdout+, and +child.stderr+.
404392

405-
=== +process.ChildProcess+
406393

407394
[cols="1,2,10",options="header"]
408395
|=========================================================
409396
| Event | Parameters |Notes
410-
411-
| +"output"+ | +data+ | Each time the child process
412-
sends data to its +stdout+, this event is
413-
emitted. +data+ is a string. If the child
414-
process closes its +stdout+ stream (a common
415-
thing to do on exit), this event will be emitted
416-
with +data === null+.
417-
418-
| +"error"+ | +data+ | Identical to the +"output"+ event except for
419-
+stderr+ instead of +stdout+.
420-
421397
| +"exit"+ | +code+ | This event is emitted after the child process
422398
ends. +code+ is the final exit code of the
423399
process. One can be assured that after this
424400
event is emitted that the +"output"+ and
425401
+"error"+ callbacks will no longer be made.
426402
|=========================================================
427403

428-
+process.createChildProcess(command, args=[], env=process.env)+::
404+
+require("child_process").spawn(command, args=[], env=process.env)+::
429405
Launches a new process with the given +command+, command line arguments, and
430406
environmental variables. For example:
431407
+
432408
----------------------------------------
433-
var ls = process.createChildProcess("ls", ["-lh", "/usr"]);
434-
ls.addListener("output", function (data) {
435-
sys.puts(data);
409+
// Pipe a child process output to
410+
// parent process output
411+
var ls = spawn("ls", ["-lh", "/usr"]);
412+
ls.stdout.addListener("data", function (data) {
413+
process.stdout.write(data);
436414
});
437415
----------------------------------------
438-
+
439-
Note, if you just want to buffer the output of a command and return it, then
440-
+exec()+ in +/sys.js+ might be better.
441416

442417

443418
+child.pid+ ::
@@ -459,6 +434,23 @@ Send a signal to the child process. If no argument is given, the process
459434
will be sent +"SIGTERM"+. See signal(7) for a list of available signals.
460435

461436

437+
+require("child_process").exec(command, callback)+::
438+
High-level way to executes a command as a child process and buffer the
439+
output and return it in a callback.
440+
+
441+
----------------------------------------
442+
var exec = require("child_process").exec;
443+
exec("ls /", function (err, stdout, stderr) {
444+
if (err) throw err;
445+
sys.puts(stdout);
446+
});
447+
----------------------------------------
448+
+
449+
The callback gets the arguments +(err, stdout, stderr)+. On success +err+
450+
will be +null+. On error +err+ will be an instance of +Error+ and +err.code+
451+
will be the exit code of the child process.
452+
453+
462454

463455
== File System
464456

lib/child_process.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
var inherits = require('sys').inherits;
2+
var EventEmitter = require('events').EventEmitter;
3+
var Socket = require('net').Socket;
4+
var InternalChildProcess = process.binding('child_process').ChildProcess;
5+
6+
7+
var spawn = exports.spawn = function (path, args, env) {
8+
var child = new ChildProcess();
9+
child.spawn(path, args, env);
10+
return child;
11+
};
12+
13+
14+
exports.exec = function (command, callback) {
15+
var child = spawn("/bin/sh", ["-c", command]);
16+
var stdout = "";
17+
var stderr = "";
18+
19+
child.stdout.setEncoding('utf8');
20+
child.stdout.addListener("data", function (chunk) { stdout += chunk; });
21+
22+
child.stderr.setEncoding('utf8');
23+
child.stderr.addListener("data", function (chunk) { stderr += chunk; });
24+
25+
child.addListener("exit", function (code) {
26+
if (code == 0) {
27+
if (callback) callback(null, stdout, stderr);
28+
} else {
29+
var e = new Error("Command failed: " + stderr);
30+
e.code = code;
31+
if (callback) callback(e, stdout, stderr);
32+
}
33+
});
34+
};
35+
36+
37+
function ChildProcess () {
38+
process.EventEmitter.call(this);
39+
40+
var self = this;
41+
42+
var gotCHLD = false;
43+
var exitCode;
44+
var internal = this._internal = new InternalChildProcess();
45+
46+
var stdin = this.stdin = new Socket();
47+
var stdout = this.stdout = new Socket();
48+
var stderr = this.stderr = new Socket();
49+
50+
stderr.onend = stdout.onend = function () {
51+
if (gotCHLD && !stdout.readable && !stderr.readable) {
52+
self.emit('exit', exitCode);
53+
}
54+
};
55+
56+
internal.onexit = function (code) {
57+
gotCHLD = true;
58+
exitCode = code;
59+
if (!stdout.readable && !stderr.readable) {
60+
self.emit('exit', exitCode);
61+
}
62+
};
63+
64+
this.__defineGetter__('pid', function () { return internal.pid; });
65+
}
66+
inherits(ChildProcess, EventEmitter);
67+
68+
69+
ChildProcess.prototype.kill = function (sig) {
70+
return this._internal.kill(sig);
71+
};
72+
73+
74+
ChildProcess.prototype.spawn = function (path, args, env) {
75+
args = args || [];
76+
env = env || process.env;
77+
var envPairs = [];
78+
for (var key in env) {
79+
if (env.hasOwnProperty(key)) {
80+
envPairs.push(key + "=" + env[key]);
81+
}
82+
}
83+
84+
var fds = this._internal.spawn(path, args, envPairs);
85+
86+
this.stdin.open(fds[0]);
87+
this.stdin.writable = true;
88+
this.stdin.readable = false;
89+
90+
this.stdout.open(fds[1]);
91+
this.stdout.writable = false;
92+
this.stdout.readable = true;
93+
this.stdout.resume();
94+
95+
this.stderr.open(fds[2]);
96+
this.stderr.writable = false;
97+
this.stderr.readable = true;
98+
this.stderr.resume();
99+
};
100+

lib/net.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -367,20 +367,25 @@ function Socket (fd) {
367367
this.fd = null;
368368

369369
if (parseInt(fd) >= 0) {
370-
initSocket(this);
371-
372-
this.fd = fd;
373-
374-
this.readable = true;
375-
376-
this._writeWatcher.set(this.fd, false, true);
377-
this.writable = true;
370+
this.open(fd);
378371
}
379372
};
380373
sys.inherits(Socket, events.EventEmitter);
381374
exports.Socket = Socket;
382375

383376

377+
Socket.prototype.open = function (fd) {
378+
initSocket(this);
379+
380+
this.fd = fd;
381+
382+
this.readable = true;
383+
384+
this._writeWatcher.set(this.fd, false, true);
385+
this.writable = true;
386+
}
387+
388+
384389
exports.createConnection = function (port, host) {
385390
var s = new Socket();
386391
s.connect(port, host);
@@ -716,6 +721,7 @@ Socket.prototype.forceClose = function (exception) {
716721

717722
timeout.unenroll(this);
718723

724+
// FIXME Bug when this.fd == 0
719725
if (this.fd) {
720726
close(this.fd);
721727
debug('close ' + this.fd);

lib/sys.js

+10-25
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ exports.debug = function (x) {
1616
process.binding('stdio').writeError("DEBUG: " + x + "\n");
1717
};
1818

19-
exports.error = function (x) {
19+
var error = exports.error = function (x) {
2020
for (var i = 0, len = arguments.length; i < len; ++i) {
2121
process.binding('stdio').writeError(arguments[i] + '\n');
2222
}
@@ -184,7 +184,7 @@ exports.inspect = function (obj, showHidden, depth) {
184184

185185
exports.p = function () {
186186
for (var i = 0, len = arguments.length; i < len; ++i) {
187-
exports.error(exports.inspect(arguments[i]));
187+
error(exports.inspect(arguments[i]));
188188
}
189189
};
190190

@@ -207,29 +207,14 @@ exports.log = function (msg) {
207207
exports.puts(timestamp() + ' - ' + msg.toString());
208208
}
209209

210-
exports.exec = function (command, callback) {
211-
var child = process.createChildProcess("/bin/sh", ["-c", command]);
212-
var stdout = "";
213-
var stderr = "";
214-
215-
child.addListener("output", function (chunk) {
216-
if (chunk) stdout += chunk;
217-
});
218-
219-
child.addListener("error", function (chunk) {
220-
if (chunk) stderr += chunk;
221-
});
222-
223-
child.addListener("exit", function (code) {
224-
if (code == 0) {
225-
if (callback) callback(null, stdout, stderr);
226-
} else {
227-
var e = new Error("Command failed: " + stderr);
228-
e.code = code;
229-
if (callback) callback(e, stdout, stderr);
230-
}
231-
});
232-
};
210+
var execWarning;
211+
exports.exec = function () {
212+
if (!execWarning) {
213+
execWarning = 'sys.exec has moved to the "child_process" module. Please update your source code.'
214+
error(execWarning);
215+
}
216+
return require('child_process').exec.apply(this, arguments);
217+
}
233218

234219
/**
235220
* Inherit the prototype methods from one constructor into another.

src/node.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,8 @@ static Handle<Value> Binding(const Arguments& args) {
10731073

10741074
Local<Object> exports;
10751075

1076+
// TODO DRY THIS UP!
1077+
10761078
if (!strcmp(*module_v, "stdio")) {
10771079
if (binding_cache->Has(module)) {
10781080
exports = binding_cache->Get(module)->ToObject();
@@ -1157,6 +1159,15 @@ static Handle<Value> Binding(const Arguments& args) {
11571159
binding_cache->Set(module, exports);
11581160
}
11591161

1162+
} else if (!strcmp(*module_v, "child_process")) {
1163+
if (binding_cache->Has(module)) {
1164+
exports = binding_cache->Get(module)->ToObject();
1165+
} else {
1166+
exports = Object::New();
1167+
ChildProcess::Initialize(exports);
1168+
binding_cache->Set(module, exports);
1169+
}
1170+
11601171
} else if (!strcmp(*module_v, "natives")) {
11611172
if (binding_cache->Has(module)) {
11621173
exports = binding_cache->Get(module)->ToObject();
@@ -1165,6 +1176,7 @@ static Handle<Value> Binding(const Arguments& args) {
11651176
// Explicitly define native sources.
11661177
// TODO DRY/automate this?
11671178
exports->Set(String::New("assert"), String::New(native_assert));
1179+
exports->Set(String::New("child_process"),String::New(native_child_process));
11681180
exports->Set(String::New("dns"), String::New(native_dns));
11691181
exports->Set(String::New("events"), String::New(native_events));
11701182
exports->Set(String::New("file"), String::New(native_file));
@@ -1285,7 +1297,6 @@ static void Load(int argc, char *argv[]) {
12851297
IOWatcher::Initialize(process); // io_watcher.cc
12861298
IdleWatcher::Initialize(process); // idle_watcher.cc
12871299
Timer::Initialize(process); // timer.cc
1288-
ChildProcess::Initialize(process); // child_process.cc
12891300
DefineConstants(process); // constants.cc
12901301

12911302
// Compile, execute the src/node.js file. (Which was included as static C

src/node.js

+1-19
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ process.unwatchFile = removed("process.unwatchFile() has moved to fs.unwatchFile
2525
GLOBAL.node = {};
2626

2727
node.createProcess = removed("node.createProcess() has been changed to process.createChildProcess() update your code");
28+
process.createChildProcess = removed("childProcess API has changed. See doc/api.txt.");
2829
node.exec = removed("process.exec() has moved. Use require('sys') to bring it back.");
2930
node.inherits = removed("node.inherits() has moved. Use require('sys') to access it.");
3031
process.inherits = removed("process.inherits() has moved to sys.inherits.");
@@ -89,24 +90,6 @@ function requireNative (id) {
8990
}
9091

9192

92-
process.createChildProcess = function (file, args, env) {
93-
var child = new process.ChildProcess();
94-
args = args || [];
95-
env = env || process.env;
96-
var envPairs = [];
97-
for (var key in env) {
98-
if (env.hasOwnProperty(key)) {
99-
envPairs.push(key + "=" + env[key]);
100-
}
101-
}
102-
// TODO Note envPairs is not currently used in child_process.cc. The PATH
103-
// needs to be searched for the 'file' command if 'file' does not contain
104-
// a '/' character.
105-
child.spawn(file, args, envPairs);
106-
return child;
107-
};
108-
109-
11093
process.assert = function (x, msg) {
11194
if (!(x)) throw new Error(msg || "assertion error");
11295
};
@@ -797,7 +780,6 @@ process.openStdin = function () {
797780
var net = requireNative('net');
798781
var fd = process.binding('stdio').openStdin();
799782
stdin = new net.Socket(fd);
800-
process.stdout.write(stdin.fd + "\n");
801783
stdin.resume();
802784
stdin.readable = true;
803785
return stdin;

0 commit comments

Comments
 (0)