d8-posix.cc 22.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
32
#include <sys/stat.h>
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>


#include "d8.h"
#include "d8-debug.h"
#include "debug.h"


namespace v8 {


// If the buffer ends in the middle of a UTF-8 sequence then we return
// the length of the string up to but not including the incomplete UTF-8
// sequence.  If the buffer ends with a valid UTF-8 sequence then we
// return the whole buffer.
static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
  int answer = len;
  // 1-byte encoding.
  static const int kUtf8SingleByteMask = 0x80;
  static const int kUtf8SingleByteValue = 0x00;
  // 2-byte encoding.
  static const int kUtf8TwoByteMask = 0xe0;
  static const int kUtf8TwoByteValue = 0xc0;
  // 3-byte encoding.
  static const int kUtf8ThreeByteMask = 0xf0;
  static const int kUtf8ThreeByteValue = 0xe0;
  // 4-byte encoding.
  static const int kUtf8FourByteMask = 0xf8;
  static const int kUtf8FourByteValue = 0xf0;
  // Subsequent bytes of a multi-byte encoding.
  static const int kMultiByteMask = 0xc0;
  static const int kMultiByteValue = 0x80;
  int multi_byte_bytes_seen = 0;
  while (answer > 0) {
    int c = buffer[answer - 1];
    // Ends in valid single-byte sequence?
    if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
    // Ends in one or more subsequent bytes of a multi-byte value?
    if ((c & kMultiByteMask) == kMultiByteValue) {
      multi_byte_bytes_seen++;
      answer--;
    } else {
      if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
        if (multi_byte_bytes_seen >= 1) {
          return answer + 2;
        }
        return answer - 1;
      } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
        if (multi_byte_bytes_seen >= 2) {
          return answer + 3;
        }
        return answer - 1;
      } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
        if (multi_byte_bytes_seen >= 3) {
          return answer + 4;
        }
        return answer - 1;
      } else {
        return answer;  // Malformed UTF-8.
      }
    }
  }
  return 0;
}


// Suspends the thread until there is data available from the child process.
// Returns false on timeout, true on data ready.
static bool WaitOnFD(int fd,
                     int read_timeout,
108
                     int total_timeout,
109 110 111
                     struct timeval& start_time) {
  fd_set readfds, writefds, exceptfds;
  struct timeval timeout;
112 113
  int gone = 0;
  if (total_timeout != -1) {
114 115 116
    struct timeval time_now;
    gettimeofday(&time_now, NULL);
    int seconds = time_now.tv_sec - start_time.tv_sec;
117 118
    gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
    if (gone >= total_timeout) return false;
119 120 121 122 123 124 125
  }
  FD_ZERO(&readfds);
  FD_ZERO(&writefds);
  FD_ZERO(&exceptfds);
  FD_SET(fd, &readfds);
  FD_SET(fd, &exceptfds);
  if (read_timeout == -1 ||
126 127
      (total_timeout != -1 && total_timeout - gone < read_timeout)) {
    read_timeout = total_timeout - gone;
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
  }
  timeout.tv_usec = (read_timeout % 1000) * 1000;
  timeout.tv_sec = read_timeout / 1000;
  int number_of_fds_ready = select(fd + 1,
                                   &readfds,
                                   &writefds,
                                   &exceptfds,
                                   read_timeout != -1 ? &timeout : NULL);
  return number_of_fds_ready == 1;
}


// Checks whether we ran out of time on the timeout.  Returns true if we ran out
// of time, false if we still have time.
static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
  if (total_time == -1) return false;
  struct timeval time_now;
  gettimeofday(&time_now, NULL);
  // Careful about overflow.
  int seconds = time_now.tv_sec - start_time.tv_sec;
  if (seconds > 100) {
    if (seconds * 1000 > total_time) return true;
    return false;
  }
  int useconds = time_now.tv_usec - start_time.tv_usec;
  if (seconds * 1000000 + useconds > total_time * 1000) {
    return true;
  }
  return false;
}


// A utility class that does a non-hanging waitpid on the child process if we
// bail out of the System() function early.  If you don't ever do a waitpid on
// a subprocess then it turns into one of those annoying 'zombie processes'.
class ZombieProtector {
 public:
  explicit ZombieProtector(int pid): pid_(pid) { }
166
  ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
  void ChildIsDeadNow() { pid_ = 0; }
 private:
  int pid_;
};


