// Copyright 2009 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <errno.h> #include <fcntl.h> #include "src/d8/d8.h" #ifndef V8_OS_ZOS #include <netinet/ip.h> #endif #include <signal.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include "include/v8-container.h" #include "include/v8-template.h" namespace v8 { // If the buffer ends in the middle of a UTF-8 sequence then we return // the length of the string up to but not including the incomplete UTF-8 // sequence. If the buffer ends with a valid UTF-8 sequence then we // return the whole buffer. static int LengthWithoutIncompleteUtf8(char* buffer, int len) { … } // Suspends the thread until there is data available from the child process. // Returns false on timeout, true on data ready. static bool WaitOnFD(int fd, int read_timeout, int total_timeout, const struct timeval& start_time) { … } // Checks whether we ran out of time on the timeout. Returns true if we ran out // of time, false if we still have time. static bool TimeIsOut(const struct timeval& start_time, const int& total_time) { … } // A utility class that does a non-hanging waitpid on the child process if we // bail out of the System() function early. If you don't ever do a waitpid on // a subprocess then it turns into one of those annoying 'zombie processes'. class ZombieProtector { … }; // A utility class that closes a file descriptor when it goes out of scope. class OpenFDCloser { … }; // A utility class that takes the array of command arguments and puts then in an // array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of // scope. class ExecArgs { … }; // Gets the optional timeouts from the arguments to the system() call. static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& info, int* read_timeout, int* total_timeout) { … } namespace { v8::Local<v8::String> v8_strerror(v8::Isolate* isolate, int err) { … } } // namespace static const int kReadFD = …; static const int kWriteFD = …; // This is run in the child process after fork() but before exec(). It normally // ends with the child process being replaced with the desired child program. // It only returns if an error occurred. static void ExecSubprocess(int* exec_error_fds, int* stdout_fds, const ExecArgs& exec_args) { … } // Runs in the parent process. Checks that the child was able to exec (closing // the file desriptor), or reports an error if it failed. static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) { … } // Accumulates the output from the child in a string handle. Returns true if it // succeeded or false if an exception was thrown. static Local<Value> GetStdout(Isolate* isolate, int child_fd, const struct timeval& start_time, int read_timeout, int total_timeout) { … } // Modern Linux has the waitid call, which is like waitpid, but more useful // if you want a timeout. If we don't have waitid we can't limit the time // waiting for the process to exit without losing the information about // whether it exited normally. In the common case this doesn't matter because // we don't get here before the child has closed stdout and most programs don't // do that before they exit. // // We're disabling usage of waitid in Mac OS X because it doesn't work for us: // a parent process hangs on waiting while a child process is already a zombie. // See http://code.google.com/p/v8/issues/detail?id=401. #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) && \ !defined(__NetBSD__) && !defined(__Fuchsia__) #if !defined(__FreeBSD__) #define HAS_WAITID … #endif #endif // Get exit status of child. static bool WaitForChild(Isolate* isolate, int pid, ZombieProtector& child_waiter, const struct timeval& start_time, int read_timeout, int total_timeout) { … } #undef HAS_WAITID // Implementation of the system() function (see d8.h for details). void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& info) { … } void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& info) { … } void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& info) { … } static bool CheckItsADirectory(Isolate* isolate, char* directory) { … } // Returns true for success. Creates intermediate directories as needed. No // error if the directory exists already. static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) { … } void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& info) { … } void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& info) { … } void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& info) { … } void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& info) { … } char* Shell::ReadCharsFromTcpPort(const char* name, int* size_out) { … } void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) { … } } // namespace v8