d8-posix.cc 29.7 KB
Newer Older
1
// Copyright 2009 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4 5

#include <errno.h>
6
#include <fcntl.h>
7
#include <netinet/ip.h>
8
#include <signal.h>
9
#include <stdlib.h>
10
#include <string.h>
yangguo's avatar
yangguo committed
11
#include <sys/select.h>
12
#include <sys/socket.h>
13
#include <sys/stat.h>
14
#include <sys/time.h>
15 16
#include <sys/types.h>
#include <sys/wait.h>
17 18
#include <unistd.h>

19
#include "src/d8.h"
20 21 22 23 24 25 26 27 28 29 30 31 32 33

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.
34 35
  static const int kUtf8TwoByteMask = 0xE0;
  static const int kUtf8TwoByteValue = 0xC0;
36
  // 3-byte encoding.
37 38
  static const int kUtf8ThreeByteMask = 0xF0;
  static const int kUtf8ThreeByteValue = 0xE0;
39
  // 4-byte encoding.
40 41
  static const int kUtf8FourByteMask = 0xF8;
  static const int kUtf8FourByteValue = 0xF0;
42
  // Subsequent bytes of a multi-byte encoding.
43
  static const int kMultiByteMask = 0xC0;
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
  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,
83
                     int total_timeout,
84
                     const struct timeval& start_time) {
85 86
  fd_set readfds, writefds, exceptfds;
  struct timeval timeout;
87 88
  int gone = 0;
  if (total_timeout != -1) {
89
    struct timeval time_now;
90
    gettimeofday(&time_now, nullptr);
91 92 93
    time_t seconds = time_now.tv_sec - start_time.tv_sec;
    gone = static_cast<int>(seconds * 1000 +
                            (time_now.tv_usec - start_time.tv_usec) / 1000);
94
    if (gone >= total_timeout) return false;
95 96 97 98 99 100 101
  }
  FD_ZERO(&readfds);
  FD_ZERO(&writefds);
  FD_ZERO(&exceptfds);
  FD_SET(fd, &readfds);
  FD_SET(fd, &exceptfds);
  if (read_timeout == -1 ||
102 103
      (total_timeout != -1 && total_timeout - gone < read_timeout)) {
    read_timeout = total_timeout - gone;
104 105 106
  }
  timeout.tv_usec = (read_timeout % 1000) * 1000;
  timeout.tv_sec = read_timeout / 1000;
107 108
  int number_of_fds_ready = select(fd + 1, &readfds, &writefds, &exceptfds,
                                   read_timeout != -1 ? &timeout : nullptr);
109 110 111 112 113 114 115 116 117
  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;
118
  gettimeofday(&time_now, nullptr);
119
  // Careful about overflow.
120
  int seconds = static_cast<int>(time_now.tv_sec - start_time.tv_sec);
121 122 123 124
  if (seconds > 100) {
    if (seconds * 1000 > total_time) return true;
    return false;
  }
125
  int useconds = static_cast<int>(time_now.tv_usec - start_time.tv_usec);
126 127 128 129 130 131 132 133 134 135 136 137 138
  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) { }
139 140 141
  ~ZombieProtector() {
    if (pid_ != 0) waitpid(pid_, nullptr, 0);
  }
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
  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:
163
  ExecArgs() { exec_args_[0] = nullptr; }
164
  bool Init(Isolate* isolate, Local<Value> arg0, Local<Array> command_args) {
165
    String::Utf8Value prog(isolate, arg0);
166
    if (*prog == nullptr) {
167 168
      const char* message =
          "os.system(): String conversion of program name failed";
169 170 171
      isolate->ThrowException(
          String::NewFromUtf8(isolate, message, NewStringType::kNormal)
              .ToLocalChecked());
172 173 174
      return false;
    }
    int len = prog.length() + 3;
175 176 177 178 179
    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++) {
180 181 182
      Local<Value> arg(
          command_args->Get(isolate->GetCurrentContext(),
                            Integer::New(isolate, j)).ToLocalChecked());
183
      String::Utf8Value utf8_arg(isolate, arg);
184 185
      if (*utf8_arg == nullptr) {
        exec_args_[i] = nullptr;  // Consistent state for destructor.
186 187
        const char* message =
            "os.system(): String conversion of argument failed.";
188 189 190
        isolate->ThrowException(
            String::NewFromUtf8(isolate, message, NewStringType::kNormal)
                .ToLocalChecked());
191 192
        return false;
      }
193 194 195 196 197
      int len = utf8_arg.length() + 1;
      char* c_arg = new char[len];
      snprintf(c_arg, len, "%s", *utf8_arg);
      exec_args_[i] = c_arg;
    }
