LCOV - code coverage report
Current view: top level - src - d8-posix.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 227 303 74.9 %
Date: 2017-04-26 Functions: 18 21 85.7 %

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

Generated by: LCOV version 1.10