1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
26 // If the buffer ends in the middle of a UTF-8 sequence then we return
27 // the length of the string up to but not including the incomplete UTF-8
28 // sequence. If the buffer ends with a valid UTF-8 sequence then we
29 // return the whole buffer.
30 static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
33 static const int kUtf8SingleByteMask = 0x80;
34 static const int kUtf8SingleByteValue = 0x00;
36 static const int kUtf8TwoByteMask = 0xe0;
37 static const int kUtf8TwoByteValue = 0xc0;
39 static const int kUtf8ThreeByteMask = 0xf0;
40 static const int kUtf8ThreeByteValue = 0xe0;
42 static const int kUtf8FourByteMask = 0xf8;
43 static const int kUtf8FourByteValue = 0xf0;
44 // Subsequent bytes of a multi-byte encoding.
45 static const int kMultiByteMask = 0xc0;
46 static const int kMultiByteValue = 0x80;
47 int multi_byte_bytes_seen = 0;
49 int c = buffer[answer - 1];
50 // Ends in valid single-byte sequence?
51 if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
52 // Ends in one or more subsequent bytes of a multi-byte value?
53 if ((c & kMultiByteMask) == kMultiByteValue) {
54 multi_byte_bytes_seen++;
57 if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
58 if (multi_byte_bytes_seen >= 1) {
62 } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
63 if (multi_byte_bytes_seen >= 2) {
67 } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
68 if (multi_byte_bytes_seen >= 3) {
73 return answer; // Malformed UTF-8.
81 // Suspends the thread until there is data available from the child process.
82 // Returns false on timeout, true on data ready.
83 static bool WaitOnFD(int fd,
86 struct timeval& start_time) {
87 fd_set readfds, writefds, exceptfds;
88 struct timeval timeout;
90 if (total_timeout != -1) {
91 struct timeval time_now;
92 gettimeofday(&time_now, NULL);
93 int seconds = time_now.tv_sec - start_time.tv_sec;
94 gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
95 if (gone >= total_timeout) return false;
100 FD_SET(fd, &readfds);
101 FD_SET(fd, &exceptfds);
102 if (read_timeout == -1 ||
103 (total_timeout != -1 && total_timeout - gone < read_timeout)) {
104 read_timeout = total_timeout - gone;
106 timeout.tv_usec = (read_timeout % 1000) * 1000;
107 timeout.tv_sec = read_timeout / 1000;
108 int number_of_fds_ready = select(fd + 1,
112 read_timeout != -1 ? &timeout : NULL);
113 return number_of_fds_ready == 1;
117 // Checks whether we ran out of time on the timeout. Returns true if we ran out
118 // of time, false if we still have time.
119 static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
120 if (total_time == -1) return false;
121 struct timeval time_now;
122 gettimeofday(&time_now, NULL);
123 // Careful about overflow.
124 int seconds = time_now.tv_sec - start_time.tv_sec;
126 if (seconds * 1000 > total_time) return true;
129 int useconds = time_now.tv_usec - start_time.tv_usec;
130 if (seconds * 1000000 + useconds > total_time * 1000) {
137 // A utility class that does a non-hanging waitpid on the child process if we
138 // bail out of the System() function early. If you don't ever do a waitpid on
139 // a subprocess then it turns into one of those annoying 'zombie processes'.
140 class ZombieProtector {
142 explicit ZombieProtector(int pid): pid_(pid) { }
143 ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
144 void ChildIsDeadNow() { pid_ = 0; }
150 // A utility class that closes a file descriptor when it goes out of scope.
153 explicit OpenFDCloser(int fd): fd_(fd) { }
154 ~OpenFDCloser() { close(fd_); }
160 // A utility class that takes the array of command arguments and puts then in an
161 // array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
166 exec_args_[0] = NULL;
168 bool Init(Isolate* isolate, Handle<Value> arg0, Handle<Array> command_args) {
169 String::Utf8Value prog(arg0);
171 const char* message =
172 "os.system(): String conversion of program name failed";
173 isolate->ThrowException(String::NewFromUtf8(isolate, message));
176 int len = prog.length() + 3;
177 char* c_arg = new char[len];
178 snprintf(c_arg, len, "%s", *prog);
179 exec_args_[0] = c_arg;
181 for (unsigned j = 0; j < command_args->Length(); i++, j++) {
182 Handle<Value> arg(command_args->Get(Integer::New(isolate, j)));
183 String::Utf8Value utf8_arg(arg);
184 if (*utf8_arg == NULL) {
185 exec_args_[i] = NULL; // Consistent state for destructor.
186 const char* message =
187 "os.system(): String conversion of argument failed.";
188 isolate->ThrowException(String::NewFromUtf8(isolate, message));
191 int len = utf8_arg.length() + 1;
192 char* c_arg = new char[len];
193 snprintf(c_arg, len, "%s", *utf8_arg);
194 exec_args_[i] = c_arg;
196 exec_args_[i] = NULL;
200 for (unsigned i = 0; i < kMaxArgs; i++) {
201 if (exec_args_[i] == NULL) {
204 delete [] exec_args_[i];
208 static const unsigned kMaxArgs = 1000;
209 char** arg_array() { return exec_args_; }
210 char* arg0() { return exec_args_[0]; }
213 char* exec_args_[kMaxArgs + 1];
217 // Gets the optional timeouts from the arguments to the system() call.
218 static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
220 int* total_timeout) {
221 if (args.Length() > 3) {
222 if (args[3]->IsNumber()) {
223 *total_timeout = args[3]->Int32Value();
225 args.GetIsolate()->ThrowException(String::NewFromUtf8(
226 args.GetIsolate(), "system: Argument 4 must be a number"));
230 if (args.Length() > 2) {
231 if (args[2]->IsNumber()) {
232 *read_timeout = args[2]->Int32Value();
234 args.GetIsolate()->ThrowException(String::NewFromUtf8(
235 args.GetIsolate(), "system: Argument 3 must be a number"));
243 static const int kReadFD = 0;
244 static const int kWriteFD = 1;
247 // This is run in the child process after fork() but before exec(). It normally
248 // ends with the child process being replaced with the desired child program.
249 // It only returns if an error occurred.
250 static void ExecSubprocess(int* exec_error_fds,
252 ExecArgs& exec_args) {
253 close(exec_error_fds[kReadFD]); // Don't need this in the child.
254 close(stdout_fds[kReadFD]); // Don't need this in the child.
255 close(1); // Close stdout.
256 dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
257 close(stdout_fds[kWriteFD]); // Don't need the original fd now.
258 fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
259 execvp(exec_args.arg0(), exec_args.arg_array());
260 // Only get here if the exec failed. Write errno to the parent to tell
261 // them it went wrong. If it went well the pipe is closed.
265 bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
266 } while (bytes_written == -1 && errno == EINTR);
267 // Return (and exit child process).
271 // Runs in the parent process. Checks that the child was able to exec (closing
272 // the file desriptor), or reports an error if it failed.
273 static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
277 bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
278 } while (bytes_read == -1 && errno == EINTR);
279 if (bytes_read != 0) {
280 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(err)));
287 // Accumulates the output from the child in a string handle. Returns true if it
288 // succeeded or false if an exception was thrown.
289 static Handle<Value> GetStdout(Isolate* isolate,
291 struct timeval& start_time,
294 Handle<String> accumulator = String::Empty(isolate);
297 static const int kStdoutReadBufferSize = 4096;
298 char buffer[kStdoutReadBufferSize];
300 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
301 return isolate->ThrowException(
302 String::NewFromUtf8(isolate, strerror(errno)));
307 bytes_read = read(child_fd,
309 kStdoutReadBufferSize - fullness);
310 if (bytes_read == -1) {
311 if (errno == EAGAIN) {
312 if (!WaitOnFD(child_fd,
316 (TimeIsOut(start_time, total_timeout))) {
317 return isolate->ThrowException(
318 String::NewFromUtf8(isolate, "Timed out waiting for output"));
321 } else if (errno == EINTR) {
327 if (bytes_read + fullness > 0) {
328 int length = bytes_read == 0 ?
329 bytes_read + fullness :
330 LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
331 Handle<String> addition =
332 String::NewFromUtf8(isolate, buffer, String::kNormalString, length);
333 accumulator = String::Concat(accumulator, addition);
334 fullness = bytes_read + fullness - length;
335 memcpy(buffer, buffer + length, fullness);
337 } while (bytes_read != 0);
342 // Modern Linux has the waitid call, which is like waitpid, but more useful
343 // if you want a timeout. If we don't have waitid we can't limit the time
344 // waiting for the process to exit without losing the information about
345 // whether it exited normally. In the common case this doesn't matter because
346 // we don't get here before the child has closed stdout and most programs don't
347 // do that before they exit.
349 // We're disabling usage of waitid in Mac OS X because it doens't work for us:
350 // a parent process hangs on waiting while a child process is already a zombie.
351 // See http://code.google.com/p/v8/issues/detail?id=401.
352 #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
353 && !defined(__NetBSD__)
354 #if !defined(__FreeBSD__)
360 // Get exit status of child.
361 static bool WaitForChild(Isolate* isolate,
363 ZombieProtector& child_waiter,
364 struct timeval& start_time,
369 siginfo_t child_info;
370 child_info.si_pid = 0;
372 // Wait for child to exit.
373 while (child_info.si_pid == 0) {
374 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
376 if (useconds < 1000000) useconds <<= 1;
377 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
378 (TimeIsOut(start_time, total_timeout))) {
379 isolate->ThrowException(String::NewFromUtf8(
380 isolate, "Timed out waiting for process to terminate"));
385 if (child_info.si_code == CLD_KILLED) {
389 "Child killed by signal %d",
390 child_info.si_status);
391 isolate->ThrowException(String::NewFromUtf8(isolate, message));
394 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
398 "Child exited with status %d",
399 child_info.si_status);
400 isolate->ThrowException(String::NewFromUtf8(isolate, message));
404 #else // No waitid call.
407 waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
408 child_waiter.ChildIsDeadNow();
409 if (WIFSIGNALED(child_status)) {
413 "Child killed by signal %d",
414 WTERMSIG(child_status));
415 isolate->ThrowException(String::NewFromUtf8(isolate, message));
418 if (WEXITSTATUS(child_status) != 0) {
420 int exit_status = WEXITSTATUS(child_status);
423 "Child exited with status %d",
425 isolate->ThrowException(String::NewFromUtf8(isolate, message));
429 #endif // No waitid call.
435 // Implementation of the system() function (see d8.h for details).
436 void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
437 HandleScope scope(args.GetIsolate());
438 int read_timeout = -1;
439 int total_timeout = -1;
440 if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
441 Handle<Array> command_args;
442 if (args.Length() > 1) {
443 if (!args[1]->IsArray()) {
444 args.GetIsolate()->ThrowException(String::NewFromUtf8(
445 args.GetIsolate(), "system: Argument 2 must be an array"));
448 command_args = Handle<Array>::Cast(args[1]);
450 command_args = Array::New(args.GetIsolate(), 0);
452 if (command_args->Length() > ExecArgs::kMaxArgs) {
453 args.GetIsolate()->ThrowException(String::NewFromUtf8(
454 args.GetIsolate(), "Too many arguments to system()"));
457 if (args.Length() < 1) {
458 args.GetIsolate()->ThrowException(String::NewFromUtf8(
459 args.GetIsolate(), "Too few arguments to system()"));
463 struct timeval start_time;
464 gettimeofday(&start_time, NULL);
467 if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
470 int exec_error_fds[2];
473 if (pipe(exec_error_fds) != 0) {
474 args.GetIsolate()->ThrowException(
475 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
478 if (pipe(stdout_fds) != 0) {
479 args.GetIsolate()->ThrowException(
480 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
485 if (pid == 0) { // Child process.
486 ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
490 // Parent process. Ensure that we clean up if we exit this function early.
491 ZombieProtector child_waiter(pid);
492 close(exec_error_fds[kWriteFD]);
493 close(stdout_fds[kWriteFD]);
494 OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
495 OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
497 if (!ChildLaunchedOK(args.GetIsolate(), exec_error_fds)) return;
499 Handle<Value> accumulator = GetStdout(args.GetIsolate(),
504 if (accumulator->IsUndefined()) {
505 kill(pid, SIGINT); // On timeout, kill the subprocess.
506 args.GetReturnValue().Set(accumulator);
510 if (!WaitForChild(args.GetIsolate(),
519 args.GetReturnValue().Set(accumulator);
523 void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
524 if (args.Length() != 1) {
525 const char* message = "chdir() takes one argument";
526 args.GetIsolate()->ThrowException(
527 String::NewFromUtf8(args.GetIsolate(), message));
530 String::Utf8Value directory(args[0]);
531 if (*directory == NULL) {
532 const char* message = "os.chdir(): String conversion of argument failed.";
533 args.GetIsolate()->ThrowException(
534 String::NewFromUtf8(args.GetIsolate(), message));
537 if (chdir(*directory) != 0) {
538 args.GetIsolate()->ThrowException(
539 String::NewFromUtf8(args.GetIsolate(), strerror(errno)));
545 void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
546 if (args.Length() != 1) {
547 const char* message = "umask() takes one argument";
548 args.GetIsolate()->ThrowException(
549 String::NewFromUtf8(args.GetIsolate(), message));
552 if (args[0]->IsNumber()) {
553 mode_t mask = args[0]->Int32Value();
554 int previous = umask(mask);
555 args.GetReturnValue().Set(previous);
558 const char* message = "umask() argument must be numeric";
559 args.GetIsolate()->ThrowException(
560 String::NewFromUtf8(args.GetIsolate(), message));
566 static bool CheckItsADirectory(Isolate* isolate, char* directory) {
567 struct stat stat_buf;
568 int stat_result = stat(directory, &stat_buf);
569 if (stat_result != 0) {
570 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
573 if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
574 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(EEXIST)));
579 // Returns true for success. Creates intermediate directories as needed. No
580 // error if the directory exists already.
581 static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
582 int result = mkdir(directory, mask);
583 if (result == 0) return true;
584 if (errno == EEXIST) {
585 return CheckItsADirectory(isolate, directory);
586 } else if (errno == ENOENT) { // Intermediate path element is missing.
587 char* last_slash = strrchr(directory, '/');
588 if (last_slash == NULL) {
589 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
593 if (!mkdirp(isolate, directory, mask)) return false;
595 result = mkdir(directory, mask);
596 if (result == 0) return true;
597 if (errno == EEXIST) {
598 return CheckItsADirectory(isolate, directory);
600 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
603 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
609 void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
611 if (args.Length() == 2) {
612 if (args[1]->IsNumber()) {
613 mask = args[1]->Int32Value();
615 const char* message = "mkdirp() second argument must be numeric";
616 args.GetIsolate()->ThrowException(
617 String::NewFromUtf8(args.GetIsolate(), message));
620 } else if (args.Length() != 1) {
621 const char* message = "mkdirp() takes one or two arguments";
622 args.GetIsolate()->ThrowException(
623 String::NewFromUtf8(args.GetIsolate(), message));
626 String::Utf8Value directory(args[0]);
627 if (*directory == NULL) {
628 const char* message = "os.mkdirp(): String conversion of argument failed.";
629 args.GetIsolate()->ThrowException(
630 String::NewFromUtf8(args.GetIsolate(), message));
633 mkdirp(args.GetIsolate(), *directory, mask);
637 void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
638 if (args.Length() != 1) {
639 const char* message = "rmdir() takes one or two arguments";
640 args.GetIsolate()->ThrowException(
641 String::NewFromUtf8(args.GetIsolate(), message));
644 String::Utf8Value directory(args[0]);
645 if (*directory == NULL) {
646 const char* message = "os.rmdir(): String conversion of argument failed.";
647 args.GetIsolate()->ThrowException(
648 String::NewFromUtf8(args.GetIsolate(), message));
655 void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
656 if (args.Length() != 2) {
657 const char* message = "setenv() takes two arguments";
658 args.GetIsolate()->ThrowException(
659 String::NewFromUtf8(args.GetIsolate(), message));
662 String::Utf8Value var(args[0]);
663 String::Utf8Value value(args[1]);
665 const char* message =
666 "os.setenv(): String conversion of variable name failed.";
667 args.GetIsolate()->ThrowException(
668 String::NewFromUtf8(args.GetIsolate(), message));
671 if (*value == NULL) {
672 const char* message =
673 "os.setenv(): String conversion of variable contents failed.";
674 args.GetIsolate()->ThrowException(
675 String::NewFromUtf8(args.GetIsolate(), message));
678 setenv(*var, *value, 1);
682 void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
683 if (args.Length() != 1) {
684 const char* message = "unsetenv() takes one argument";
685 args.GetIsolate()->ThrowException(
686 String::NewFromUtf8(args.GetIsolate(), message));
689 String::Utf8Value var(args[0]);
691 const char* message =
692 "os.setenv(): String conversion of variable name failed.";
693 args.GetIsolate()->ThrowException(
694 String::NewFromUtf8(args.GetIsolate(), message));
701 void Shell::AddOSMethods(Isolate* isolate, Handle<ObjectTemplate> os_templ) {
702 os_templ->Set(String::NewFromUtf8(isolate, "system"),
703 FunctionTemplate::New(isolate, System));
704 os_templ->Set(String::NewFromUtf8(isolate, "chdir"),
705 FunctionTemplate::New(isolate, ChangeDirectory));
706 os_templ->Set(String::NewFromUtf8(isolate, "setenv"),
707 FunctionTemplate::New(isolate, SetEnvironment));
708 os_templ->Set(String::NewFromUtf8(isolate, "unsetenv"),
709 FunctionTemplate::New(isolate, UnsetEnvironment));
710 os_templ->Set(String::NewFromUtf8(isolate, "umask"),
711 FunctionTemplate::New(isolate, SetUMask));
712 os_templ->Set(String::NewFromUtf8(isolate, "mkdirp"),
713 FunctionTemplate::New(isolate, MakeDirectory));
714 os_templ->Set(String::NewFromUtf8(isolate, "rmdir"),
715 FunctionTemplate::New(isolate, RemoveDirectory));