198
    exec_args_[i] = nullptr;
199
    return true;
200 201 202
  }
  ~ExecArgs() {
    for (unsigned i = 0; i < kMaxArgs; i++) {
203
      if (exec_args_[i] == nullptr) {
204 205 206 207 208 209 210
        return;
      }
      delete [] exec_args_[i];
      exec_args_[i] = 0;
    }
  }
  static const unsigned kMaxArgs = 1000;
211 212
  char* const* arg_array() const { return exec_args_; }
  const char* arg0() const { return exec_args_[0]; }
213

214 215 216 217 218 219
 private:
  char* exec_args_[kMaxArgs + 1];
};


// Gets the optional timeouts from the arguments to the system() call.
220
static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
221 222 223 224
                        int* read_timeout,
                        int* total_timeout) {
  if (args.Length() > 3) {
    if (args[3]->IsNumber()) {
225 226 227
      *total_timeout = args[3]
                           ->Int32Value(args.GetIsolate()->GetCurrentContext())
                           .FromJust();
228
    } else {
229 230 231 232
      args.GetIsolate()->ThrowException(
          String::NewFromUtf8(args.GetIsolate(),
                              "system: Argument 4 must be a number",
                              NewStringType::kNormal).ToLocalChecked());
233 234 235 236 237
      return false;
    }
  }
  if (args.Length() > 2) {
    if (args[2]->IsNumber()) {
238 239 240
      *read_timeout = args[2]
                          ->Int32Value(args.GetIsolate()->GetCurrentContext())
                          .FromJust();
241
    } else {
242 243 244 245
      args.GetIsolate()->ThrowException(
          String::NewFromUtf8(args.GetIsolate(),
                              "system: Argument 3 must be a number",
                              NewStringType::kNormal).ToLocalChecked());
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
      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,
262
                           const ExecArgs& exec_args) {
263 264 265 266 267 268 269 270 271 272
  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;
273
  ssize_t bytes_written;
274 275 276
  do {
    bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
  } while (bytes_written == -1 && errno == EINTR);
277 278 279 280 281 282
  // 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.
283
static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
284
  ssize_t bytes_read;
285 286 287 288 289
  int err;
  do {
    bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
  } while (bytes_read == -1 && errno == EINTR);
  if (bytes_read != 0) {
290 291 292
    isolate->ThrowException(
        String::NewFromUtf8(isolate, strerror(err), NewStringType::kNormal)
            .ToLocalChecked());
293 294 295 296 297 298 299 300
    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.
301 302 303 304
static Local<Value> GetStdout(Isolate* isolate, int child_fd,
                              const struct timeval& start_time,
                              int read_timeout, int total_timeout) {
  Local<String> accumulator = String::Empty(isolate);
305 306 307 308 309 310

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

  if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
311
    return isolate->ThrowException(
312 313
        String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
            .ToLocalChecked());
314 315 316 317
  }

  int bytes_read;
  do {
318 319
    bytes_read = static_cast<int>(
        read(child_fd, buffer + fullness, kStdoutReadBufferSize - fullness));
320 321 322 323 324 325
    if (bytes_read == -1) {
      if (errno == EAGAIN) {
        if (!WaitOnFD(child_fd,
                      read_timeout,
                      total_timeout,
                      start_time) ||
326
            (TimeIsOut(start_time, total_timeout))) {
327
          return isolate->ThrowException(
328 329
              String::NewFromUtf8(isolate, "Timed out waiting for output",
                                  NewStringType::kNormal).ToLocalChecked());
330 331 332 333 334 335 336 337 338 339 340 341
        }
        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);
342 343 344
      Local<String> addition =
          String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
              .ToLocalChecked();
345
      accumulator = String::Concat(accumulator, addition);
346 347 348 349 350 351 352 353 354 355 356 357 358 359
      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.
360
//
361
// We're disabling usage of waitid in Mac OS X because it doesn't work for us:
362 363
// 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.
364 365 366
#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
    && !defined(__NetBSD__)
#if !defined(__FreeBSD__)
367 368
#define HAS_WAITID 1
#endif
369
#endif
370 371 372


// Get exit status of child.
373 374
static bool WaitForChild(Isolate* isolate,
                         int pid,
375 376
                         ZombieProtector& child_waiter,  // NOLINT
                         const struct timeval& start_time,
377 378 379 380 381 382 383 384 385 386 387 388 389 390
                         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))) {
391 392 393 394
      isolate->ThrowException(
          String::NewFromUtf8(isolate,
                              "Timed out waiting for process to terminate",
                              NewStringType::kNormal).ToLocalChecked());
395 396 397 398 399 400 401 402 403 404
      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);
405 406 407
    isolate->ThrowException(
        String::NewFromUtf8(isolate, message, NewStringType::kNormal)
            .ToLocalChecked());
408 409 410 411 412 413 414 415
    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);
416 417 418
    isolate->ThrowException(
        String::NewFromUtf8(isolate, message, NewStringType::kNormal)
            .ToLocalChecked());
419 420 421 422 423 424 425 426 427 428 429 430 431 432
    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));
433 434 435
    isolate->ThrowException(
        String::NewFromUtf8(isolate, message, NewStringType::kNormal)
            .ToLocalChecked());
436 437 438 439 440 441 442 443 444
    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);
