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 #define HAS_WAITID 1
    379 #endif
    380 
    381 
    382 // Get exit status of child.
    383 static bool WaitForChild(int pid,
    384                          ZombieProtector& child_waiter,
    385                          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       ThrowException(String::New("Timed out waiting for process to terminate"));
    401       kill(pid, SIGINT);
    402       return false;
    403     }
    404   }
    405   if (child_info.si_code == CLD_KILLED) {
    406     char message[999];
    407     snprintf(message,
    408              sizeof(message),
    409              "Child killed by signal %d",
    410              child_info.si_status);
    411     ThrowException(String::New(message));
    412     return false;
    413   }
    414   if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
    415     char message[999];
    416     snprintf(message,
    417              sizeof(message),
    418              "Child exited with status %d",
    419              child_info.si_status);
    420     ThrowException(String::New(message));
    421     return false;
    422   }
    423 
    424 #else  // No waitid call.
    425 
    426   int child_status;
    427   waitpid(pid, &child_status, 0);  // We hang here if the child doesn't exit.
    428   child_waiter.ChildIsDeadNow();
    429   if (WIFSIGNALED(child_status)) {
    430     char message[999];
    431     snprintf(message,
    432              sizeof(message),
    433              "Child killed by signal %d",
    434              WTERMSIG(child_status));
    435     ThrowException(String::New(message));
    436     return false;
    437   }
    438   if (WEXITSTATUS(child_status) != 0) {
    439     char message[999];
    440     int exit_status = WEXITSTATUS(child_status);
    441     snprintf(message,
    442              sizeof(message),
    443              "Child exited with status %d",
    444              exit_status);
    445     ThrowException(String::New(message));
    446     return false;
    447   }
    448 
    449 #endif  // No waitid call.
    450 
    451   return true;
    452 }
    453 
    454 
    455 // Implementation of the system() function (see d8.h for details).
    456 Handle<Value> Shell::System(const Arguments& args) {
    457   HandleScope scope;
    458   int read_timeout = -1;
    459   int total_timeout = -1;
    460   if (!GetTimeouts(args, &read_timeout, &total_timeout)) return v8::Undefined();
    461   Handle<Array> command_args;
    462   if (args.Length() > 1) {
    463     if (!args[1]->IsArray()) {
    464       return ThrowException(String::New("system: Argument 2 must be an array"));
    465     }
    466     command_args = Handle<Array>::Cast(args[1]);
    467   } else {
    468     command_args = Array::New(0);
    469   }
    470   if (command_args->Length() > ExecArgs::kMaxArgs) {
    471     return ThrowException(String::New("Too many arguments to system()"));
    472   }
    473   if (args.Length() < 1) {
    474     return ThrowException(String::New("Too few arguments to system()"));
    475   }
    476 
    477   struct timeval start_time;
    478   gettimeofday(&start_time, NULL);
    479 
    480   ExecArgs exec_args;
    481   if (!exec_args.Init(args[0], command_args)) {
    482     return v8::Undefined();
    483   }
    484   int exec_error_fds[2];
    485   int stdout_fds[2];
    486 
    487   if (pipe(exec_error_fds) != 0) {
    488     return ThrowException(String::New("pipe syscall failed."));
    489   }
    490   if (pipe(stdout_fds) != 0) {
    491     return ThrowException(String::New("pipe syscall failed."));
    492   }
    493 
    494   pid_t pid = fork();
    495   if (pid == 0) {  // Child process.
    496     ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
    497     exit(1);
    498   }
    499 
    500   // Parent process.  Ensure that we clean up if we exit this function early.
    501   ZombieProtector child_waiter(pid);
    502   close(exec_error_fds[kWriteFD]);
    503   close(stdout_fds[kWriteFD]);
    504   OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
    505   OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
    506 
    507   if (!ChildLaunchedOK(exec_error_fds)) return v8::Undefined();
    508 
    509   Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD],
    510                                         start_time,
    511                                         read_timeout,
    512                                         total_timeout);
    513   if (accumulator->IsUndefined()) {
    514     kill(pid, SIGINT);  // On timeout, kill the subprocess.
    515     return accumulator;
    516   }
    517 
    518   if (!WaitForChild(pid,
    519                     child_waiter,
    520                     start_time,
    521                     read_timeout,
    522                     total_timeout)) {
    523     return v8::Undefined();
    524   }
    525 
    526   return scope.Close(accumulator);
    527 }
    528 
    529 
    530 Handle<Value> Shell::ChangeDirectory(const Arguments& args) {
    531   if (args.Length() != 1) {
    532     const char* message = "chdir() takes one argument";
    533     return ThrowException(String::New(message));
    534   }
    535   String::Utf8Value directory(args[0]);
    536   if (*directory == NULL) {
    537     const char* message = "os.chdir(): String conversion of argument failed.";
    538     return ThrowException(String::New(message));
    539   }
    540   if (chdir(*directory) != 0) {
    541     return ThrowException(String::New(strerror(errno)));
    542   }
    543   return v8::Undefined();
    544 }
    545 
    546 
    547 Handle<Value> Shell::SetUMask(const Arguments& args) {
    548   if (args.Length() != 1) {
    549     const char* message = "umask() takes one argument";
    550     return ThrowException(String::New(message));
    551   }
    552   if (args[0]->IsNumber()) {
    553     mode_t mask = args[0]->Int32Value();
    554     int previous = umask(mask);
    555     return Number::New(previous);
    556   } else {
    557     const char* message = "umask() argument must be numeric";
    558     return ThrowException(String::New(message));
    559   }
    560 }
    561 
    562 
    563 static bool CheckItsADirectory(char* directory) {
    564   struct stat stat_buf;
    565   int stat_result = stat(directory, &stat_buf);
    566   if (stat_result != 0) {
    567     ThrowException(String::New(strerror(errno)));
    568     return false;
    569   }
    570   if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
    571   ThrowException(String::New(strerror(EEXIST)));
    572   return false;
    573 }
    574 
    575 
    576 // Returns true for success.  Creates intermediate directories as needed.  No
    577 // error if the directory exists already.
    578 static bool mkdirp(char* directory, mode_t mask) {
    579   int result = mkdir(directory, mask);
    580   if (result == 0) return true;
    581   if (errno == EEXIST) {
    582     return CheckItsADirectory(directory);
    583   } else if (errno == ENOENT) {  // Intermediate path element is missing.
    584     char* last_slash = strrchr(directory, '/');
    585     if (last_slash == NULL) {
    586       ThrowException(String::New(strerror(errno)));
    587       return false;
    588     }
    589     *last_slash = 0;
    590     if (!mkdirp(directory, mask)) return false;
    591     *last_slash = '/';
    592     result = mkdir(directory, mask);
    593     if (result == 0) return true;
    594     if (errno == EEXIST) {
    595       return CheckItsADirectory(directory);
    596     }
    597     ThrowException(String::New(strerror(errno)));
    598     return false;
    599   } else {
    600     ThrowException(String::New(strerror(errno)));
    601     return false;
    602   }
    603 }
    604 
    605 
    606 Handle<Value> Shell::MakeDirectory(const Arguments& args) {
    607   mode_t mask = 0777;
    608   if (args.Length() == 2) {
    609     if (args[1]->IsNumber()) {
    610       mask = args[1]->Int32Value();
    611     } else {
    612       const char* message = "mkdirp() second argument must be numeric";
    613       return ThrowException(String::New(message));
    614     }
    615   } else if (args.Length() != 1) {
    616     const char* message = "mkdirp() takes one or two arguments";
    617     return ThrowException(String::New(message));
    618   }
    619   String::Utf8Value directory(args[0]);
    620   if (*directory == NULL) {
    621     const char* message = "os.mkdirp(): String conversion of argument failed.";
    622     return ThrowException(String::New(message));
    623   }
    624   mkdirp(*directory, mask);
    625   return v8::Undefined();
    626 }
    627 
    628 
    629 Handle<Value> Shell::RemoveDirectory(const Arguments& args) {
    630   if (args.Length() != 1) {
    631     const char* message = "rmdir() takes one or two arguments";
    632     return ThrowException(String::New(message));
    633   }
    634   String::Utf8Value directory(args[0]);
    635   if (*directory == NULL) {
    636     const char* message = "os.rmdir(): String conversion of argument failed.";
    637     return ThrowException(String::New(message));
    638   }
    639   rmdir(*directory);
    640   return v8::Undefined();
    641 }
    642 
    643 
    644 Handle<Value> Shell::SetEnvironment(const Arguments& args) {
    645   if (args.Length() != 2) {
    646     const char* message = "setenv() takes two arguments";
    647     return ThrowException(String::New(message));
    648   }
    649   String::Utf8Value var(args[0]);
    650   String::Utf8Value value(args[1]);
    651   if (*var == NULL) {
    652     const char* message =
    653         "os.setenv(): String conversion of variable name failed.";
    654     return ThrowException(String::New(message));
    655   }
    656   if (*value == NULL) {
    657     const char* message =
    658         "os.setenv(): String conversion of variable contents failed.";
    659     return ThrowException(String::New(message));
    660   }
    661   setenv(*var, *value, 1);
    662   return v8::Undefined();
    663 }
    664 
    665 
    666 void Shell::AddOSMethods(Handle<ObjectTemplate> os_templ) {
    667   os_templ->Set(String::New("system"), FunctionTemplate::New(System));
    668   os_templ->Set(String::New("chdir"), FunctionTemplate::New(ChangeDirectory));
    669   os_templ->Set(String::New("setenv"), FunctionTemplate::New(SetEnvironment));
    670   os_templ->Set(String::New("umask"), FunctionTemplate::New(SetUMask));
    671   os_templ->Set(String::New("mkdirp"), FunctionTemplate::New(MakeDirectory));
    672   os_templ->Set(String::New("rmdir"), FunctionTemplate::New(RemoveDirectory));
    673 }
    674 
    675 }  // namespace v8
    676