blob: 9a20b06643540a477549bfc0e04d4e5b1307e2c7 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Steve Blocka7e24c12009-10-30 11:49:00 +00004
Steve Blocka7e24c12009-10-30 11:49:00 +00005#include <errno.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +00006#include <fcntl.h>
7#include <signal.h>
8#include <stdlib.h>
9#include <string.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000010#include <sys/stat.h>
11#include <sys/time.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000012#include <sys/types.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000013#include <sys/wait.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000014#include <unistd.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000015
Ben Murdochb8a8cc12014-11-26 15:28:44 +000016#include "src/d8.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000017
Ben Murdochb8a8cc12014-11-26 15:28:44 +000018#if !V8_OS_NACL
19#include <sys/select.h>
20#endif
Steve Blocka7e24c12009-10-30 11:49:00 +000021
22namespace v8 {
23
24
25// If the buffer ends in the middle of a UTF-8 sequence then we return
26// the length of the string up to but not including the incomplete UTF-8
27// sequence. If the buffer ends with a valid UTF-8 sequence then we
28// return the whole buffer.
29static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
30 int answer = len;
31 // 1-byte encoding.
32 static const int kUtf8SingleByteMask = 0x80;
33 static const int kUtf8SingleByteValue = 0x00;
34 // 2-byte encoding.
35 static const int kUtf8TwoByteMask = 0xe0;
36 static const int kUtf8TwoByteValue = 0xc0;
37 // 3-byte encoding.
38 static const int kUtf8ThreeByteMask = 0xf0;
39 static const int kUtf8ThreeByteValue = 0xe0;
40 // 4-byte encoding.
41 static const int kUtf8FourByteMask = 0xf8;
42 static const int kUtf8FourByteValue = 0xf0;
43 // Subsequent bytes of a multi-byte encoding.
44 static const int kMultiByteMask = 0xc0;
45 static const int kMultiByteValue = 0x80;
46 int multi_byte_bytes_seen = 0;
47 while (answer > 0) {
48 int c = buffer[answer - 1];
49 // Ends in valid single-byte sequence?
50 if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
51 // Ends in one or more subsequent bytes of a multi-byte value?
52 if ((c & kMultiByteMask) == kMultiByteValue) {
53 multi_byte_bytes_seen++;
54 answer--;
55 } else {
56 if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
57 if (multi_byte_bytes_seen >= 1) {
58 return answer + 2;
59 }
60 return answer - 1;
61 } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
62 if (multi_byte_bytes_seen >= 2) {
63 return answer + 3;
64 }
65 return answer - 1;
66 } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
67 if (multi_byte_bytes_seen >= 3) {
68 return answer + 4;
69 }
70 return answer - 1;
71 } else {
72 return answer; // Malformed UTF-8.
73 }
74 }
75 }
76 return 0;
77}
78
79
80// Suspends the thread until there is data available from the child process.
81// Returns false on timeout, true on data ready.
82static bool WaitOnFD(int fd,
83 int read_timeout,
84 int total_timeout,
Ben Murdochb8a8cc12014-11-26 15:28:44 +000085 const struct timeval& start_time) {
Steve Blocka7e24c12009-10-30 11:49:00 +000086 fd_set readfds, writefds, exceptfds;
87 struct timeval timeout;
88 int gone = 0;
89 if (total_timeout != -1) {
90 struct timeval time_now;
91 gettimeofday(&time_now, NULL);
92 int seconds = time_now.tv_sec - start_time.tv_sec;
93 gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
94 if (gone >= total_timeout) return false;
95 }
96 FD_ZERO(&readfds);
97 FD_ZERO(&writefds);
98 FD_ZERO(&exceptfds);
99 FD_SET(fd, &readfds);
100 FD_SET(fd, &exceptfds);
101 if (read_timeout == -1 ||
102 (total_timeout != -1 && total_timeout - gone < read_timeout)) {
103 read_timeout = total_timeout - gone;
104 }
105 timeout.tv_usec = (read_timeout % 1000) * 1000;
106 timeout.tv_sec = read_timeout / 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000107#if V8_OS_NACL
108 // PNaCL has no support for select.
109 int number_of_fds_ready = -1;
110#else
Steve Blocka7e24c12009-10-30 11:49:00 +0000111 int number_of_fds_ready = select(fd + 1,
112 &readfds,
113 &writefds,
114 &exceptfds,
115 read_timeout != -1 ? &timeout : NULL);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000116#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000117 return number_of_fds_ready == 1;
118}
119
120
121// Checks whether we ran out of time on the timeout. Returns true if we ran out
122// of time, false if we still have time.
123static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
124 if (total_time == -1) return false;
125 struct timeval time_now;
126 gettimeofday(&time_now, NULL);
127 // Careful about overflow.
128 int seconds = time_now.tv_sec - start_time.tv_sec;
129 if (seconds > 100) {
130 if (seconds * 1000 > total_time) return true;
131 return false;
132 }
133 int useconds = time_now.tv_usec - start_time.tv_usec;
134 if (seconds * 1000000 + useconds > total_time * 1000) {
135 return true;
136 }
137 return false;
138}
139
140
141// A utility class that does a non-hanging waitpid on the child process if we
142// bail out of the System() function early. If you don't ever do a waitpid on
143// a subprocess then it turns into one of those annoying 'zombie processes'.
144class ZombieProtector {
145 public:
146 explicit ZombieProtector(int pid): pid_(pid) { }
147 ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
148 void ChildIsDeadNow() { pid_ = 0; }
149 private:
150 int pid_;
151};
152
153
154// A utility class that closes a file descriptor when it goes out of scope.
155class OpenFDCloser {
156 public:
157 explicit OpenFDCloser(int fd): fd_(fd) { }
158 ~OpenFDCloser() { close(fd_); }
159 private:
160 int fd_;
161};
162
163
164// A utility class that takes the array of command arguments and puts then in an
165// array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
166// scope.
167class ExecArgs {
168 public:
169 ExecArgs() {
170 exec_args_[0] = NULL;
171 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000172 bool Init(Isolate* isolate, Handle<Value> arg0, Handle<Array> command_args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000173 String::Utf8Value prog(arg0);
174 if (*prog == NULL) {
175 const char* message =
176 "os.system(): String conversion of program name failed";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000177 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000178 return false;
179 }
180 int len = prog.length() + 3;
181 char* c_arg = new char[len];
182 snprintf(c_arg, len, "%s", *prog);
183 exec_args_[0] = c_arg;
184 int i = 1;
185 for (unsigned j = 0; j < command_args->Length(); i++, j++) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000186 Handle<Value> arg(command_args->Get(Integer::New(isolate, j)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000187 String::Utf8Value utf8_arg(arg);
188 if (*utf8_arg == NULL) {
189 exec_args_[i] = NULL; // Consistent state for destructor.
190 const char* message =
191 "os.system(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000192 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000193 return false;
194 }
195 int len = utf8_arg.length() + 1;
196 char* c_arg = new char[len];
197 snprintf(c_arg, len, "%s", *utf8_arg);
198 exec_args_[i] = c_arg;
199 }
200 exec_args_[i] = NULL;
201 return true;
202 }
203 ~ExecArgs() {
204 for (unsigned i = 0; i < kMaxArgs; i++) {
205 if (exec_args_[i] == NULL) {
206 return;
207 }
208 delete [] exec_args_[i];
209 exec_args_[i] = 0;
210 }
211 }
212 static const unsigned kMaxArgs = 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000213 char* const* arg_array() const { return exec_args_; }
214 const char* arg0() const { return exec_args_[0]; }
Ben Murdoch589d6972011-11-30 16:04:58 +0000215
Steve Blocka7e24c12009-10-30 11:49:00 +0000216 private:
217 char* exec_args_[kMaxArgs + 1];
218};
219
220
221// Gets the optional timeouts from the arguments to the system() call.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000222static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
Steve Blocka7e24c12009-10-30 11:49:00 +0000223 int* read_timeout,
224 int* total_timeout) {
225 if (args.Length() > 3) {
226 if (args[3]->IsNumber()) {
227 *total_timeout = args[3]->Int32Value();
228 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000229 args.GetIsolate()->ThrowException(String::NewFromUtf8(
230 args.GetIsolate(), "system: Argument 4 must be a number"));
Steve Blocka7e24c12009-10-30 11:49:00 +0000231 return false;
232 }
233 }
234 if (args.Length() > 2) {
235 if (args[2]->IsNumber()) {
236 *read_timeout = args[2]->Int32Value();
237 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000238 args.GetIsolate()->ThrowException(String::NewFromUtf8(
239 args.GetIsolate(), "system: Argument 3 must be a number"));
Steve Blocka7e24c12009-10-30 11:49:00 +0000240 return false;
241 }
242 }
243 return true;
244}
245
246
247static const int kReadFD = 0;
248static const int kWriteFD = 1;
249
250
251// This is run in the child process after fork() but before exec(). It normally
252// ends with the child process being replaced with the desired child program.
253// It only returns if an error occurred.
254static void ExecSubprocess(int* exec_error_fds,
255 int* stdout_fds,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000256 const ExecArgs& exec_args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000257 close(exec_error_fds[kReadFD]); // Don't need this in the child.
258 close(stdout_fds[kReadFD]); // Don't need this in the child.
259 close(1); // Close stdout.
260 dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
261 close(stdout_fds[kWriteFD]); // Don't need the original fd now.
262 fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
263 execvp(exec_args.arg0(), exec_args.arg_array());
264 // Only get here if the exec failed. Write errno to the parent to tell
265 // them it went wrong. If it went well the pipe is closed.
266 int err = errno;
267 int bytes_written;
268 do {
269 bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
270 } while (bytes_written == -1 && errno == EINTR);
271 // Return (and exit child process).
272}
273
274
275// Runs in the parent process. Checks that the child was able to exec (closing
276// the file desriptor), or reports an error if it failed.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000277static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000278 int bytes_read;
279 int err;
280 do {
281 bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
282 } while (bytes_read == -1 && errno == EINTR);
283 if (bytes_read != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000284 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(err)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000285 return false;
286 }
287 return true;
288}
289
290
291// Accumulates the output from the child in a string handle. Returns true if it
292// succeeded or false if an exception was thrown.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000293static Handle<Value> GetStdout(Isolate* isolate,
294 int child_fd,
295 const struct timeval& start_time,
Steve Blocka7e24c12009-10-30 11:49:00 +0000296 int read_timeout,
297 int total_timeout) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000298 Handle<String> accumulator = String::Empty(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000299
300 int fullness = 0;
301 static const int kStdoutReadBufferSize = 4096;
302 char buffer[kStdoutReadBufferSize];
303
304 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000305 return isolate->ThrowException(
306 String::NewFromUtf8(isolate, strerror(errno)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000307 }
308
309 int bytes_read;
310 do {
311 bytes_read = read(child_fd,
312 buffer + fullness,
313 kStdoutReadBufferSize - fullness);
314 if (bytes_read == -1) {
315 if (errno == EAGAIN) {
316 if (!WaitOnFD(child_fd,
317 read_timeout,
318 total_timeout,
319 start_time) ||
320 (TimeIsOut(start_time, total_timeout))) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000321 return isolate->ThrowException(
322 String::NewFromUtf8(isolate, "Timed out waiting for output"));
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 }
324 continue;
325 } else if (errno == EINTR) {
326 continue;
327 } else {
328 break;
329 }
330 }
331 if (bytes_read + fullness > 0) {
332 int length = bytes_read == 0 ?
333 bytes_read + fullness :
334 LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000335 Handle<String> addition =
336 String::NewFromUtf8(isolate, buffer, String::kNormalString, length);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000337 accumulator = String::Concat(accumulator, addition);
Steve Blocka7e24c12009-10-30 11:49:00 +0000338 fullness = bytes_read + fullness - length;
339 memcpy(buffer, buffer + length, fullness);
340 }
341 } while (bytes_read != 0);
342 return accumulator;
343}
344
345
346// Modern Linux has the waitid call, which is like waitpid, but more useful
347// if you want a timeout. If we don't have waitid we can't limit the time
348// waiting for the process to exit without losing the information about
349// whether it exited normally. In the common case this doesn't matter because
350// we don't get here before the child has closed stdout and most programs don't
351// do that before they exit.
352//
353// We're disabling usage of waitid in Mac OS X because it doens't work for us:
354// a parent process hangs on waiting while a child process is already a zombie.
355// See http://code.google.com/p/v8/issues/detail?id=401.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100356#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
357 && !defined(__NetBSD__)
Steve Block44f0eee2011-05-26 01:26:41 +0100358#if !defined(__FreeBSD__)
Steve Blocka7e24c12009-10-30 11:49:00 +0000359#define HAS_WAITID 1
360#endif
Steve Block44f0eee2011-05-26 01:26:41 +0100361#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000362
363
364// Get exit status of child.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000365static bool WaitForChild(Isolate* isolate,
366 int pid,
367 ZombieProtector& child_waiter, // NOLINT
368 const struct timeval& start_time,
Steve Blocka7e24c12009-10-30 11:49:00 +0000369 int read_timeout,
370 int total_timeout) {
371#ifdef HAS_WAITID
372
373 siginfo_t child_info;
374 child_info.si_pid = 0;
375 int useconds = 1;
376 // Wait for child to exit.
377 while (child_info.si_pid == 0) {
378 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
379 usleep(useconds);
380 if (useconds < 1000000) useconds <<= 1;
381 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
382 (TimeIsOut(start_time, total_timeout))) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000383 isolate->ThrowException(String::NewFromUtf8(
384 isolate, "Timed out waiting for process to terminate"));
Steve Blocka7e24c12009-10-30 11:49:00 +0000385 kill(pid, SIGINT);
386 return false;
387 }
388 }
389 if (child_info.si_code == CLD_KILLED) {
390 char message[999];
391 snprintf(message,
392 sizeof(message),
393 "Child killed by signal %d",
394 child_info.si_status);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000395 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000396 return false;
397 }
398 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
399 char message[999];
400 snprintf(message,
401 sizeof(message),
402 "Child exited with status %d",
403 child_info.si_status);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000404 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000405 return false;
406 }
407
408#else // No waitid call.
409
410 int child_status;
411 waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
412 child_waiter.ChildIsDeadNow();
413 if (WIFSIGNALED(child_status)) {
414 char message[999];
415 snprintf(message,
416 sizeof(message),
417 "Child killed by signal %d",
418 WTERMSIG(child_status));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000419 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000420 return false;
421 }
422 if (WEXITSTATUS(child_status) != 0) {
423 char message[999];
424 int exit_status = WEXITSTATUS(child_status);
425 snprintf(message,
426 sizeof(message),
427 "Child exited with status %d",
428 exit_status);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000429 isolate->ThrowException(String::NewFromUtf8(isolate, message));
Steve Blocka7e24c12009-10-30 11:49:00 +0000430 return false;
431 }
432
433#endif // No waitid call.
434
435 return true;
436}
437
438
439// Implementation of the system() function (see d8.h for details).
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000440void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
441 HandleScope scope(args.GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000442 int read_timeout = -1;
443 int total_timeout = -1;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000444 if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000445 Handle<Array> command_args;
446 if (args.Length() > 1) {
447 if (!args[1]->IsArray()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000448 args.GetIsolate()->ThrowException(String::NewFromUtf8(
449 args.GetIsolate(), "system: Argument 2 must be an array"));
450 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000451 }
452 command_args = Handle<Array>::Cast(args[1]);
453 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000454 command_args = Array::New(args.GetIsolate(), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000455 }
456 if (command_args->Length() > ExecArgs::kMaxArgs) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000457 args.GetIsolate()->ThrowException(String::NewFromUtf8(
458 args.GetIsolate(), "Too many arguments to system()"));
459 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000460 }
461 if (args.Length() < 1) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000462 args.GetIsolate()->ThrowException(String::NewFromUtf8(
463 args.GetIsolate(), "Too few arguments to system()"));
464 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000465 }
466
467 struct timeval start_time;
468 gettimeofday(&start_time, NULL);
469
470 ExecArgs exec_args;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000471 if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
472 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 }
474 int exec_error_fds[2];
475 int stdout_fds[2];
476
477 if (pipe(exec_error_fds) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000478 args.GetIsolate()->ThrowException(
479 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
480 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000481 }
482 if (pipe(stdout_fds) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000483 args.GetIsolate()->ThrowException(
484 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
485 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000486 }
487
488 pid_t pid = fork();
489 if (pid == 0) { // Child process.
490 ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
491 exit(1);
492 }
493
494 // Parent process. Ensure that we clean up if we exit this function early.
495 ZombieProtector child_waiter(pid);
496 close(exec_error_fds[kWriteFD]);
497 close(stdout_fds[kWriteFD]);
498 OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
499 OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
500
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000501 if (!ChildLaunchedOK(args.GetIsolate(), exec_error_fds)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000502
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000503 Handle<Value> accumulator = GetStdout(args.GetIsolate(),
504 stdout_fds[kReadFD],
Steve Blocka7e24c12009-10-30 11:49:00 +0000505 start_time,
506 read_timeout,
507 total_timeout);
508 if (accumulator->IsUndefined()) {
509 kill(pid, SIGINT); // On timeout, kill the subprocess.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000510 args.GetReturnValue().Set(accumulator);
511 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000512 }
513
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000514 if (!WaitForChild(args.GetIsolate(),
515 pid,
Steve Blocka7e24c12009-10-30 11:49:00 +0000516 child_waiter,
517 start_time,
518 read_timeout,
519 total_timeout)) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000520 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000521 }
522
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000523 args.GetReturnValue().Set(accumulator);
Steve Blocka7e24c12009-10-30 11:49:00 +0000524}
525
526
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000527void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000528 if (args.Length() != 1) {
529 const char* message = "chdir() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000530 args.GetIsolate()->ThrowException(
531 String::NewFromUtf8(args.GetIsolate(), message));
532 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000533 }
534 String::Utf8Value directory(args[0]);
535 if (*directory == NULL) {
536 const char* message = "os.chdir(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000537 args.GetIsolate()->ThrowException(
538 String::NewFromUtf8(args.GetIsolate(), message));
539 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000540 }
541 if (chdir(*directory) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000542 args.GetIsolate()->ThrowException(
543 String::NewFromUtf8(args.GetIsolate(), strerror(errno)));
544 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000545 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000546}
547
548
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000549void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000550 if (args.Length() != 1) {
551 const char* message = "umask() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000552 args.GetIsolate()->ThrowException(
553 String::NewFromUtf8(args.GetIsolate(), message));
554 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000555 }
556 if (args[0]->IsNumber()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000557#if V8_OS_NACL
558 // PNaCL has no support for umask.
559 int previous = 0;
560#else
561 int previous = umask(args[0]->Int32Value());
562#endif
563 args.GetReturnValue().Set(previous);
564 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000565 } else {
566 const char* message = "umask() argument must be numeric";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000567 args.GetIsolate()->ThrowException(
568 String::NewFromUtf8(args.GetIsolate(), message));
569 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000570 }
571}
572
573
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000574static bool CheckItsADirectory(Isolate* isolate, char* directory) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 struct stat stat_buf;
576 int stat_result = stat(directory, &stat_buf);
577 if (stat_result != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000578 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000579 return false;
580 }
581 if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000582 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(EEXIST)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000583 return false;
584}
585
586
587// Returns true for success. Creates intermediate directories as needed. No
588// error if the directory exists already.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000589static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000590 int result = mkdir(directory, mask);
591 if (result == 0) return true;
592 if (errno == EEXIST) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000593 return CheckItsADirectory(isolate, directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000594 } else if (errno == ENOENT) { // Intermediate path element is missing.
595 char* last_slash = strrchr(directory, '/');
596 if (last_slash == NULL) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000597 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000598 return false;
599 }
600 *last_slash = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000601 if (!mkdirp(isolate, directory, mask)) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000602 *last_slash = '/';
603 result = mkdir(directory, mask);
604 if (result == 0) return true;
605 if (errno == EEXIST) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000606 return CheckItsADirectory(isolate, directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000607 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000608 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000609 return false;
610 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000611 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
Steve Blocka7e24c12009-10-30 11:49:00 +0000612 return false;
613 }
614}
615
616
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000617void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000618 mode_t mask = 0777;
619 if (args.Length() == 2) {
620 if (args[1]->IsNumber()) {
621 mask = args[1]->Int32Value();
622 } else {
623 const char* message = "mkdirp() second argument must be numeric";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000624 args.GetIsolate()->ThrowException(
625 String::NewFromUtf8(args.GetIsolate(), message));
626 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000627 }
628 } else if (args.Length() != 1) {
629 const char* message = "mkdirp() takes one or two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000630 args.GetIsolate()->ThrowException(
631 String::NewFromUtf8(args.GetIsolate(), message));
632 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000633 }
634 String::Utf8Value directory(args[0]);
635 if (*directory == NULL) {
636 const char* message = "os.mkdirp(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000637 args.GetIsolate()->ThrowException(
638 String::NewFromUtf8(args.GetIsolate(), message));
639 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000640 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000641 mkdirp(args.GetIsolate(), *directory, mask);
Steve Blocka7e24c12009-10-30 11:49:00 +0000642}
643
644
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000645void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000646 if (args.Length() != 1) {
647 const char* message = "rmdir() takes one or two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000648 args.GetIsolate()->ThrowException(
649 String::NewFromUtf8(args.GetIsolate(), message));
650 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000651 }
652 String::Utf8Value directory(args[0]);
653 if (*directory == NULL) {
654 const char* message = "os.rmdir(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000655 args.GetIsolate()->ThrowException(
656 String::NewFromUtf8(args.GetIsolate(), message));
657 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000658 }
659 rmdir(*directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000660}
661
662
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000663void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000664 if (args.Length() != 2) {
665 const char* message = "setenv() takes two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000666 args.GetIsolate()->ThrowException(
667 String::NewFromUtf8(args.GetIsolate(), message));
668 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000669 }
670 String::Utf8Value var(args[0]);
671 String::Utf8Value value(args[1]);
672 if (*var == NULL) {
673 const char* message =
674 "os.setenv(): String conversion of variable name failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000675 args.GetIsolate()->ThrowException(
676 String::NewFromUtf8(args.GetIsolate(), message));
677 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000678 }
679 if (*value == NULL) {
680 const char* message =
681 "os.setenv(): String conversion of variable contents failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000682 args.GetIsolate()->ThrowException(
683 String::NewFromUtf8(args.GetIsolate(), message));
684 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000685 }
686 setenv(*var, *value, 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000687}
688
689
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000690void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Block6ded16b2010-05-10 14:33:55 +0100691 if (args.Length() != 1) {
692 const char* message = "unsetenv() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000693 args.GetIsolate()->ThrowException(
694 String::NewFromUtf8(args.GetIsolate(), message));
695 return;
Steve Block6ded16b2010-05-10 14:33:55 +0100696 }
697 String::Utf8Value var(args[0]);
698 if (*var == NULL) {
699 const char* message =
700 "os.setenv(): String conversion of variable name failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000701 args.GetIsolate()->ThrowException(
702 String::NewFromUtf8(args.GetIsolate(), message));
703 return;
Steve Block6ded16b2010-05-10 14:33:55 +0100704 }
705 unsetenv(*var);
Steve Block6ded16b2010-05-10 14:33:55 +0100706}
707
708
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000709void Shell::AddOSMethods(Isolate* isolate, Handle<ObjectTemplate> os_templ) {
710 os_templ->Set(String::NewFromUtf8(isolate, "system"),
711 FunctionTemplate::New(isolate, System));
712 os_templ->Set(String::NewFromUtf8(isolate, "chdir"),
713 FunctionTemplate::New(isolate, ChangeDirectory));
714 os_templ->Set(String::NewFromUtf8(isolate, "setenv"),
715 FunctionTemplate::New(isolate, SetEnvironment));
716 os_templ->Set(String::NewFromUtf8(isolate, "unsetenv"),
717 FunctionTemplate::New(isolate, UnsetEnvironment));
718 os_templ->Set(String::NewFromUtf8(isolate, "umask"),
719 FunctionTemplate::New(isolate, SetUMask));
720 os_templ->Set(String::NewFromUtf8(isolate, "mkdirp"),
721 FunctionTemplate::New(isolate, MakeDirectory));
722 os_templ->Set(String::NewFromUtf8(isolate, "rmdir"),
723 FunctionTemplate::New(isolate, RemoveDirectory));
Steve Blocka7e24c12009-10-30 11:49:00 +0000724}
725
726} // namespace v8