445 446 447
    isolate->ThrowException(
        String::NewFromUtf8(isolate, message, NewStringType::kNormal)
            .ToLocalChecked());
448 449 450 451 452 453 454 455 456 457
    return false;
  }

#endif  // No waitid call.

  return true;
}


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

  struct timeval start_time;
490
  gettimeofday(&start_time, nullptr);
491

492
  ExecArgs exec_args;
493
  if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
494
    return;
495
  }
496 497 498 499
  int exec_error_fds[2];
  int stdout_fds[2];

  if (pipe(exec_error_fds) != 0) {
500
    args.GetIsolate()->ThrowException(
501 502
        String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
                            NewStringType::kNormal).ToLocalChecked());
503
    return;
504 505
  }
  if (pipe(stdout_fds) != 0) {
506
    args.GetIsolate()->ThrowException(
507 508
        String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
                            NewStringType::kNormal).ToLocalChecked());
509
    return;
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
  }

  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]);

525 526
  Isolate* isolate = args.GetIsolate();
  if (!ChildLaunchedOK(isolate, exec_error_fds)) return;
527

528 529
  Local<Value> accumulator = GetStdout(isolate, stdout_fds[kReadFD], start_time,
                                       read_timeout, total_timeout);
530 531
  if (accumulator->IsUndefined()) {
    kill(pid, SIGINT);  // On timeout, kill the subprocess.
532 533
    args.GetReturnValue().Set(accumulator);
    return;
534 535
  }

536
  if (!WaitForChild(isolate, pid, child_waiter, start_time, read_timeout,
537
                    total_timeout)) {
538
    return;
539 540
  }

541
  args.GetReturnValue().Set(accumulator);
542 543 544
}


