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