// A utility class that closes a file descriptor when it goes out of scope.
class OpenFDCloser {
 public:
  explicit OpenFDCloser(int fd): fd_(fd) { }
  ~OpenFDCloser() { close(fd_); }
 private:
  int fd_;
};


// A utility class that takes the array of command arguments and puts then in an
// array of new[]ed UTF-8 C strings.  Deallocates them again when it goes out of
// scope.
class ExecArgs {
 public:
188 189 190 191
  ExecArgs() {
    exec_args_[0] = NULL;
  }
  bool Init(Handle<Value> arg0, Handle<Array> command_args) {
192
    String::Utf8Value prog(arg0);
193 194 195 196 197 198 199
    if (*prog == NULL) {
      const char* message =
          "os.system(): String conversion of program name failed";
      ThrowException(String::New(message));
      return false;
    }
    int len = prog.length() + 3;
200 201 202 203 204 205 206
    char* c_arg = new char[len];
    snprintf(c_arg, len, "%s", *prog);
    exec_args_[0] = c_arg;
    int i = 1;
    for (unsigned j = 0; j < command_args->Length(); i++, j++) {
      Handle<Value> arg(command_args->Get(Integer::New(j)));
      String::Utf8Value utf8_arg(arg);
207 208 209 210 211 212 213
      if (*utf8_arg == NULL) {
        exec_args_[i] = NULL;  // Consistent state for destructor.
        const char* message =
            "os.system(): String conversion of argument failed.";
        ThrowException(String::New(message));
        return false;
      }
214 215 216 217 218 219
      int len = utf8_arg.length() + 1;
      char* c_arg = new char[len];
      snprintf(c_arg, len, "%s", *utf8_arg);
      exec_args_[i] = c_arg;
    }
    exec_args_[i] = NULL;
220
    return true;
221 222 223 224 225 226 227 228 229 230 231 232 233
  }
  ~ExecArgs() {
    for (unsigned i = 0; i < kMaxArgs; i++) {
      if (exec_args_[i] == NULL) {
        return;
      }
      delete [] exec_args_[i];
      exec_args_[i] = 0;
    }
  }
  static const unsigned kMaxArgs = 1000;
  char** arg_array() { return exec_args_; }
  char* arg0() { return exec_args_[0]; }
234

235 236 237 238 239 240
 private:
  char* exec_args_[kMaxArgs + 1];
};


// Gets the optional timeouts from the arguments to the system() call.
241
static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
242 243 244 245 246 247
                        int* read_timeout,
                        int* total_timeout) {
  if (args.Length() > 3) {
    if (args[3]->IsNumber()) {
      *total_timeout = args[3]->Int32Value();
    } else {
248 249
      args.GetIsolate()->ThrowException(
          String::New("system: Argument 4 must be a number"));
250 251 252 253 254 255 256
      return false;
    }
  }
  if (args.Length() > 2) {
    if (args[2]->IsNumber()) {
      *read_timeout = args[2]->Int32Value();
    } else {
257 258
      args.GetIsolate()->ThrowException(
          String::New("system: Argument 3 must be a number"));
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
      return false;
    }
  }
  return true;
}


static const int kReadFD = 0;
static const int kWriteFD = 1;


// This is run in the child process after fork() but before exec().  It normally
// ends with the child process being replaced with the desired child program.
// It only returns if an error occurred.
static void ExecSubprocess(int* exec_error_fds,
                           int* stdout_fds,
                           ExecArgs& exec_args) {
  close(exec_error_fds[kReadFD]);  // Don't need this in the child.
  close(stdout_fds[kReadFD]);      // Don't need this in the child.
  close(1);                        // Close stdout.
  dup2(stdout_fds[kWriteFD], 1);   // Dup pipe fd to stdout.
  close(stdout_fds[kWriteFD]);     // Don't need the original fd now.
  fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
  execvp(exec_args.arg0(), exec_args.arg_array());
  // Only get here if the exec failed.  Write errno to the parent to tell
  // them it went wrong.  If it went well the pipe is closed.
  int err = errno;
286 287 288 289
  int bytes_written;
  do {
    bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
  } while (bytes_written == -1 && errno == EINTR);
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
  // Return (and exit child process).
}


