LCOV - code coverage report
Current view: top level - src - d8-posix.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 16 376 4.3 %
Date: 2019-04-17 Functions: 2 22 9.1 %

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

Generated by: LCOV version 1.10