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