// Runs in the parent process.  Checks that the child was able to exec (closing
// the file desriptor), or reports an error if it failed.
static bool ChildLaunchedOK(int* exec_error_fds) {
  int bytes_read;
  int err;
  do {
    bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
  } while (bytes_read == -1 && errno == EINTR);
  if (bytes_read != 0) {
    ThrowException(String::New(strerror(err)));
    return false;
  }
  return true;
}


// Accumulates the output from the child in a string handle.  Returns true if it
// succeeded or false if an exception was thrown.
static Handle<Value> GetStdout(int child_fd,
                               struct timeval& start_time,
                               int read_timeout,
315
                               int total_timeout) {
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
  Handle<String> accumulator = String::Empty();

  int fullness = 0;
  static const int kStdoutReadBufferSize = 4096;
  char buffer[kStdoutReadBufferSize];

  if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
    return ThrowException(String::New(strerror(errno)));
  }

  int bytes_read;
  do {
    bytes_read = read(child_fd,
                      buffer + fullness,
                      kStdoutReadBufferSize - fullness);
    if (bytes_read == -1) {
      if (errno == EAGAIN) {
        if (!WaitOnFD(child_fd,
                      read_timeout,
                      total_timeout,
                      start_time) ||
337
            (TimeIsOut(start_time, total_timeout))) {
338 339 340 341 342 343 344 345 346 347 348 349 350 351
          return ThrowException(String::New("Timed out waiting for output"));
        }
        continue;
      } else if (errno == EINTR) {
        continue;
      } else {
        break;
      }
    }
    if (bytes_read + fullness > 0) {
      int length = bytes_read == 0 ?
                   bytes_read + fullness :
                   LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
      Handle<String> addition = String::New(buffer, length);
352
      accumulator = String::Concat(accumulator, addition);
353 354 355 356 357 358 359 360 361 362 363 364 365 366
      fullness = bytes_read + fullness - length;
      memcpy(buffer, buffer + length, fullness);
    }
  } while (bytes_read != 0);
  return accumulator;
}


// Modern Linux has the waitid call, which is like waitpid, but more useful
// if you want a timeout.  If we don't have waitid we can't limit the time
// waiting for the process to exit without losing the information about
// whether it exited normally.  In the common case this doesn't matter because
// we don't get here before the child has closed stdout and most programs don't
// do that before they exit.
367 368 369 370
//
// We're disabling usage of waitid in Mac OS X because it doens't work for us:
// a parent process hangs on waiting while a child process is already a zombie.
// See http://code.google.com/p/v8/issues/detail?id=401.
371 372 373
#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
    && !defined(__NetBSD__)
#if !defined(__FreeBSD__)
374 375
#define HAS_WAITID 1
#endif
376
#endif
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452


// Get exit status of child.
static bool WaitForChild(int pid,
                         ZombieProtector& child_waiter,
                         struct timeval& start_time,
                         int read_timeout,
                         int total_timeout) {
#ifdef HAS_WAITID

  siginfo_t child_info;
  child_info.si_pid = 0;
  int useconds = 1;
  // Wait for child to exit.
  while (child_info.si_pid == 0) {
    waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
    usleep(useconds);
    if (useconds < 1000000) useconds <<= 1;
    if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
        (TimeIsOut(start_time, total_timeout))) {
      ThrowException(String::New("Timed out waiting for process to terminate"));
      kill(pid, SIGINT);
      return false;
    }
  }
  if (child_info.si_code == CLD_KILLED) {
    char message[999];
    snprintf(message,
             sizeof(message),
             "Child killed by signal %d",
             child_info.si_status);
    ThrowException(String::New(message));
    return false;
  }
  if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
    char message[999];
    snprintf(message,
             sizeof(message),
             "Child exited with status %d",
             child_info.si_status);
    ThrowException(String::New(message));
    return false;
  }

#else  // No waitid call.

  int child_status;
  waitpid(pid, &child_status, 0);  // We hang here if the child doesn't exit.
  child_waiter.ChildIsDeadNow();
  if (WIFSIGNALED(child_status)) {
    char message[999];
    snprintf(message,
             sizeof(message),
             "Child killed by signal %d",
             WTERMSIG(child_status));
    ThrowException(String::New(message));
    return false;
  }
  if (WEXITSTATUS(child_status) != 0) {
    char message[999];
    int exit_status = WEXITSTATUS(child_status);
    snprintf(message,
             sizeof(message),
             "Child exited with status %d",
             exit_status);
    ThrowException(String::New(message));
    return false;
  }