545
void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
546 547
  if (args.Length() != 1) {
    const char* message = "chdir() takes one argument";
548
    args.GetIsolate()->ThrowException(
549 550
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
551
    return;
552
  }
553
  String::Utf8Value directory(args.GetIsolate(), args[0]);
554
  if (*directory == nullptr) {
555
    const char* message = "os.chdir(): String conversion of argument failed.";
556
    args.GetIsolate()->ThrowException(
557 558
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
559
    return;
560 561
  }
  if (chdir(*directory) != 0) {
562
    args.GetIsolate()->ThrowException(
563 564
        String::NewFromUtf8(args.GetIsolate(), strerror(errno),
                            NewStringType::kNormal).ToLocalChecked());
565
    return;
566 567 568 569
  }
}


570
void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
571 572
  if (args.Length() != 1) {
    const char* message = "umask() takes one argument";
573
    args.GetIsolate()->ThrowException(
574 575
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
576
    return;
577 578
  }
  if (args[0]->IsNumber()) {
579 580
    int previous = umask(
        args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust());
581 582
    args.GetReturnValue().Set(previous);
    return;
583 584
  } else {
    const char* message = "umask() argument must be numeric";
585
    args.GetIsolate()->ThrowException(
586 587
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
588
    return;
589 590 591 592
  }
}


593
static bool CheckItsADirectory(Isolate* isolate, char* directory) {
594 595 596
  struct stat stat_buf;
  int stat_result = stat(directory, &stat_buf);
  if (stat_result != 0) {
597 598 599
    isolate->ThrowException(
        String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
            .ToLocalChecked());
600 601 602
    return false;
  }
  if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
603 604 605
  isolate->ThrowException(
      String::NewFromUtf8(isolate, strerror(EEXIST), NewStringType::kNormal)
          .ToLocalChecked());
606 607 608 609 610 611
  return false;
}


// Returns true for success.  Creates intermediate directories as needed.  No
// error if the directory exists already.
612
static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
613 614 615
  int result = mkdir(directory, mask);
  if (result == 0) return true;
  if (errno == EEXIST) {
616
    return CheckItsADirectory(isolate, directory);
617 618
  } else if (errno == ENOENT) {  // Intermediate path element is missing.
    char* last_slash = strrchr(directory, '/');
619
    if (last_slash == nullptr) {
620 621 622
      isolate->ThrowException(
          String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
              .ToLocalChecked());
623 624 625
      return false;
    }
    *last_slash = 0;
626
    if (!mkdirp(isolate, directory, mask)) return false;
627 628 629 630
    *last_slash = '/';
    result = mkdir(directory, mask);
    if (result == 0) return true;
    if (errno == EEXIST) {
631
      return CheckItsADirectory(isolate, directory);
632
    }
633 634 635
    isolate->ThrowException(
        String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
            .ToLocalChecked());
636 637
    return false;
  } else {
638 639 640
    isolate->ThrowException(
        String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
            .ToLocalChecked());
641 642 643 644 645
    return false;
  }
}


646
void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
647 648 649
  mode_t mask = 0777;
  if (args.Length() == 2) {
    if (args[1]->IsNumber()) {
650 651 652
      mask = args[1]
                 ->Int32Value(args.GetIsolate()->GetCurrentContext())
                 .FromJust();
653 654
    } else {
      const char* message = "mkdirp() second argument must be numeric";
655
      args.GetIsolate()->ThrowException(
656 657
          String::NewFromUtf8(args.GetIsolate(), message,
                              NewStringType::kNormal).ToLocalChecked());
658
      return;
659 660 661
    }
  } else if (args.Length() != 1) {
    const char* message = "mkdirp() takes one or two arguments";
662
    args.GetIsolate()->ThrowException(
663 664
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
665
    return;
666
  }
667
  String::Utf8Value directory(args.GetIsolate(), args[0]);
668
  if (*directory == nullptr) {
669
    const char* message = "os.mkdirp(): String conversion of argument failed.";
670
    args.GetIsolate()->ThrowException(
671 672
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
673
    return;
674
  }
675
  mkdirp(args.GetIsolate(), *directory, mask);
676 677 678
}


679
void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
680 681
  if (args.Length() != 1) {
    const char* message = "rmdir() takes one or two arguments";
682
    args.GetIsolate()->ThrowException(
683 684
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
685
    return;
686
  }
687
  String::Utf8Value directory(args.GetIsolate(), args[0]);
688
  if (*directory == nullptr) {
689
    const char* message = "os.rmdir(): String conversion of argument failed.";
690
    args.GetIsolate()->ThrowException(
691 692
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
693
    return;
694 695 696 697 698
  }
  rmdir(*directory);
}


699
void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
700 701
  if (args.Length() != 2) {
    const char* message = "setenv() takes two arguments";
702
    args.GetIsolate()->ThrowException(
703 704
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
705
    return;
706
  }
707 708
  String::Utf8Value var(args.GetIsolate(), args[0]);
  String::Utf8Value value(args.GetIsolate(), args[1]);
709
  if (*var == nullptr) {
710 711
    const char* message =
        "os.setenv(): String conversion of variable name failed.";
712
    args.GetIsolate()->ThrowException(
713 714
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
715
    return;
716
  }
717
  if (*value == nullptr) {
718 719
    const char* message =
        "os.setenv(): String conversion of variable contents failed.";
720
    args.GetIsolate()->ThrowException(
721 722
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
723
    return;
724 725 726 727 728
  }
  setenv(*var, *value, 1);
}


729
void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
730 731
  if (args.Length() != 1) {
    const char* message = "unsetenv() takes one argument";
732
    args.GetIsolate()->ThrowException(
733 734
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
735
    return;
736
  }
737
  String::Utf8Value var(args.GetIsolate(), args[0]);
738
  if (*var == nullptr) {
739 740
    const char* message =
        "os.setenv(): String conversion of variable name failed.";
741
    args.GetIsolate()->ThrowException(
742 743
        String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
            .ToLocalChecked());
744
    return;
745 746 747 748
  }
  unsetenv(*var);
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
char* Shell::ReadCharsFromTcpPort(const char* name, int* size_out) {
  DCHECK_GE(Shell::options.read_from_tcp_port, 0);

  int sockfd = socket(PF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) {
    fprintf(stderr, "Failed to create IPv4 socket\n");
    return nullptr;
  }

  // Create an address for localhost:PORT where PORT is specified by the shell
  // option --read-from-tcp-port.
  sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(sockaddr_in));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  serv_addr.sin_port = htons(Shell::options.read_from_tcp_port);

  if (connect(sockfd, reinterpret_cast<sockaddr*>(&serv_addr),
              sizeof(serv_addr)) < 0) {
    fprintf(stderr, "Failed to connect to localhost:%d\n",
            Shell::options.read_from_tcp_port);
    close(sockfd);
    return nullptr;
  }

  // The file server follows the simple protocol for requesting and receiving
  // a file with a given filename:
  //
  //   REQUEST client -> server: {filename}"\0"
  //   RESPONSE server -> client: {4-byte file-length}{file contents}
  //
  // i.e. the request sends the filename with a null terminator, and response
  // sends the file contents by sending the length (as a 4-byte big-endian
  // value) and the contents.

  // If the file length is <0, there was an error sending the file, and the
  // rest of the response is undefined (and may, in the future, contain an error
  // message). The socket should be closed to avoid trying to interpret the
  // undefined data.

  // REQUEST
  // Send the filename.
  size_t sent_len = 0;
  size_t name_len = strlen(name) + 1;  // Includes the null terminator
  while (sent_len < name_len) {
    ssize_t sent_now = send(sockfd, name + sent_len, name_len - sent_len, 0);
    if (sent_now < 0) {
      fprintf(stderr, "Failed to send %s to localhost:%d\n", name,
              Shell::options.read_from_tcp_port);
      close(sockfd);
      return nullptr;
    }
    sent_len += sent_now;
  }

  // RESPONSE
  // Receive the file.
  ssize_t received = 0;

  // First, read the (zero-terminated) file length.
  uint32_t big_endian_file_length;
  received = recv(sockfd, &big_endian_file_length, 4, 0);
  // We need those 4 bytes to read off the file length.
  if (received < 4) {
    fprintf(stderr, "Failed to receive %s's length from localhost:%d\n", name,
            Shell::options.read_from_tcp_port);
    close(sockfd);
    return nullptr;
  }
  // Reinterpretet the received file length as a signed big-endian integer.
  int32_t file_length = bit_cast<int32_t>(htonl(big_endian_file_length));

  if (file_length < 0) {
    fprintf(stderr, "Received length %d for %s from localhost:%d\n",
            file_length, name, Shell::options.read_from_tcp_port);
    close(sockfd);
825
    return nullptr;
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
  }

  // Allocate the output array.
  char* chars = new char[file_length];

  // Now keep receiving and copying until the whole file is received.
  ssize_t total_received = 0;
  while (total_received < file_length) {
    received =
        recv(sockfd, chars + total_received, file_length - total_received, 0);
    if (received < 0) {
      fprintf(stderr, "Failed to receive %s from localhost:%d\n", name,
              Shell::options.read_from_tcp_port);
      close(sockfd);
      delete[] chars;
841
      return nullptr;
842 843 844 845 846 847 848 849
    }
    total_received += received;
  }

  close(sockfd);
  *size_out = file_length;
  return chars;
}
850

851
void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) {
852 853 854 855 856
  if (options.enable_os_system) {
    os_templ->Set(String::NewFromUtf8(isolate, "system", NewStringType::kNormal)
                      .ToLocalChecked(),
                  FunctionTemplate::New(isolate, System));
  }
857 858
  os_templ->Set(String::NewFromUtf8(isolate, "chdir", NewStringType::kNormal)
                    .ToLocalChecked(),
859
                FunctionTemplate::New(isolate, ChangeDirectory));
860 861
  os_templ->Set(String::NewFromUtf8(isolate, "setenv", NewStringType::kNormal)
                    .ToLocalChecked(),
862
                FunctionTemplate::New(isolate, SetEnvironment));
863 864
  os_templ->Set(String::NewFromUtf8(isolate, "unsetenv", NewStringType::kNormal)
                    .ToLocalChecked(),
865
                FunctionTemplate::New(isolate, UnsetEnvironment));
866 867
  os_templ->Set(String::NewFromUtf8(isolate, "umask", NewStringType::kNormal)
                    .ToLocalChecked(),
868
                FunctionTemplate::New(isolate, SetUMask));
869 870
  os_templ->Set(String::NewFromUtf8(isolate, "mkdirp", NewStringType::kNormal)
                    .ToLocalChecked(),
871
                FunctionTemplate::New(isolate, MakeDirectory));
872 873
  os_templ->Set(String::NewFromUtf8(isolate, "rmdir", NewStringType::kNormal)
                    .ToLocalChecked(),
874
                FunctionTemplate::New(isolate, RemoveDirectory));
875 876
}

877
void Shell::Exit(int exit_code) {
878 879
  // Use _exit instead of exit to avoid races between isolate
  // threads and static destructors.
880 881
  fflush(stdout);
  fflush(stderr);
882 883 884
  _exit(exit_code);
}

885
}  // namespace v8