Home | History | Annotate | Download | only in src
      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.
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <signal.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <sys/select.h>
     11 #include <sys/stat.h>
     12 #include <sys/time.h>
     13 #include <sys/types.h>
     14 #include <sys/wait.h>
     15 #include <unistd.h>
     16 
     17 #include "src/d8.h"
     18 
     19 namespace v8 {
     20 
     21 
     22 // If the buffer ends in the middle of a UTF-8 sequence then we return
     23 // the length of the string up to but not including the incomplete UTF-8
     24 // sequence.  If the buffer ends with a valid UTF-8 sequence then we
     25 // return the whole buffer.
     26 static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
     27   int answer = len;
     28   // 1-byte encoding.
     29   static const int kUtf8SingleByteMask = 0x80;
     30   static const int kUtf8SingleByteValue = 0x00;
     31   // 2-byte encoding.
     32   static const int kUtf8TwoByteMask = 0xe0;
     33   static const int kUtf8TwoByteValue = 0xc0;
     34   // 3-byte encoding.
     35   static const int kUtf8ThreeByteMask = 0xf0;
     36   static const int kUtf8ThreeByteValue = 0xe0;
     37   // 4-byte encoding.
     38   static const int kUtf8FourByteMask = 0xf8;
     39   static const int kUtf8FourByteValue = 0xf0;
     40   // Subsequent bytes of a multi-byte encoding.
     41   static const int kMultiByteMask = 0xc0;
     42   static const int kMultiByteValue = 0x80;
     43   int multi_byte_bytes_seen = 0;
     44   while (answer > 0) {
     45     int c = buffer[answer - 1];
     46     // Ends in valid single-byte sequence?
     47     if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
     48     // Ends in one or more subsequent bytes of a multi-byte value?
     49     if ((c & kMultiByteMask) == kMultiByteValue) {
     50       multi_byte_bytes_seen++;
     51       answer--;
     52     } else {
     53       if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
     54         if (multi_byte_bytes_seen >= 1) {
     55           return answer + 2;
     56         }
     57         return answer - 1;
     58       } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
     59         if (multi_byte_bytes_seen >= 2) {
     60           return answer + 3;
     61         }
     62         return answer - 1;
     63       } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
     64         if (multi_byte_bytes_seen >= 3) {
     65           return answer + 4;
     66         }
     67         return answer - 1;
     68       } else {
     69         return answer;  // Malformed UTF-8.
     70       }
     71     }
     72   }
     73   return 0;
     74 }
     75 
     76 
     77 // Suspends the thread until there is data available from the child process.
     78 // Returns false on timeout, true on data ready.
     79 static bool WaitOnFD(int fd,
     80                      int read_timeout,
     81                      int total_timeout,
     82                      const struct timeval& start_time) {
     83   fd_set readfds, writefds, exceptfds;
     84   struct timeval timeout;
     85   int gone = 0;
     86   if (total_timeout != -1) {
     87     struct timeval time_now;
     88     gettimeofday(&time_now, NULL);
     89     time_t seconds = time_now.tv_sec - start_time.tv_sec;
     90     gone = static_cast<int>(seconds * 1000 +
     91                             (time_now.tv_usec - start_time.tv_usec) / 1000);
     92     if (gone >= total_timeout) return false;
     93   }
     94   FD_ZERO(&readfds);
     95   FD_ZERO(&writefds);
     96   FD_ZERO(&exceptfds);
     97   FD_SET(fd, &readfds);
     98   FD_SET(fd, &exceptfds);
     99   if (read_timeout == -1 ||
    100       (total_timeout != -1 && total_timeout - gone < read_timeout)) {
    101     read_timeout = total_timeout - gone;
    102   }
    103   timeout.tv_usec = (read_timeout % 1000) * 1000;
    104   timeout.tv_sec = read_timeout / 1000;
    105   int number_of_fds_ready = select(fd + 1,
    106                                    &readfds,
    107                                    &writefds,
    108                                    &exceptfds,
    109                                    read_timeout != -1 ? &timeout : NULL);
    110   return number_of_fds_ready == 1;
    111 }
    112 
    113 
    114 // Checks whether we ran out of time on the timeout.  Returns true if we ran out
    115 // of time, false if we still have time.
    116 static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
    117   if (total_time == -1) return false;
    118   struct timeval time_now;
    119   gettimeofday(&time_now, NULL);
    120   // Careful about overflow.
    121   int seconds = static_cast<int>(time_now.tv_sec - start_time.tv_sec);
    122   if (seconds > 100) {
    123     if (seconds * 1000 > total_time) return true;
    124     return false;
    125   }
    126   int useconds = static_cast<int>(time_now.tv_usec - start_time.tv_usec);
    127   if (seconds * 1000000 + useconds > total_time * 1000) {
    128     return true;
    129   }
    130   return false;
    131 }
    132 
    133 
    134 // A utility class that does a non-hanging waitpid on the child process if we
    135 // bail out of the System() function early.  If you don't ever do a waitpid on
    136 // a subprocess then it turns into one of those annoying 'zombie processes'.
    137 class ZombieProtector {
    138  public:
    139   explicit ZombieProtector(int pid): pid_(pid) { }
    140   ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
    141   void ChildIsDeadNow() { pid_ = 0; }
    142  private:
    143   int pid_;
    144 };
    145 
    146 
    147 // A utility class that closes a file descriptor when it goes out of scope.
    148 class OpenFDCloser {
    149  public:
    150   explicit OpenFDCloser(int fd): fd_(fd) { }
    151   ~OpenFDCloser() { close(fd_); }
    152  private:
    153   int fd_;
    154 };
    155 
    156 
    157 // A utility class that takes the array of command arguments and puts then in an
    158 // array of new[]ed UTF-8 C strings.  Deallocates them again when it goes out of
    159 // scope.
    160 class ExecArgs {
    161  public:
    162   ExecArgs() {
    163     exec_args_[0] = NULL;
    164   }
    165   bool Init(Isolate* isolate, Local<Value> arg0, Local<Array> command_args) {
    166     String::Utf8Value prog(arg0);
    167     if (*prog == NULL) {
    168       const char* message =
    169           "os.system(): String conversion of program name failed";
    170       isolate->ThrowException(
    171           String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    172               .ToLocalChecked());
    173       return false;
    174     }
    175     int len = prog.length() + 3;
    176     char* c_arg = new char[len];
    177     snprintf(c_arg, len, "%s", *prog);
    178     exec_args_[0] = c_arg;
    179     int i = 1;
    180     for (unsigned j = 0; j < command_args->Length(); i++, j++) {
    181       Local<Value> arg(
    182           command_args->Get(isolate->GetCurrentContext(),
    183                             Integer::New(isolate, j)).ToLocalChecked());
    184       String::Utf8Value utf8_arg(arg);
    185       if (*utf8_arg == NULL) {
    186         exec_args_[i] = NULL;  // Consistent state for destructor.
    187         const char* message =
    188             "os.system(): String conversion of argument failed.";
    189         isolate->ThrowException(
    190             String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    191                 .ToLocalChecked());
    192         return false;
    193       }
    194       int len = utf8_arg.length() + 1;
    195       char* c_arg = new char[len];
    196       snprintf(c_arg, len, "%s", *utf8_arg);
    197       exec_args_[i] = c_arg;
    198     }
    199     exec_args_[i] = NULL;
    200     return true;
    201   }
    202   ~ExecArgs() {
    203     for (unsigned i = 0; i < kMaxArgs; i++) {
    204       if (exec_args_[i] == NULL) {
    205         return;
    206       }
    207       delete [] exec_args_[i];
    208       exec_args_[i] = 0;
    209     }
    210   }
    211   static const unsigned kMaxArgs = 1000;
    212   char* const* arg_array() const { return exec_args_; }
    213   const char* arg0() const { return exec_args_[0]; }
    214 
    215  private:
    216   char* exec_args_[kMaxArgs + 1];
    217 };
    218 
    219 
    220 // Gets the optional timeouts from the arguments to the system() call.
    221 static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
    222                         int* read_timeout,
    223                         int* total_timeout) {
    224   if (args.Length() > 3) {
    225     if (args[3]->IsNumber()) {
    226       *total_timeout = args[3]
    227                            ->Int32Value(args.GetIsolate()->GetCurrentContext())
    228                            .FromJust();
    229     } else {
    230       args.GetIsolate()->ThrowException(
    231           String::NewFromUtf8(args.GetIsolate(),
    232                               "system: Argument 4 must be a number",
    233                               NewStringType::kNormal).ToLocalChecked());
    234       return false;
    235     }
    236   }
    237   if (args.Length() > 2) {
    238     if (args[2]->IsNumber()) {
    239       *read_timeout = args[2]
    240                           ->Int32Value(args.GetIsolate()->GetCurrentContext())
    241                           .FromJust();
    242     } else {
    243       args.GetIsolate()->ThrowException(
    244           String::NewFromUtf8(args.GetIsolate(),
    245                               "system: Argument 3 must be a number",
    246                               NewStringType::kNormal).ToLocalChecked());
    247       return false;
    248     }
    249   }
    250   return true;
    251 }
    252 
    253 
    254 static const int kReadFD = 0;
    255 static const int kWriteFD = 1;
    256 
    257 
    258 // This is run in the child process after fork() but before exec().  It normally
    259 // ends with the child process being replaced with the desired child program.
    260 // It only returns if an error occurred.
    261 static void ExecSubprocess(int* exec_error_fds,
    262                            int* stdout_fds,
    263                            const ExecArgs& exec_args) {
    264   close(exec_error_fds[kReadFD]);  // Don't need this in the child.
    265   close(stdout_fds[kReadFD]);      // Don't need this in the child.
    266   close(1);                        // Close stdout.
    267   dup2(stdout_fds[kWriteFD], 1);   // Dup pipe fd to stdout.
    268   close(stdout_fds[kWriteFD]);     // Don't need the original fd now.
    269   fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
    270   execvp(exec_args.arg0(), exec_args.arg_array());
    271   // Only get here if the exec failed.  Write errno to the parent to tell
    272   // them it went wrong.  If it went well the pipe is closed.
    273   int err = errno;
    274   ssize_t bytes_written;
    275   do {
    276     bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
    277   } while (bytes_written == -1 && errno == EINTR);
    278   // Return (and exit child process).
    279 }
    280 
    281 
    282 // Runs in the parent process.  Checks that the child was able to exec (closing
    283 // the file desriptor), or reports an error if it failed.
    284 static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
    285   ssize_t bytes_read;
    286   int err;
    287   do {
    288     bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
    289   } while (bytes_read == -1 && errno == EINTR);
    290   if (bytes_read != 0) {
    291     isolate->ThrowException(
    292         String::NewFromUtf8(isolate, strerror(err), NewStringType::kNormal)
    293             .ToLocalChecked());
    294     return false;
    295   }
    296   return true;
    297 }
    298 
    299 
    300 // Accumulates the output from the child in a string handle.  Returns true if it
    301 // succeeded or false if an exception was thrown.
    302 static Local<Value> GetStdout(Isolate* isolate, int child_fd,
    303                               const struct timeval& start_time,
    304                               int read_timeout, int total_timeout) {
    305   Local<String> accumulator = String::Empty(isolate);
    306 
    307   int fullness = 0;
    308   static const int kStdoutReadBufferSize = 4096;
    309   char buffer[kStdoutReadBufferSize];
    310 
    311   if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
    312     return isolate->ThrowException(
    313         String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
    314             .ToLocalChecked());
    315   }
    316 
    317   int bytes_read;
    318   do {
    319     bytes_read = static_cast<int>(
    320         read(child_fd, buffer + fullness, kStdoutReadBufferSize - fullness));
    321     if (bytes_read == -1) {
    322       if (errno == EAGAIN) {
    323         if (!WaitOnFD(child_fd,
    324                       read_timeout,
    325                       total_timeout,
    326                       start_time) ||
    327             (TimeIsOut(start_time, total_timeout))) {
    328           return isolate->ThrowException(
    329               String::NewFromUtf8(isolate, "Timed out waiting for output",
    330                                   NewStringType::kNormal).ToLocalChecked());
    331         }
    332         continue;
    333       } else if (errno == EINTR) {
    334         continue;
    335       } else {
    336         break;
    337       }
    338     }
    339     if (bytes_read + fullness > 0) {
    340       int length = bytes_read == 0 ?
    341                    bytes_read + fullness :
    342                    LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
    343       Local<String> addition =
    344           String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
    345               .ToLocalChecked();
    346       accumulator = String::Concat(accumulator, addition);
    347       fullness = bytes_read + fullness - length;
    348       memcpy(buffer, buffer + length, fullness);
    349     }
    350   } while (bytes_read != 0);
    351   return accumulator;
    352 }
    353 
    354 
    355 // Modern Linux has the waitid call, which is like waitpid, but more useful
    356 // if you want a timeout.  If we don't have waitid we can't limit the time
    357 // waiting for the process to exit without losing the information about
    358 // whether it exited normally.  In the common case this doesn't matter because
    359 // we don't get here before the child has closed stdout and most programs don't
    360 // do that before they exit.
    361 //
    362 // We're disabling usage of waitid in Mac OS X because it doens't work for us:
    363 // a parent process hangs on waiting while a child process is already a zombie.
    364 // See http://code.google.com/p/v8/issues/detail?id=401.
    365 #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
    366     && !defined(__NetBSD__)
    367 #if !defined(__FreeBSD__)
    368 #define HAS_WAITID 1
    369 #endif
    370 #endif
    371 
    372 
    373 // Get exit status of child.
    374 static bool WaitForChild(Isolate* isolate,
    375                          int pid,
    376                          ZombieProtector& child_waiter,  // NOLINT
    377                          const struct timeval& start_time,
    378                          int read_timeout,
    379                          int total_timeout) {
    380 #ifdef HAS_WAITID
    381 
    382   siginfo_t child_info;
    383   child_info.si_pid = 0;
    384   int useconds = 1;
    385   // Wait for child to exit.
    386   while (child_info.si_pid == 0) {
    387     waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
    388     usleep(useconds);
    389     if (useconds < 1000000) useconds <<= 1;
    390     if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
    391         (TimeIsOut(start_time, total_timeout))) {
    392       isolate->ThrowException(
    393           String::NewFromUtf8(isolate,
    394                               "Timed out waiting for process to terminate",
    395                               NewStringType::kNormal).ToLocalChecked());
    396       kill(pid, SIGINT);
    397       return false;
    398     }
    399   }
    400   if (child_info.si_code == CLD_KILLED) {
    401     char message[999];
    402     snprintf(message,
    403              sizeof(message),
    404              "Child killed by signal %d",
    405              child_info.si_status);
    406     isolate->ThrowException(
    407         String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    408             .ToLocalChecked());
    409     return false;
    410   }
    411   if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
    412     char message[999];
    413     snprintf(message,
    414              sizeof(message),
    415              "Child exited with status %d",
    416              child_info.si_status);
    417     isolate->ThrowException(
    418         String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    419             .ToLocalChecked());
    420     return false;
    421   }
    422 
    423 #else  // No waitid call.
    424 
    425   int child_status;
    426   waitpid(pid, &child_status, 0);  // We hang here if the child doesn't exit.
    427   child_waiter.ChildIsDeadNow();
    428   if (WIFSIGNALED(child_status)) {
    429     char message[999];
    430     snprintf(message,
    431              sizeof(message),
    432              "Child killed by signal %d",
    433              WTERMSIG(child_status));
    434     isolate->ThrowException(
    435         String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    436             .ToLocalChecked());
    437     return false;
    438   }
    439   if (WEXITSTATUS(child_status) != 0) {
    440     char message[999];
    441     int exit_status = WEXITSTATUS(child_status);
    442     snprintf(message,
    443              sizeof(message),
    444              "Child exited with status %d",
    445              exit_status);
    446     isolate->ThrowException(
    447         String::NewFromUtf8(isolate, message, NewStringType::kNormal)
    448             .ToLocalChecked());
    449     return false;
    450   }
    451 
    452 #endif  // No waitid call.
    453 
    454   return true;
    455 }
    456 
    457 
    458 // Implementation of the system() function (see d8.h for details).
    459 void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
    460   HandleScope scope(args.GetIsolate());
    461   int read_timeout = -1;
    462   int total_timeout = -1;
    463   if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
    464   Local<Array> command_args;
    465   if (args.Length() > 1) {
    466     if (!args[1]->IsArray()) {
    467       args.GetIsolate()->ThrowException(
    468           String::NewFromUtf8(args.GetIsolate(),
    469                               "system: Argument 2 must be an array",
    470                               NewStringType::kNormal).ToLocalChecked());
    471       return;
    472     }
    473     command_args = Local<Array>::Cast(args[1]);
    474   } else {
    475     command_args = Array::New(args.GetIsolate(), 0);
    476   }
    477   if (command_args->Length() > ExecArgs::kMaxArgs) {
    478     args.GetIsolate()->ThrowException(
    479         String::NewFromUtf8(args.GetIsolate(), "Too many arguments to system()",
    480                             NewStringType::kNormal).ToLocalChecked());
    481     return;
    482   }
    483   if (args.Length() < 1) {
    484     args.GetIsolate()->ThrowException(
    485         String::NewFromUtf8(args.GetIsolate(), "Too few arguments to system()",
    486                             NewStringType::kNormal).ToLocalChecked());
    487     return;
    488   }
    489 
    490   struct timeval start_time;
    491   gettimeofday(&start_time, NULL);
    492 
    493   ExecArgs exec_args;
    494   if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
    495     return;
    496   }
    497   int exec_error_fds[2];
    498   int stdout_fds[2];
    499 
    500   if (pipe(exec_error_fds) != 0) {
    501     args.GetIsolate()->ThrowException(
    502         String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
    503                             NewStringType::kNormal).ToLocalChecked());
    504     return;
    505   }
    506   if (pipe(stdout_fds) != 0) {
    507     args.GetIsolate()->ThrowException(
    508         String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
    509                             NewStringType::kNormal).ToLocalChecked());
    510     return;
    511   }
    512 
    513   pid_t pid = fork();
    514   if (pid == 0) {  // Child process.
    515     ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
    516     exit(1);
    517   }
    518 
    519   // Parent process.  Ensure that we clean up if we exit this function early.
    520   ZombieProtector child_waiter(pid);
    521   close(exec_error_fds[kWriteFD]);
    522   close(stdout_fds[kWriteFD]);
    523   OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
    524   OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
    525 
    526   Isolate* isolate = args.GetIsolate();
    527   if (!ChildLaunchedOK(isolate, exec_error_fds)) return;
    528 
    529   Local<Value> accumulator = GetStdout(isolate, stdout_fds[kReadFD], start_time,
    530                                        read_timeout, total_timeout);
    531   if (accumulator->IsUndefined()) {
    532     kill(pid, SIGINT);  // On timeout, kill the subprocess.
    533     args.GetReturnValue().Set(accumulator);
    534     return;
    535   }
    536 
    537   if (!WaitForChild(isolate, pid, child_waiter, start_time, read_timeout,
    538                     total_timeout)) {
    539     return;
    540   }
    541 
    542   args.GetReturnValue().Set(accumulator);
    543 }
    544 
    545 
    546 void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    547   if (args.Length() != 1) {
    548     const char* message = "chdir() takes one argument";
    549     args.GetIsolate()->ThrowException(
    550         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    551             .ToLocalChecked());
    552     return;
    553   }
    554   String::Utf8Value directory(args[0]);
    555   if (*directory == NULL) {
    556     const char* message = "os.chdir(): String conversion of argument failed.";
    557     args.GetIsolate()->ThrowException(
    558         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    559             .ToLocalChecked());
    560     return;
    561   }
    562   if (chdir(*directory) != 0) {
    563     args.GetIsolate()->ThrowException(
    564         String::NewFromUtf8(args.GetIsolate(), strerror(errno),
    565                             NewStringType::kNormal).ToLocalChecked());
    566     return;
    567   }
    568 }
    569 
    570 
    571 void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
    572   if (args.Length() != 1) {
    573     const char* message = "umask() takes one argument";
    574     args.GetIsolate()->ThrowException(
    575         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    576             .ToLocalChecked());
    577     return;
    578   }
    579   if (args[0]->IsNumber()) {
    580     int previous = umask(
    581         args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust());
    582     args.GetReturnValue().Set(previous);
    583     return;
    584   } else {
    585     const char* message = "umask() argument must be numeric";
    586     args.GetIsolate()->ThrowException(
    587         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    588             .ToLocalChecked());
    589     return;
    590   }
    591 }
    592 
    593 
    594 static bool CheckItsADirectory(Isolate* isolate, char* directory) {
    595   struct stat stat_buf;
    596   int stat_result = stat(directory, &stat_buf);
    597   if (stat_result != 0) {
    598     isolate->ThrowException(
    599         String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
    600             .ToLocalChecked());
    601     return false;
    602   }
    603   if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
    604   isolate->ThrowException(
    605       String::NewFromUtf8(isolate, strerror(EEXIST), NewStringType::kNormal)
    606           .ToLocalChecked());
    607   return false;
    608 }
    609 
    610 
    611 // Returns true for success.  Creates intermediate directories as needed.  No
    612 // error if the directory exists already.
    613 static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
    614   int result = mkdir(directory, mask);
    615   if (result == 0) return true;
    616   if (errno == EEXIST) {
    617     return CheckItsADirectory(isolate, directory);
    618   } else if (errno == ENOENT) {  // Intermediate path element is missing.
    619     char* last_slash = strrchr(directory, '/');
    620     if (last_slash == NULL) {
    621       isolate->ThrowException(
    622           String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
    623               .ToLocalChecked());
    624       return false;
    625     }
    626     *last_slash = 0;
    627     if (!mkdirp(isolate, directory, mask)) return false;
    628     *last_slash = '/';
    629     result = mkdir(directory, mask);
    630     if (result == 0) return true;
    631     if (errno == EEXIST) {
    632       return CheckItsADirectory(isolate, directory);
    633     }
    634     isolate->ThrowException(
    635         String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
    636             .ToLocalChecked());
    637     return false;
    638   } else {
    639     isolate->ThrowException(
    640         String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
    641             .ToLocalChecked());
    642     return false;
    643   }
    644 }
    645 
    646 
    647 void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    648   mode_t mask = 0777;
    649   if (args.Length() == 2) {
    650     if (args[1]->IsNumber()) {
    651       mask = args[1]
    652                  ->Int32Value(args.GetIsolate()->GetCurrentContext())
    653                  .FromJust();
    654     } else {
    655       const char* message = "mkdirp() second argument must be numeric";
    656       args.GetIsolate()->ThrowException(
    657           String::NewFromUtf8(args.GetIsolate(), message,
    658                               NewStringType::kNormal).ToLocalChecked());
    659       return;
    660     }
    661   } else if (args.Length() != 1) {
    662     const char* message = "mkdirp() takes one or two arguments";
    663     args.GetIsolate()->ThrowException(
    664         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    665             .ToLocalChecked());
    666     return;
    667   }
    668   String::Utf8Value directory(args[0]);
    669   if (*directory == NULL) {
    670     const char* message = "os.mkdirp(): String conversion of argument failed.";
    671     args.GetIsolate()->ThrowException(
    672         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    673             .ToLocalChecked());
    674     return;
    675   }
    676   mkdirp(args.GetIsolate(), *directory, mask);
    677 }
    678 
    679 
    680 void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    681   if (args.Length() != 1) {
    682     const char* message = "rmdir() takes one or two arguments";
    683     args.GetIsolate()->ThrowException(
    684         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    685             .ToLocalChecked());
    686     return;
    687   }
    688   String::Utf8Value directory(args[0]);
    689   if (*directory == NULL) {
    690     const char* message = "os.rmdir(): String conversion of argument failed.";
    691     args.GetIsolate()->ThrowException(
    692         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    693             .ToLocalChecked());
    694     return;
    695   }
    696   rmdir(*directory);
    697 }
    698 
    699 
    700 void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
    701   if (args.Length() != 2) {
    702     const char* message = "setenv() takes two arguments";
    703     args.GetIsolate()->ThrowException(
    704         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    705             .ToLocalChecked());
    706     return;
    707   }
    708   String::Utf8Value var(args[0]);
    709   String::Utf8Value value(args[1]);
    710   if (*var == NULL) {
    711     const char* message =
    712         "os.setenv(): String conversion of variable name failed.";
    713     args.GetIsolate()->ThrowException(
    714         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    715             .ToLocalChecked());
    716     return;
    717   }
    718   if (*value == NULL) {
    719     const char* message =
    720         "os.setenv(): String conversion of variable contents failed.";
    721     args.GetIsolate()->ThrowException(
    722         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    723             .ToLocalChecked());
    724     return;
    725   }
    726   setenv(*var, *value, 1);
    727 }
    728 
    729 
    730 void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
    731   if (args.Length() != 1) {
    732     const char* message = "unsetenv() takes one argument";
    733     args.GetIsolate()->ThrowException(
    734         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    735             .ToLocalChecked());
    736     return;
    737   }
    738   String::Utf8Value var(args[0]);
    739   if (*var == NULL) {
    740     const char* message =
    741         "os.setenv(): String conversion of variable name failed.";
    742     args.GetIsolate()->ThrowException(
    743         String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
    744             .ToLocalChecked());
    745     return;
    746   }
    747   unsetenv(*var);
    748 }
    749 
    750 
    751 void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) {
    752   os_templ->Set(String::NewFromUtf8(isolate, "system", NewStringType::kNormal)
    753                     .ToLocalChecked(),
    754                 FunctionTemplate::New(isolate, System));
    755   os_templ->Set(String::NewFromUtf8(isolate, "chdir", NewStringType::kNormal)
    756                     .ToLocalChecked(),
    757                 FunctionTemplate::New(isolate, ChangeDirectory));
    758   os_templ->Set(String::NewFromUtf8(isolate, "setenv", NewStringType::kNormal)
    759                     .ToLocalChecked(),
    760                 FunctionTemplate::New(isolate, SetEnvironment));
    761   os_templ->Set(String::NewFromUtf8(isolate, "unsetenv", NewStringType::kNormal)
    762                     .ToLocalChecked(),
    763                 FunctionTemplate::New(isolate, UnsetEnvironment));
    764   os_templ->Set(String::NewFromUtf8(isolate, "umask", NewStringType::kNormal)
    765                     .ToLocalChecked(),
    766                 FunctionTemplate::New(isolate, SetUMask));
    767   os_templ->Set(String::NewFromUtf8(isolate, "mkdirp", NewStringType::kNormal)
    768                     .ToLocalChecked(),
    769                 FunctionTemplate::New(isolate, MakeDirectory));
    770   os_templ->Set(String::NewFromUtf8(isolate, "rmdir", NewStringType::kNormal)
    771                     .ToLocalChecked(),
    772                 FunctionTemplate::New(isolate, RemoveDirectory));
    773 }
    774 
    775 void Shell::Exit(int exit_code) {
    776   // Use _exit instead of exit to avoid races between isolate
    777   // threads and static destructors.
    778   fflush(stdout);
    779   fflush(stderr);
    780   _exit(exit_code);
    781 }
    782 
    783 }  // namespace v8
    784