#endif  // No waitid call.

  return true;
}


// Implementation of the system() function (see d8.h for details).
453
void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
454
  HandleScope scope(args.GetIsolate());
455 456
  int read_timeout = -1;
  int total_timeout = -1;
457
  if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
458 459 460
  Handle<Array> command_args;
  if (args.Length() > 1) {
    if (!args[1]->IsArray()) {
461 462
      args.GetIsolate()->ThrowException(
          String::New("system: Argument 2 must be an array"));
463
      return;
464 465 466 467 468 469
    }
    command_args = Handle<Array>::Cast(args[1]);
  } else {
    command_args = Array::New(0);
  }
  if (command_args->Length() > ExecArgs::kMaxArgs) {
470 471
    args.GetIsolate()->ThrowException(
        String::New("Too many arguments to system()"));
472
    return;
473 474
  }
  if (args.Length() < 1) {
475 476
    args.GetIsolate()->ThrowException(
        String::New("Too few arguments to system()"));
477
    return;
478 479 480 481 482
  }

  struct timeval start_time;
  gettimeofday(&start_time, NULL);

483 484
  ExecArgs exec_args;
  if (!exec_args.Init(args[0], command_args)) {
485
    return;
486
  }
487 488 489 490
  int exec_error_fds[2];
  int stdout_fds[2];

  if (pipe(exec_error_fds) != 0) {
491 492
    args.GetIsolate()->ThrowException(
        String::New("pipe syscall failed."));
493
    return;
494 495
  }
  if (pipe(stdout_fds) != 0) {
496 497
    args.GetIsolate()->ThrowException(
        String::New("pipe syscall failed."));
498
    return;
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
  }

  pid_t pid = fork();
  if (pid == 0) {  // Child process.
    ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
    exit(1);
  }

  // Parent process.  Ensure that we clean up if we exit this function early.
  ZombieProtector child_waiter(pid);
  close(exec_error_fds[kWriteFD]);
  close(stdout_fds[kWriteFD]);
  OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
  OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);

514
  if (!ChildLaunchedOK(exec_error_fds)) return;
515 516 517 518

  Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD],
                                        start_time,
                                        read_timeout,
519
                                        total_timeout);
520 521
  if (accumulator->IsUndefined()) {
    kill(pid, SIGINT);  // On timeout, kill the subprocess.
522 523
    args.GetReturnValue().Set(accumulator);
    return;
524 525 526 527 528 529 530
  }

  if (!WaitForChild(pid,
                    child_waiter,
                    start_time,
                    read_timeout,
                    total_timeout)) {
531
    return;
532 533
  }

534
  args.GetReturnValue().Set(accumulator);
535 536 537
}


538
void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
539 540
  if (args.Length() != 1) {
    const char* message = "chdir() takes one argument";
541
    args.GetIsolate()->ThrowException(String::New(message));
542
    return;
543 544 545 546
  }
  String::Utf8Value directory(args[0]);
  if (*directory == NULL) {
    const char* message = "os.chdir(): String conversion of argument failed.";
547
    args.GetIsolate()->ThrowException(String::New(message));
548
    return;
549 550
  }
  if (chdir(*directory) != 0) {
551
    args.GetIsolate()->ThrowException(String::New(strerror(errno)));
552
    return;
553 554 555 556
  }
}


557
void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
558 559
  if (args.Length() != 1) {
    const char* message = "umask() takes one argument";
560
    args.GetIsolate()->ThrowException(String::New(message));
561
    return;
562 563 564 565
  }
  if (args[0]->IsNumber()) {
    mode_t mask = args[0]->Int32Value();
    int previous = umask(mask);
566 567
    args.GetReturnValue().Set(previous);
    return;
568 569
  } else {
    const char* message = "umask() argument must be numeric";
570
    args.GetIsolate()->ThrowException(String::New(message));
571
    return;
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
  }
}


static bool CheckItsADirectory(char* directory) {
  struct stat stat_buf;
  int stat_result = stat(directory, &stat_buf);
  if (stat_result != 0) {
    ThrowException(String::New(strerror(errno)));
    return false;
  }
  if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
  ThrowException(String::New(strerror(EEXIST)));
  return false;
}


