Skip to content
Newer
Older
100644 575 lines (459 sloc) 15 KB
55048cd @ry Update copyright headers
ry authored
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
a5df0f6 @ry Prefix all source files with node_
ry authored
22 #include <node_child_process.h>
f8a3cf9 @felixge Properly handle child process exit codes
felixge authored
23 #include <node.h>
83cb156 @ry skelton of node.Process
ry authored
24
25 #include <assert.h>
8e5b91c @ry Revert "Check for strings.h"
ry authored
26 #include <string.h>
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
27 #include <stdlib.h>
28 #include <errno.h>
83cb156 @ry skelton of node.Process
ry authored
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
32 #include <pwd.h> /* getpwnam() */
33 #include <grp.h> /* getgrnam() */
ab5c0dd @mscdex Port to OpenBSD
mscdex authored
34 #if defined(__FreeBSD__ ) || defined(__OpenBSD__)
5d9753f @herby Fix child_process.cc build failure for FreeBSD.
herby authored
35 #include <sys/wait.h>
36 #endif
83cb156 @ry skelton of node.Process
ry authored
37
9e26dab @ry child_process.spawnNode
ry authored
38 #include <sys/socket.h> /* socketpair */
39 #include <sys/un.h>
40
50443f0 @rsms environ symbol fix for Mac OS X
rsms authored
41 # ifdef __APPLE__
42 # include <crt_externs.h>
43 # define environ (*_NSGetEnviron())
44 # else
769a350 @ry Allow passing env to child process
ry authored
45 extern char **environ;
50443f0 @rsms environ symbol fix for Mac OS X
rsms authored
46 # endif
769a350 @ry Allow passing env to child process
ry authored
47
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
48 #include <limits.h> /* PATH_MAX */
49
227638b @ry Lint
ry authored
50 namespace node {
51
83cb156 @ry skelton of node.Process
ry authored
52 using namespace v8;
53
45a806a @ry Statically define symbols
ry authored
54 static Persistent<String> pid_symbol;
04c06b9 @ry child process now use net.Socket
ry authored
55 static Persistent<String> onexit_symbol;
56
57
58 // TODO share with other modules
59 static inline int SetNonBlocking(int fd) {
60 int flags = fcntl(fd, F_GETFL, 0);
61 int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
62 if (r != 0) {
63 perror("SetNonBlocking()");
64 }
65 return r;
66 }
67
45a806a @ry Statically define symbols
ry authored
68
07da49b @guitt Set FD_CLOEXEC flag on stdio FDs before spawning.
guitt authored
69 static inline int SetCloseOnExec(int fd) {
70 int flags = fcntl(fd, F_GETFD, 0);
71 int r = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
72 if (r != 0) {
73 perror("SetCloseOnExec()");
74 }
75 return r;
76 }
77
78
f4f05a8 @ry Unset CLOEXEC on spawn's customFds
ry authored
79 static inline int ResetFlags(int fd) {
e9aacd4 @isaacs When the parent's stdio FDs are passed to a child, make them temporar…
isaacs authored
80 int flags = fcntl(fd, F_GETFL, 0);
f4f05a8 @ry Unset CLOEXEC on spawn's customFds
ry authored
81 // blocking
1a12148 @isaacs SetBlocking should set blocking, not toggle it
isaacs authored
82 int r = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
f4f05a8 @ry Unset CLOEXEC on spawn's customFds
ry authored
83 flags = fcntl(fd, F_GETFD, 0);
84 // unset the CLOEXEC
85 fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC);
e9aacd4 @isaacs When the parent's stdio FDs are passed to a child, make them temporar…
isaacs authored
86 return r;
87 }
88
89
227638b @ry Lint
ry authored
90 void ChildProcess::Initialize(Handle<Object> target) {
83cb156 @ry skelton of node.Process
ry authored
91 HandleScope scope;
92
ad9d683 @ry API: rename node.Process to node.ChildProcess
ry authored
93 Local<FunctionTemplate> t = FunctionTemplate::New(ChildProcess::New);
04c06b9 @ry child process now use net.Socket
ry authored
94 t->InstanceTemplate()->SetInternalFieldCount(1);
95 t->SetClassName(String::NewSymbol("ChildProcess"));
83cb156 @ry skelton of node.Process
ry authored
96
45a806a @ry Statically define symbols
ry authored
97 pid_symbol = NODE_PSYMBOL("pid");
04c06b9 @ry child process now use net.Socket
ry authored
98 onexit_symbol = NODE_PSYMBOL("onexit");
45a806a @ry Statically define symbols
ry authored
99
04c06b9 @ry child process now use net.Socket
ry authored
100 NODE_SET_PROTOTYPE_METHOD(t, "spawn", ChildProcess::Spawn);
101 NODE_SET_PROTOTYPE_METHOD(t, "kill", ChildProcess::Kill);
83cb156 @ry skelton of node.Process
ry authored
102
04c06b9 @ry child process now use net.Socket
ry authored
103 target->Set(String::NewSymbol("ChildProcess"), t->GetFunction());
83cb156 @ry skelton of node.Process
ry authored
104 }
105
04c06b9 @ry child process now use net.Socket
ry authored
106
227638b @ry Lint
ry authored
107 Handle<Value> ChildProcess::New(const Arguments& args) {
83cb156 @ry skelton of node.Process
ry authored
108 HandleScope scope;
ad9d683 @ry API: rename node.Process to node.ChildProcess
ry authored
109 ChildProcess *p = new ChildProcess();
1fc4dce @ry Simplify and cleanup ObjectWrap.
ry authored
110 p->Wrap(args.Holder());
d56552d @ry Remove node.Process constructor from API
ry authored
111 return args.This();
112 }
113
04c06b9 @ry child process now use net.Socket
ry authored
114
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
115 // This is an internal function. The third argument should be an array
116 // of key value pairs seperated with '='.
227638b @ry Lint
ry authored
117 Handle<Value> ChildProcess::Spawn(const Arguments& args) {
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
118 HandleScope scope;
119
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
120 if (args.Length() < 3 ||
04c06b9 @ry child process now use net.Socket
ry authored
121 !args[0]->IsString() ||
122 !args[1]->IsArray() ||
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
123 !args[2]->IsString() ||
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
124 !args[3]->IsArray() ||
125 !args[4]->IsArray() ||
126 !args[5]->IsBoolean() ||
127 !(args[6]->IsInt32() || args[6]->IsString()) ||
128 !(args[7]->IsInt32() || args[7]->IsString())) {
393caeb @ry Add Exception::Error where missing.
ry authored
129 return ThrowException(Exception::Error(String::New("Bad argument.")));
d56552d @ry Remove node.Process constructor from API
ry authored
130 }
131
ad9d683 @ry API: rename node.Process to node.ChildProcess
ry authored
132 ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
d56552d @ry Remove node.Process constructor from API
ry authored
133
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
134 String::Utf8Value file(args[0]->ToString());
135
136 int i;
137
138 // Copy second argument args[1] into a c-string array called argv.
139 // The array must be null terminated, and the first element must be
140 // the name of the executable -- hence the complication.
141 Local<Array> argv_handle = Local<Array>::Cast(args[1]);
142 int argc = argv_handle->Length();
143 int argv_length = argc + 1 + 1;
144 char **argv = new char*[argv_length]; // heap allocated to detect errors
145 argv[0] = strdup(*file); // + 1 for file
146 argv[argv_length-1] = NULL; // + 1 for NULL;
147 for (i = 0; i < argc; i++) {
148 String::Utf8Value arg(argv_handle->Get(Integer::New(i))->ToString());
149 argv[i+1] = strdup(*arg);
150 }
151
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
152 // Copy third argument, args[2], into a c-string called cwd.
153 String::Utf8Value arg(args[2]->ToString());
154 char *cwd = strdup(*arg);
155
156 // Copy fourth argument, args[3], into a c-string array called env.
157 Local<Array> env_handle = Local<Array>::Cast(args[3]);
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
158 int envc = env_handle->Length();
9e26dab @ry child_process.spawnNode
ry authored
159 char **env = new char*[envc + 1]; // heap allocated to detect errors
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
160 env[envc] = NULL;
161 for (int i = 0; i < envc; i++) {
162 String::Utf8Value pair(env_handle->Get(Integer::New(i))->ToString());
163 env[i] = strdup(*pair);
164 }
165
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
166 int custom_fds[3] = { -1, -1, -1 };
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
167 if (args[4]->IsArray()) {
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
168 // Set the custom file descriptor values (if any) for the child process
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
169 Local<Array> custom_fds_handle = Local<Array>::Cast(args[4]);
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
170 int custom_fds_len = custom_fds_handle->Length();
171 for (int i = 0; i < custom_fds_len; i++) {
172 if (custom_fds_handle->Get(i)->IsUndefined()) continue;
173 Local<Integer> fd = custom_fds_handle->Get(i)->ToInteger();
174 custom_fds[i] = fd->Value();
175 }
176 }
177
63bd237 @ry typo setuid -> setsid
ry authored
178 int do_setsid = false;
202dd83 @ry Add setsid option to child_process
ry authored
179 if (args[5]->IsBoolean()) {
63bd237 @ry typo setuid -> setsid
ry authored
180 do_setsid = args[5]->BooleanValue();
202dd83 @ry Add setsid option to child_process
ry authored
181 }
182
183
04c06b9 @ry child process now use net.Socket
ry authored
184 int fds[3];
185
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
186 char *custom_uname = NULL;
187 int custom_uid = -1;
188 if (args[6]->IsNumber()) {
189 custom_uid = args[6]->Int32Value();
190 } else if (args[6]->IsString()) {
191 String::Utf8Value pwnam(args[6]->ToString());
192 custom_uname = (char *)calloc(sizeof(char), pwnam.length() + 1);
193 strncpy(custom_uname, *pwnam, pwnam.length() + 1);
194 } else {
195 return ThrowException(Exception::Error(
196 String::New("setuid argument must be a number or a string")));
197 }
198
199 char *custom_gname = NULL;
200 int custom_gid = -1;
201 if (args[7]->IsNumber()) {
202 custom_gid = args[7]->Int32Value();
203 } else if (args[7]->IsString()) {
204 String::Utf8Value grnam(args[7]->ToString());
205 custom_gname = (char *)calloc(sizeof(char), grnam.length() + 1);
206 strncpy(custom_gname, *grnam, grnam.length() + 1);
207 } else {
208 return ThrowException(Exception::Error(
209 String::New("setgid argument must be a number or a string")));
210 }
211
9e26dab @ry child_process.spawnNode
ry authored
212 int channel_fd = -1;
6f5d95d @isaacs child_process: Add gid/uid flags to spawn config
isaacs authored
213
214 int r = child->Spawn(argv[0],
215 argv,
216 cwd,
217 env,
218 fds,
219 custom_fds,
220 do_setsid,
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
221 custom_uid,
222 custom_uname,
223 custom_gid,
9e26dab @ry child_process.spawnNode
ry authored
224 custom_gname,
225 &channel_fd);
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
226
227 if (custom_uname != NULL) free(custom_uname);
228 if (custom_gname != NULL) free(custom_gname);
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
229
230 for (i = 0; i < argv_length; i++) free(argv[i]);
231 delete [] argv;
232
7fcfb7b @thughes Fix memleak in ChildProcess:Spawn().
thughes authored
233 free(cwd);
234
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
235 for (i = 0; i < envc; i++) free(env[i]);
236 delete [] env;
d56552d @ry Remove node.Process constructor from API
ry authored
237
83cb156 @ry skelton of node.Process
ry authored
238 if (r != 0) {
393caeb @ry Add Exception::Error where missing.
ry authored
239 return ThrowException(Exception::Error(String::New("Error spawning")));
83cb156 @ry skelton of node.Process
ry authored
240 }
241
9e26dab @ry child_process.spawnNode
ry authored
242
243 Local<Array> a = Array::New(channel_fd >= 0 ? 4 : 3);
8658999 @ry Refactor node.Process to take advantage of evcom_reader/writer.
ry authored
244
04c06b9 @ry child process now use net.Socket
ry authored
245 assert(fds[0] >= 0);
246 a->Set(0, Integer::New(fds[0])); // stdin
247 assert(fds[1] >= 0);
248 a->Set(1, Integer::New(fds[1])); // stdout
249 assert(fds[2] >= 0);
250 a->Set(2, Integer::New(fds[2])); // stderr
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
251
9e26dab @ry child_process.spawnNode
ry authored
252 if (channel_fd >= 0) {
253 a->Set(3, Integer::New(channel_fd));
254 }
255
04c06b9 @ry child process now use net.Socket
ry authored
256 return scope.Close(a);
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
257 }
258
04c06b9 @ry child process now use net.Socket
ry authored
259
227638b @ry Lint
ry authored
260 Handle<Value> ChildProcess::Kill(const Arguments& args) {
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
261 HandleScope scope;
ad9d683 @ry API: rename node.Process to node.ChildProcess
ry authored
262 ChildProcess *child = ObjectWrap::Unwrap<ChildProcess>(args.Holder());
263 assert(child);
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
264
bfdc421 @ry Don't kill negative PIDs
ry authored
265 if (child->pid_ < 1) {
6ad6298 @piscisaureus Make child_process.kill always work on windows
piscisaureus authored
266 // nothing to do
267 return False();
bfdc421 @ry Don't kill negative PIDs
ry authored
268 }
269
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
270 int sig = SIGTERM;
3456a16 @ry Accept string representations of signals in node.kill and child.kill
ry authored
271
272 if (args.Length() > 0) {
273 if (args[0]->IsNumber()) {
274 sig = args[0]->Int32Value();
6eca948 @ry Move constants out of process object
ry authored
275 } else {
6ad6298 @piscisaureus Make child_process.kill always work on windows
piscisaureus authored
276 return ThrowException(Exception::TypeError(String::New("Bad argument.")));
3456a16 @ry Accept string representations of signals in node.kill and child.kill
ry authored
277 }
278 }
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
279
ad9d683 @ry API: rename node.Process to node.ChildProcess
ry authored
280 if (child->Kill(sig) != 0) {
6ad6298 @piscisaureus Make child_process.kill always work on windows
piscisaureus authored
281 return ThrowException(ErrnoException(errno, "Kill"));
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
282 }
283
6ad6298 @piscisaureus Make child_process.kill always work on windows
piscisaureus authored
284 return True();
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
285 }
286
83cb156 @ry skelton of node.Process
ry authored
287
04c06b9 @ry child process now use net.Socket
ry authored
288 void ChildProcess::Stop() {
289 if (ev_is_active(&child_watcher_)) {
290 ev_child_stop(EV_DEFAULT_UC_ &child_watcher_);
291 Unref();
2fd4958 @ry Add pid accessor
ry authored
292 }
04c06b9 @ry child process now use net.Socket
ry authored
293 // Don't kill the PID here. We want to allow for killing the parent
294 // process and reparenting to initd. This is perhaps not going the best
295 // technique for daemonizing, but I don't want to rule it out.
296 pid_ = -1;
83cb156 @ry skelton of node.Process
ry authored
297 }
298
299
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
300 // Note that args[0] must be the same as the "file" param. This is an
301 // execvp() requirement.
04c06b9 @ry child process now use net.Socket
ry authored
302 //
9e26dab @ry child_process.spawnNode
ry authored
303 // TODO: The arguments are rediculously long. Needs to be put into a struct.
304 //
04c06b9 @ry child process now use net.Socket
ry authored
305 int ChildProcess::Spawn(const char *file,
306 char *const args[],
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
307 const char *cwd,
04c06b9 @ry child process now use net.Socket
ry authored
308 char **env,
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
309 int stdio_fds[3],
202dd83 @ry Add setsid option to child_process
ry authored
310 int custom_fds[3],
6f5d95d @isaacs child_process: Add gid/uid flags to spawn config
isaacs authored
311 bool do_setsid,
312 int custom_uid,
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
313 char *custom_uname,
314 int custom_gid,
9e26dab @ry child_process.spawnNode
ry authored
315 char *custom_gname,
316 int* channel) {
04c06b9 @ry child process now use net.Socket
ry authored
317 HandleScope scope;
318 assert(pid_ == -1);
319 assert(!ev_is_active(&child_watcher_));
8658999 @ry Refactor node.Process to take advantage of evcom_reader/writer.
ry authored
320
04c06b9 @ry child process now use net.Socket
ry authored
321 int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
83cb156 @ry skelton of node.Process
ry authored
322
323 /* An implementation of popen(), basically */
80b5a52 @bnoordhuis Fix compiler warnings.
bnoordhuis authored
324 if ((custom_fds[0] == -1 && pipe(stdin_pipe) < 0) ||
325 (custom_fds[1] == -1 && pipe(stdout_pipe) < 0) ||
326 (custom_fds[2] == -1 && pipe(stderr_pipe) < 0)) {
83cb156 @ry skelton of node.Process
ry authored
327 perror("pipe()");
328 return -1;
329 }
330
07da49b @guitt Set FD_CLOEXEC flag on stdio FDs before spawning.
guitt authored
331 // Set the close-on-exec FD flag
332 if (custom_fds[0] == -1) {
333 SetCloseOnExec(stdin_pipe[0]);
334 SetCloseOnExec(stdin_pipe[1]);
335 }
336
337 if (custom_fds[1] == -1) {
338 SetCloseOnExec(stdout_pipe[0]);
339 SetCloseOnExec(stdout_pipe[1]);
340 }
341
342 if (custom_fds[2] == -1) {
343 SetCloseOnExec(stderr_pipe[0]);
344 SetCloseOnExec(stderr_pipe[1]);
345 }
346
9e26dab @ry child_process.spawnNode
ry authored
347
337c48d @ry Rename spawnNode to fork
ry authored
348 // The channel will be used by js-land "fork()" for a little JSON channel.
9e26dab @ry child_process.spawnNode
ry authored
349 // The pointer is used to pass one end of the socket pair back to the
350 // parent.
351 // channel_fds[0] is for the parent
352 // channel_fds[1] is for the child
353 int channel_fds[2] = { -1, -1 };
354
355 #define NODE_CHANNEL_FD "NODE_CHANNEL_FD"
356
357 for (int i = 0; env[i]; i++) {
358 if (!strncmp(env[i], NODE_CHANNEL_FD, sizeof NODE_CHANNEL_FD - 1)) {
359 if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel_fds)) {
360 perror("socketpair()");
361 return -1;
362 }
363
364 assert(channel_fds[0] >= 0 && channel_fds[1] >= 0);
365
366 SetNonBlocking(channel_fds[0]);
367 SetNonBlocking(channel_fds[1]);
368 // Write over the FILLMEIN :D
369 sprintf(env[i], NODE_CHANNEL_FD "=%d", channel_fds[1]);
370 }
371 }
372
d5ee777 @ry Don't allow child process to clobber environ
ry authored
373 // Save environ in the case that we get it clobbered
374 // by the child process.
375 char **save_our_env = environ;
376
9e26dab @ry child_process.spawnNode
ry authored
377 switch (pid_ = fork()) {
227638b @ry Lint
ry authored
378 case -1: // Error.
04c06b9 @ry child process now use net.Socket
ry authored
379 Stop();
83cb156 @ry skelton of node.Process
ry authored
380 return -4;
381
227638b @ry Lint
ry authored
382 case 0: // Child.
63bd237 @ry typo setuid -> setsid
ry authored
383 if (do_setsid && setsid() < 0) {
384 perror("setsid");
6f5d95d @isaacs child_process: Add gid/uid flags to spawn config
isaacs authored
385 _exit(127);
386 }
387
bc8e9b3 @isaacs Closes GH-734 Do the setuid() after chdir()
isaacs authored
388 if (custom_fds[0] == -1) {
389 close(stdin_pipe[1]); // close write end
390 dup2(stdin_pipe[0], STDIN_FILENO);
391 } else {
392 ResetFlags(custom_fds[0]);
393 dup2(custom_fds[0], STDIN_FILENO);
394 }
395
396 if (custom_fds[1] == -1) {
397 close(stdout_pipe[0]); // close read end
398 dup2(stdout_pipe[1], STDOUT_FILENO);
399 } else {
400 ResetFlags(custom_fds[1]);
401 dup2(custom_fds[1], STDOUT_FILENO);
402 }
403
404 if (custom_fds[2] == -1) {
405 close(stderr_pipe[0]); // close read end
406 dup2(stderr_pipe[1], STDERR_FILENO);
407 } else {
408 ResetFlags(custom_fds[2]);
409 dup2(custom_fds[2], STDERR_FILENO);
410 }
411
412 if (strlen(cwd) && chdir(cwd)) {
413 perror("chdir()");
414 _exit(127);
415 }
416
417
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
418 static char buf[PATH_MAX + 1];
419
420 int gid = -1;
421 if (custom_gid != -1) {
422 gid = custom_gid;
423 } else if (custom_gname != NULL) {
424 struct group grp, *grpp = NULL;
425 int err = getgrnam_r(custom_gname,
426 &grp,
427 buf,
428 PATH_MAX + 1,
429 &grpp);
430
431 if (err || grpp == NULL) {
432 perror("getgrnam_r()");
433 _exit(127);
434 }
435
436 gid = grpp->gr_gid;
437 }
438
439
440 int uid = -1;
441 if (custom_uid != -1) {
442 uid = custom_uid;
443 } else if (custom_uname != NULL) {
444 struct passwd pwd, *pwdp = NULL;
445 int err = getpwnam_r(custom_uname,
446 &pwd,
447 buf,
448 PATH_MAX + 1,
449 &pwdp);
450
451 if (err || pwdp == NULL) {
452 perror("getpwnam_r()");
453 _exit(127);
454 }
455
456 uid = pwdp->pw_uid;
457 }
458
459
460 if (gid != -1 && setgid(gid)) {
6f5d95d @isaacs child_process: Add gid/uid flags to spawn config
isaacs authored
461 perror("setgid()");
462 _exit(127);
463 }
464
435ece5 @isaacs child_process: Support setting uid/gid by name
isaacs authored
465 if (uid != -1 && setuid(uid)) {
6f5d95d @isaacs child_process: Add gid/uid flags to spawn config
isaacs authored
466 perror("setuid()");
467 _exit(127);
202dd83 @ry Add setsid option to child_process
ry authored
468 }
469
9e26dab @ry child_process.spawnNode
ry authored
470 // Close the parent's end of the channel.
471 if (channel_fds[0] >= 0) {
472 close(channel_fds[0]);
473 channel_fds[0] = -1;
474 }
9491413 @piscisaureus New api for child_process.spawn; ability to set cwd for spawn()ed pro…
piscisaureus authored
475
769a350 @ry Allow passing env to child process
ry authored
476 environ = env;
477
82465fc @ry Do not use /bin/sh to create child processes.
ry authored
478 execvp(file, args);
479 perror("execvp()");
83cb156 @ry skelton of node.Process
ry authored
480 _exit(127);
481 }
482
04c06b9 @ry child process now use net.Socket
ry authored
483 // Parent.
484
d5ee777 @ry Don't allow child process to clobber environ
ry authored
485 // Restore environment.
486 environ = save_our_env;
487
83cb156 @ry skelton of node.Process
ry authored
488 ev_child_set(&child_watcher_, pid_, 0);
489 ev_child_start(EV_DEFAULT_UC_ &child_watcher_);
04c06b9 @ry child process now use net.Socket
ry authored
490 Ref();
491 handle_->Set(pid_symbol, Integer::New(pid_));
492
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
493 if (custom_fds[0] == -1) {
494 close(stdin_pipe[0]);
495 stdio_fds[0] = stdin_pipe[1];
496 SetNonBlocking(stdin_pipe[1]);
5be6ab6 @ry Fix style
ry authored
497 } else {
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
498 stdio_fds[0] = custom_fds[0];
499 }
83cb156 @ry skelton of node.Process
ry authored
500
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
501 if (custom_fds[1] == -1) {
502 close(stdout_pipe[1]);
503 stdio_fds[1] = stdout_pipe[0];
504 SetNonBlocking(stdout_pipe[0]);
5be6ab6 @ry Fix style
ry authored
505 } else {
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
506 stdio_fds[1] = custom_fds[1];
507 }
83cb156 @ry skelton of node.Process
ry authored
508
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
509 if (custom_fds[2] == -1) {
510 close(stderr_pipe[1]);
511 stdio_fds[2] = stderr_pipe[0];
512 SetNonBlocking(stderr_pipe[0]);
5be6ab6 @ry Fix style
ry authored
513 } else {
92da636 @orlandov Add a parameter to spawn() that sets the child's stdio file descriptors.
orlandov authored
514 stdio_fds[2] = custom_fds[2];
515 }
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
516
9e26dab @ry child_process.spawnNode
ry authored
517 // Close the child's end of the channel.
518 if (channel_fds[1] >= 0) {
519 close(channel_fds[1]);
520 channel_fds[1] = -1;
521 assert(channel_fds[0] >= 0);
522 assert(channel);
523 *channel = channel_fds[0];
524 } else {
525 *channel = -1;
526 }
527
04c06b9 @ry child process now use net.Socket
ry authored
528 return 0;
529 }
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
530
531
f8a3cf9 @felixge Properly handle child process exit codes
felixge authored
532 void ChildProcess::OnExit(int status) {
04c06b9 @ry child process now use net.Socket
ry authored
533 HandleScope scope;
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
534
04c06b9 @ry child process now use net.Socket
ry authored
535 pid_ = -1;
536 Stop();
83cb156 @ry skelton of node.Process
ry authored
537
04c06b9 @ry child process now use net.Socket
ry authored
538 handle_->Set(pid_symbol, Null());
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
539
04c06b9 @ry child process now use net.Socket
ry authored
540 Local<Value> onexit_v = handle_->Get(onexit_symbol);
541 assert(onexit_v->IsFunction());
542 Local<Function> onexit = Local<Function>::Cast(onexit_v);
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
543
04c06b9 @ry child process now use net.Socket
ry authored
544 TryCatch try_catch;
a78ea51 @ry Add onExit callback
ry authored
545
f8a3cf9 @felixge Properly handle child process exit codes
felixge authored
546 Local<Value> argv[2];
547 if (WIFEXITED(status)) {
548 argv[0] = Integer::New(WEXITSTATUS(status));
549 } else {
550 argv[0] = Local<Value>::New(Null());
551 }
552
553 if (WIFSIGNALED(status)) {
554 argv[1] = String::NewSymbol(signo_string(WTERMSIG(status)));
555 } else {
556 argv[1] = Local<Value>::New(Null());
557 }
7363ccd @ry bugfix: Properly exit a process.
ry authored
558
f8a3cf9 @felixge Properly handle child process exit codes
felixge authored
559 onexit->Call(handle_, 2, argv);
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
560
04c06b9 @ry child process now use net.Socket
ry authored
561 if (try_catch.HasCaught()) {
562 FatalException(try_catch);
563 }
03c5772 @ry Get stdin/stdout working. Add process->Close().
ry authored
564 }
565
566
227638b @ry Lint
ry authored
567 int ChildProcess::Kill(int sig) {
765f0cd @ry Fix ChildProcess::Kill
ry authored
568 if (pid_ < 1) return -1;
e39923a @ry Add process.kill(sig = SIGTERM)
ry authored
569 return kill(pid_, sig);
570 }
7363ccd @ry bugfix: Properly exit a process.
ry authored
571
227638b @ry Lint
ry authored
572 } // namespace node
4f7f437 @pquerna Move child process to extension model.
pquerna authored
573
574 NODE_MODULE(node_child_process, node::ChildProcess::Initialize);
Something went wrong with that request. Please try again.