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