// Returns true for success.  Creates intermediate directories as needed.  No
// error if the directory exists already.
static bool mkdirp(char* directory, mode_t mask) {
  int result = mkdir(directory, mask);
  if (result == 0) return true;
  if (errno == EEXIST) {
    return CheckItsADirectory(directory);
  } else if (errno == ENOENT) {  // Intermediate path element is missing.
    char* last_slash = strrchr(directory, '/');
    if (last_slash == NULL) {
      ThrowException(String::New(strerror(errno)));
      return false;
    }
    *last_slash = 0;
    if (!mkdirp(directory, mask)) return false;
    *last_slash = '/';
    result = mkdir(directory, mask);
    if (result == 0) return true;
    if (errno == EEXIST) {
      return CheckItsADirectory(directory);
    }
    ThrowException(String::New(strerror(errno)));
    return false;
  } else {
    ThrowException(String::New(strerror(errno)));
    return false;
  }
}


619
void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
620 621 622 623 624 625
  mode_t mask = 0777;
  if (args.Length() == 2) {
    if (args[1]->IsNumber()) {
      mask = args[1]->Int32Value();
    } else {
      const char* message = "mkdirp() second argument must be numeric";
626
      args.GetIsolate()->ThrowException(String::New(message));
627
      return;
628 629 630
    }
  } else if (args.Length() != 1) {
    const char* message = "mkdirp() takes one or two arguments";
631
    args.GetIsolate()->ThrowException(String::New(message));
632
    return;
633 634 635 636
  }
  String::Utf8Value directory(args[0]);
  if (*directory == NULL) {
    const char* message = "os.mkdirp(): String conversion of argument failed.";
637
    args.GetIsolate()->ThrowException(String::New(message));
638
    return;
639 640 641 642 643
  }
  mkdirp(*directory, mask);
}


644
void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
645 646
  if (args.Length() != 1) {
    const char* message = "rmdir() takes one or two arguments";
647
    args.GetIsolate()->ThrowException(String::New(message));
648
    return;
649 650 651 652
  }
  String::Utf8Value directory(args[0]);
  if (*directory == NULL) {
    const char* message = "os.rmdir(): String conversion of argument failed.";
653
    args.GetIsolate()->ThrowException(String::New(message));
654
    return;
655 656 657 658 659
  }
  rmdir(*directory);
}


660
void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
661 662
  if (args.Length() != 2) {
    const char* message = "setenv() takes two arguments";
663
    args.GetIsolate()->ThrowException(String::New(message));
664
    return;
665 666 667 668 669 670
  }
  String::Utf8Value var(args[0]);
  String::Utf8Value value(args[1]);
  if (*var == NULL) {
    const char* message =
        "os.setenv(): String conversion of variable name failed.";
671
    args.GetIsolate()->ThrowException(String::New(message));
672
    return;
673 674 675 676
  }
  if (*value == NULL) {
    const char* message =
        "os.setenv(): String conversion of variable contents failed.";
677
    args.GetIsolate()->ThrowException(String::New(message));
678
    return;
679 680 681 682 683
  }
  setenv(*var, *value, 1);
}


684
void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
685 686
  if (args.Length() != 1) {
    const char* message = "unsetenv() takes one argument";
687
    args.GetIsolate()->ThrowException(String::New(message));
688
    return;
689 690 691 692 693
  }
  String::Utf8Value var(args[0]);
  if (*var == NULL) {
    const char* message =
        "os.setenv(): String conversion of variable name failed.";
694
    args.GetIsolate()->ThrowException(String::New(message));
695
    return;
696 697 698 699 700
  }
  unsetenv(*var);
}


701 702 703 704
void Shell::AddOSMethods(Handle<ObjectTemplate> os_templ) {
  os_templ->Set(String::New("system"), FunctionTemplate::New(System));
  os_templ->Set(String::New("chdir"), FunctionTemplate::New(ChangeDirectory));
  os_templ->Set(String::New("setenv"), FunctionTemplate::New(SetEnvironment));
705 706
  os_templ->Set(String::New("unsetenv"),
                FunctionTemplate::New(UnsetEnvironment));
707 708 709 710 711
  os_templ->Set(String::New("umask"), FunctionTemplate::New(SetUMask));
  os_templ->Set(String::New("mkdirp"), FunctionTemplate::New(MakeDirectory));
  os_templ->Set(String::New("rmdir"), FunctionTemplate::New(RemoveDirectory));
}

712
}  // namespace v8