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     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;
    107 #if V8_OS_NACL
    108   // PNaCL has no support for select.
    109   int number_of_fds_ready = -1;
    110 #else
    111   int number_of_fds_ready = select(fd + 1,
    112                                    &readfds,
    113                                    &writefds,
    114                                    &exceptfds,
    115                                    read_timeout != -1 ? &timeout : NULL);
    116 #endif
    117   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.
    123 static 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'.
    144 class 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.
    155 class 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.
    167 class ExecArgs {
    168  public:
    169   ExecArgs() {
    170     exec_args_[0] = NULL;
    171   }
    172   bool Init(Isolate* isolate, Handle<Value> arg0, Handle<Array> command_args) {
    173     String::Utf8Value prog(arg0);
    174     if (*prog == NULL) {
    175       const char* message =
    176           "os.system(): String conversion of program name failed";
    177       isolate->ThrowException(String::NewFromUtf8(isolate, message));
    178       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++) {
    186       Handle<Value> arg(command_args->Get(Integer::New(isolate, j)));
    187       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.";
    192         isolate->ThrowException(String::NewFromUtf8(isolate, message));
    193         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;
    213   char* const* arg_array() const { return exec_args_; }
    214   const char* arg0() const { return exec_args_[0]; }
    215 
    216  private:
    217   char* exec_args_[kMaxArgs + 1];
    218 };
    219 
    220 
    221 // Gets the optional timeouts from the arguments to the system() call.
    222 static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
    223                         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 {
    229       args.GetIsolate()->ThrowException(String::NewFromUtf8(
    230           args.GetIsolate(), "system: Argument 4 must be a number"));
    231       return false;
    232     }
    233   }
    234   if (args.Length() > 2) {
    235     if (args[2]->IsNumber()) {
    236       *read_timeout = args[2]->Int32Value();
    237     } else {
    238       args.GetIsolate()->ThrowException(String::NewFromUtf8(
    239           args.GetIsolate(), "system: Argument 3 must be a number"));
    240       return false;
    241     }
    242   }
    243   return true;
    244 }
    245 
    246 
    247 static const int kReadFD = 0;
    248 static 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.
    254 static void ExecSubprocess(int* exec_error_fds,
    255                            int* stdout_fds,
    256                            const ExecArgs& exec_args) {
    257   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.
    277 static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
    278   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) {
    284     isolate->ThrowException(String::NewFromUtf8(isolate, strerror(err)));
    285     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.
    293 static Handle<Value> GetStdout(Isolate* isolate,
    294                                int child_fd,
    295                                const struct timeval& start_time,
    296                                int read_timeout,
    297                                int total_timeout) {
    298   Handle<String> accumulator = String::Empty(isolate);
    299 
    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) {
    305     return isolate->ThrowException(
    306         String::NewFromUtf8(isolate, strerror(errno)));
    307   }
    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))) {
    321           return isolate->ThrowException(
    322               String::NewFromUtf8(isolate, "Timed out waiting for output"));
    323         }
    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);
    335       Handle<String> addition =
    336           String::NewFromUtf8(isolate, buffer, String::kNormalString, length);
    337       accumulator = String::Concat(accumulator, addition);
    338       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.
    356 #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
    357     && !defined(__NetBSD__)
    358 #if !defined(__FreeBSD__)
    359 #define HAS_WAITID 1
    360 #endif
    361 #endif
    362 
    363 
    364 // Get exit status of child.
    365 static bool WaitForChild(Isolate* isolate,
    366                          int pid,
    367                          ZombieProtector& child_waiter,  // NOLINT
    368                          const struct timeval& start_time,
    369                          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))) {
    383       isolate->ThrowException(String::NewFromUtf8(
    384           isolate, "Timed out waiting for process to terminate"));
    385       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);
    395     isolate->ThrowException(String::NewFromUtf8(isolate, message));
    396     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);
    404     isolate->ThrowException(String::NewFromUtf8(isolate, message));
    405     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));
    419     isolate->ThrowException(String::NewFromUtf8(isolate, message));
    420     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);
    429     isolate->ThrowException(String::NewFromUtf8(isolate, message));
    430     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).
    440 void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
    441   HandleScope scope(args.GetIsolate());
    442   int read_timeout = -1;
    443   int total_timeout = -1;
    444   if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
    445   Handle<Array> command_args;
    446   if (args.Length() > 1) {
    447     if (!args[1]->IsArray()) {
    448       args.GetIsolate()->ThrowException(String::NewFromUtf8(
    449           args.GetIsolate(), "system: Argument 2 must be an array"));
    450       return;
    451     }
    452     command_args = Handle<Array>::Cast(args[1]);
    453   } else {
    454     command_args = Array::New(args.GetIsolate(), 0);
    455   }
    456   if (command_args->Length() > ExecArgs::kMaxArgs) {
    457     args.GetIsolate()->ThrowException(String::NewFromUtf8(
    458         args.GetIsolate(), "Too many arguments to system()"));
    459     return;
    460   }
    461   if (args.Length() < 1) {
    462     args.GetIsolate()->ThrowException(String::NewFromUtf8(
    463         args.GetIsolate(), "Too few arguments to system()"));
    464     return;
    465   }
    466 
    467   struct timeval start_time;
    468   gettimeofday(&start_time, NULL);
    469 
    470   ExecArgs exec_args;
    471   if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
    472     return;
    473   }
    474   int exec_error_fds[2];
    475   int stdout_fds[2];
    476 
    477   if (pipe(exec_error_fds) != 0) {
    478     args.GetIsolate()->ThrowException(
    479         String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
    480     return;
    481   }
    482   if (pipe(stdout_fds) != 0) {
    483     args.GetIsolate()->ThrowException(
    484         String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
    485     return;
    486   }
    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 
    501   if (!ChildLaunchedOK(args.GetIsolate(), exec_error_fds)) return;
    502 
    503   Handle<Value> accumulator = GetStdout(args.GetIsolate(),
    504                                         stdout_fds[kReadFD],
    505                                         start_time,
    506                                         read_timeout,
    507                                         total_timeout);
    508   if (accumulator->IsUndefined()) {
    509     kill(pid, SIGINT);  // On timeout, kill the subprocess.
    510     args.GetReturnValue().Set(accumulator);
    511     return;
    512   }
    513 
    514   if (!WaitForChild(args.GetIsolate(),
    515                     pid,
    516                     child_waiter,
    517                     start_time,
    518                     read_timeout,
    519                     total_timeout)) {
    520     return;
    521   }
    522 
    523   args.GetReturnValue().Set(accumulator);
    524 }
    525 
    526 
    527 void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    528   if (args.Length() != 1) {
    529     const char* message = "chdir() takes one argument";
    530     args.GetIsolate()->ThrowException(
    531         String::NewFromUtf8(args.GetIsolate(), message));
    532     return;
    533   }
    534   String::Utf8Value directory(args[0]);
    535   if (*directory == NULL) {
    536     const char* message = "os.chdir(): String conversion of argument failed.";
    537     args.GetIsolate()->ThrowException(
    538         String::NewFromUtf8(args.GetIsolate(), message));
    539     return;
    540   }
    541   if (chdir(*directory) != 0) {
    542     args.GetIsolate()->ThrowException(
    543         String::NewFromUtf8(args.GetIsolate(), strerror(errno)));
    544     return;
    545   }
    546 }
    547 
    548 
    549 void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
    550   if (args.Length() != 1) {
    551     const char* message = "umask() takes one argument";
    552     args.GetIsolate()->ThrowException(
    553         String::NewFromUtf8(args.GetIsolate(), message));
    554     return;
    555   }
    556   if (args[0]->IsNumber()) {
    557 #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;
    565   } else {
    566     const char* message = "umask() argument must be numeric";
    567     args.GetIsolate()->ThrowException(
    568         String::NewFromUtf8(args.GetIsolate(), message));
    569     return;
    570   }
    571 }
    572 
    573 
    574 static bool CheckItsADirectory(Isolate* isolate, char* directory) {
    575   struct stat stat_buf;
    576   int stat_result = stat(directory, &stat_buf);
    577   if (stat_result != 0) {
    578     isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
    579     return false;
    580   }
    581   if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
    582   isolate->ThrowException(String::NewFromUtf8(isolate, strerror(EEXIST)));
    583   return false;
    584 }
    585 
    586 
    587 // Returns true for success.  Creates intermediate directories as needed.  No
    588 // error if the directory exists already.
    589 static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
    590   int result = mkdir(directory, mask);
    591   if (result == 0) return true;
    592   if (errno == EEXIST) {
    593     return CheckItsADirectory(isolate, directory);
    594   } else if (errno == ENOENT) {  // Intermediate path element is missing.
    595     char* last_slash = strrchr(directory, '/');
    596     if (last_slash == NULL) {
    597       isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
    598       return false;
    599     }
    600     *last_slash = 0;
    601     if (!mkdirp(isolate, directory, mask)) return false;
    602     *last_slash = '/';
    603     result = mkdir(directory, mask);
    604     if (result == 0) return true;
    605     if (errno == EEXIST) {
    606       return CheckItsADirectory(isolate, directory);
    607     }
    608     isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
    609     return false;
    610   } else {
    611     isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
    612     return false;
    613   }
    614 }
    615 
    616 
    617 void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    618   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";
    624       args.GetIsolate()->ThrowException(
    625           String::NewFromUtf8(args.GetIsolate(), message));
    626       return;
    627     }
    628   } else if (args.Length() != 1) {
    629     const char* message = "mkdirp() takes one or two arguments";
    630     args.GetIsolate()->ThrowException(
    631         String::NewFromUtf8(args.GetIsolate(), message));
    632     return;
    633   }
    634   String::Utf8Value directory(args[0]);
    635   if (*directory == NULL) {
    636     const char* message = "os.mkdirp(): String conversion of argument failed.";
    637     args.GetIsolate()->ThrowException(
    638         String::NewFromUtf8(args.GetIsolate(), message));
    639     return;
    640   }
    641   mkdirp(args.GetIsolate(), *directory, mask);
    642 }
    643 
    644 
    645 void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
    646   if (args.Length() != 1) {
    647     const char* message = "rmdir() takes one or two arguments";
    648     args.GetIsolate()->ThrowException(
    649         String::NewFromUtf8(args.GetIsolate(), message));
    650     return;
    651   }
    652   String::Utf8Value directory(args[0]);
    653   if (*directory == NULL) {
    654     const char* message = "os.rmdir(): String conversion of argument failed.";
    655     args.GetIsolate()->ThrowException(
    656         String::NewFromUtf8(args.GetIsolate(), message));
    657     return;
    658   }
    659   rmdir(*directory);
    660 }
    661 
    662 
    663 void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
    664   if (args.Length() != 2) {
    665     const char* message = "setenv() takes two arguments";
    666     args.GetIsolate()->ThrowException(
    667         String::NewFromUtf8(args.GetIsolate(), message));
    668     return;
    669   }
    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.";
    675     args.GetIsolate()->ThrowException(
    676         String::NewFromUtf8(args.GetIsolate(), message));
    677     return;
    678   }
    679   if (*value == NULL) {
    680     const char* message =
    681         "os.setenv(): String conversion of variable contents failed.";
    682     args.GetIsolate()->ThrowException(
    683         String::NewFromUtf8(args.GetIsolate(), message));
    684     return;
    685   }
    686   setenv(*var, *value, 1);
    687 }
    688 
    689 
    690 void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
    691   if (args.Length() != 1) {
    692     const char* message = "unsetenv() takes one argument";
    693     args.GetIsolate()->ThrowException(
    694         String::NewFromUtf8(args.GetIsolate(), message));
    695     return;
    696   }
    697   String::Utf8Value var(args[0]);
    698   if (*var == NULL) {
    699     const char* message =
    700         "os.setenv(): String conversion of variable name failed.";
    701     args.GetIsolate()->ThrowException(
    702         String::NewFromUtf8(args.GetIsolate(), message));
    703     return;
    704   }
    705   unsetenv(*var);
    706 }
    707 
    708 
    709 void 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));
    724 }
    725 
    726 }  // namespace v8
    727