| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_BIN_PROCESS_H_ | |
| #define RUNTIME_BIN_PROCESS_H_ | |
| #include <errno.h> | |
| #include "bin/builtin.h" | |
| #include "bin/io_buffer.h" | |
| #include "bin/lockers.h" | |
| #include "bin/namespace.h" | |
| #include "bin/thread.h" | |
| #include "platform/globals.h" | |
| #if !defined(DART_HOST_OS_WINDOWS) | |
| #include "platform/signal_blocker.h" | |
| #endif | |
| #include "platform/utils.h" | |
| namespace dart { | |
| namespace bin { | |
| class ProcessResult { | |
| public: | |
| ProcessResult() : exit_code_(0) {} | |
| void set_stdout_data(Dart_Handle stdout_data) { stdout_data_ = stdout_data; } | |
| void set_stderr_data(Dart_Handle stderr_data) { stderr_data_ = stderr_data; } | |
| void set_exit_code(intptr_t exit_code) { exit_code_ = exit_code; } | |
| Dart_Handle stdout_data() { return stdout_data_; } | |
| Dart_Handle stderr_data() { return stderr_data_; } | |
| intptr_t exit_code() { return exit_code_; } | |
| private: | |
| Dart_Handle stdout_data_; | |
| Dart_Handle stderr_data_; | |
| intptr_t exit_code_; | |
| DISALLOW_ALLOCATION(); | |
| }; | |
| // To be kept in sync with ProcessSignal consts in sdk/lib/io/process.dart | |
| // Note that this map is as on Linux. | |
| enum ProcessSignals { | |
| kSighup = 1, | |
| kSigint = 2, | |
| kSigquit = 3, | |
| kSigill = 4, | |
| kSigtrap = 5, | |
| kSigabrt = 6, | |
| kSigbus = 7, | |
| kSigfpe = 8, | |
| kSigkill = 9, | |
| kSigusr1 = 10, | |
| kSigsegv = 11, | |
| kSigusr2 = 12, | |
| kSigpipe = 13, | |
| kSigalrm = 14, | |
| kSigterm = 15, | |
| kSigchld = 17, | |
| kSigcont = 18, | |
| kSigstop = 19, | |
| kSigtstp = 20, | |
| kSigttin = 21, | |
| kSigttou = 22, | |
| kSigurg = 23, | |
| kSigxcpu = 24, | |
| kSigxfsz = 25, | |
| kSigvtalrm = 26, | |
| kSigprof = 27, | |
| kSigwinch = 28, | |
| kSigpoll = 29, | |
| kSigsys = 31, | |
| kLastSignal = kSigsys, | |
| }; | |
| // To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart. | |
| enum ProcessStartMode { | |
| kNormal = 0, | |
| kInheritStdio = 1, | |
| kDetached = 2, | |
| kDetachedWithStdio = 3, | |
| }; | |
| class Process { | |
| public: | |
| static void Init(); | |
| static void Cleanup(); | |
| // Start a new process providing access to stdin, stdout, stderr and | |
| // process exit streams. | |
| static int Start(Namespace* namespc, | |
| const char* path, | |
| const char* arguments[], | |
| intptr_t arguments_length, | |
| const char* working_directory, | |
| char* environment[], | |
| intptr_t environment_length, | |
| ProcessStartMode mode, | |
| intptr_t* in, | |
| intptr_t* out, | |
| intptr_t* err, | |
| intptr_t* id, | |
| intptr_t* exit_handler, | |
| char** os_error_message); | |
| static bool Wait(intptr_t id, | |
| intptr_t in, | |
| intptr_t out, | |
| intptr_t err, | |
| intptr_t exit_handler, | |
| ProcessResult* result); | |
| // Exec process. | |
| // On systems that support 'exec' it will use it to replace | |
| // the current process image with the image corresponding to 'path' | |
| // On systems that do not support it (Windows) it will start in a | |
| // child process in the same group as the parent so that when the parent | |
| // is killed the child also dies. | |
| // Returns 0 if the process could be execed successfully | |
| // Returns -1 if the exec could not be done successfully and 'errmsg' | |
| // points to the error message | |
| static int Exec(Namespace* namespc, | |
| const char* path, | |
| const char* arguments[], | |
| intptr_t arguments_length, | |
| const char* working_directory, | |
| char* errmsg, | |
| intptr_t errmsg_len); | |
| // Kill a process with a given pid. | |
| static bool Kill(intptr_t id, int signal); | |
| // Terminate the exit code handler thread. Does not return before | |
| // the thread has terminated. | |
| static void TerminateExitCodeHandler(); | |
| static int GlobalExitCode() { | |
| MutexLocker ml(global_exit_code_mutex_); | |
| return global_exit_code_; | |
| } | |
| static void SetGlobalExitCode(int exit_code) { | |
| MutexLocker ml(global_exit_code_mutex_); | |
| global_exit_code_ = exit_code; | |
| } | |
| typedef void (*ExitHook)(int64_t exit_code); | |
| static void SetExitHook(ExitHook hook) { exit_hook_ = hook; } | |
| static void RunExitHook(int64_t exit_code) { | |
| if (exit_hook_ != nullptr) { | |
| exit_hook_(exit_code); | |
| } | |
| } | |
| static intptr_t CurrentProcessId(); | |
| static intptr_t SetSignalHandler(intptr_t signal); | |
| // When there is a current Isolate and the 'port' argument is | |
| // Dart_GetMainPortId(), this clears the signal handler for the current | |
| // isolate. When 'port' is ILLEGAL_PORT, this clears all signal handlers for | |
| // 'signal' for all Isolates. | |
| static void ClearSignalHandler(intptr_t signal, Dart_Port port); | |
| static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port); | |
| static void ClearAllSignalHandlers(); | |
| static Dart_Handle GetProcessIdNativeField(Dart_Handle process, | |
| intptr_t* pid); | |
| static Dart_Handle SetProcessIdNativeField(Dart_Handle process, intptr_t pid); | |
| static int64_t CurrentRSS(); | |
| static int64_t MaxRSS(); | |
| static void GetRSSInformation(int64_t* max_rss, int64_t* current_rss); | |
| static bool ModeIsAttached(ProcessStartMode mode); | |
| static bool ModeHasStdio(ProcessStartMode mode); | |
| private: | |
| static int global_exit_code_; | |
| static Mutex* global_exit_code_mutex_; | |
| static ExitHook exit_hook_; | |
| DISALLOW_ALLOCATION(); | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(Process); | |
| }; | |
| typedef void (*sa_handler_t)(int); | |
| class SignalInfo { | |
| public: | |
| SignalInfo(intptr_t fd, | |
| intptr_t signal, | |
| sa_handler_t oldact, | |
| SignalInfo* next) | |
| : fd_(fd), | |
| signal_(signal), | |
| oldact_(oldact), | |
| // SignalInfo is expected to be created when in a isolate. | |
| port_(Dart_GetMainPortId()), | |
| next_(next), | |
| prev_(nullptr) { | |
| if (next_ != nullptr) { | |
| next_->prev_ = this; | |
| } | |
| } | |
| ~SignalInfo(); | |
| void Unlink() { | |
| if (prev_ != nullptr) { | |
| prev_->next_ = next_; | |
| } | |
| if (next_ != nullptr) { | |
| next_->prev_ = prev_; | |
| } | |
| } | |
| intptr_t fd() const { return fd_; } | |
| intptr_t signal() const { return signal_; } | |
| sa_handler_t oldact() const { return oldact_; } | |
| Dart_Port port() const { return port_; } | |
| SignalInfo* next() const { return next_; } | |
| private: | |
| intptr_t fd_; | |
| intptr_t signal_; | |
| sa_handler_t oldact_; | |
| // The port_ is used to identify what isolate the signal-info belongs to. | |
| Dart_Port port_; | |
| SignalInfo* next_; | |
| SignalInfo* prev_; | |
| DISALLOW_COPY_AND_ASSIGN(SignalInfo); | |
| }; | |
| // Utility class for collecting the output when running a process | |
| // synchronously by using Process::Wait. This class is sub-classed in | |
| // the platform specific files to implement reading into the buffers | |
| // allocated. | |
| class BufferListBase { | |
| protected: | |
| static constexpr intptr_t kBufferSize = 16 * 1024; | |
| class BufferListNode { | |
| public: | |
| explicit BufferListNode(intptr_t size) { | |
| data_ = new uint8_t[size]; | |
| // We check for a failed allocation below in Allocate() | |
| next_ = nullptr; | |
| } | |
| ~BufferListNode() { delete[] data_; } | |
| bool Valid() const { return data_ != nullptr; } | |
| uint8_t* data() const { return data_; } | |
| BufferListNode* next() const { return next_; } | |
| void set_next(BufferListNode* n) { next_ = n; } | |
| private: | |
| uint8_t* data_; | |
| BufferListNode* next_; | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(BufferListNode); | |
| }; | |
| public: | |
| BufferListBase() | |
| : head_(nullptr), tail_(nullptr), data_size_(0), free_size_(0) {} | |
| ~BufferListBase() { | |
| Free(); | |
| DEBUG_ASSERT(IsEmpty()); | |
| } | |
| // Returns the collected data as a Uint8List. If an error occurs an | |
| // error handle is returned. | |
| Dart_Handle GetData() { | |
| uint8_t* buffer; | |
| intptr_t buffer_position = 0; | |
| Dart_Handle result = IOBuffer::Allocate(data_size_, &buffer); | |
| if (Dart_IsNull(result)) { | |
| return DartUtils::NewDartOSError(); | |
| } | |
| if (Dart_IsError(result)) { | |
| Free(); | |
| return result; | |
| } | |
| for (BufferListNode* current = head_; current != nullptr; | |
| current = current->next()) { | |
| intptr_t to_copy = dart::Utils::Minimum(data_size_, kBufferSize); | |
| memmove(buffer + buffer_position, current->data(), to_copy); | |
| buffer_position += to_copy; | |
| data_size_ -= to_copy; | |
| } | |
| ASSERT(data_size_ == 0); | |
| Free(); | |
| return result; | |
| } | |
| #if defined(DEBUG) | |
| bool IsEmpty() const { return (head_ == nullptr) && (tail_ == nullptr); } | |
| #endif | |
| protected: | |
| bool Allocate() { | |
| ASSERT(free_size_ == 0); | |
| BufferListNode* node = new BufferListNode(kBufferSize); | |
| if ((node == nullptr) || !node->Valid()) { | |
| // Failed to allocate a buffer for the node. | |
| delete node; | |
| return false; | |
| } | |
| if (head_ == nullptr) { | |
| head_ = node; | |
| tail_ = node; | |
| } else { | |
| ASSERT(tail_->next() == nullptr); | |
| tail_->set_next(node); | |
| tail_ = node; | |
| } | |
| free_size_ = kBufferSize; | |
| return true; | |
| } | |
| void Free() { | |
| BufferListNode* current = head_; | |
| while (current != nullptr) { | |
| BufferListNode* tmp = current; | |
| current = current->next(); | |
| delete tmp; | |
| } | |
| head_ = nullptr; | |
| tail_ = nullptr; | |
| data_size_ = 0; | |
| free_size_ = 0; | |
| } | |
| // Returns the address of the first byte in the free space. | |
| uint8_t* FreeSpaceAddress() { | |
| return tail_->data() + (kBufferSize - free_size_); | |
| } | |
| intptr_t data_size() const { return data_size_; } | |
| void set_data_size(intptr_t size) { data_size_ = size; } | |
| intptr_t free_size() const { return free_size_; } | |
| void set_free_size(intptr_t size) { free_size_ = size; } | |
| BufferListNode* head() const { return head_; } | |
| BufferListNode* tail() const { return tail_; } | |
| private: | |
| // Linked list for data collected. | |
| BufferListNode* head_; | |
| BufferListNode* tail_; | |
| // Number of bytes of data collected in the linked list. | |
| intptr_t data_size_; | |
| // Number of free bytes in the last node in the list. | |
| intptr_t free_size_; | |
| DISALLOW_COPY_AND_ASSIGN(BufferListBase); | |
| }; | |
| #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \ | |
| defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) | |
| class BufferList : public BufferListBase { | |
| public: | |
| BufferList() {} | |
| bool Read(int fd, intptr_t available) { | |
| // Read all available bytes. | |
| while (available > 0) { | |
| if (free_size() == 0) { | |
| if (!Allocate()) { | |
| errno = ENOMEM; | |
| return false; | |
| } | |
| } | |
| ASSERT(free_size() > 0); | |
| ASSERT(free_size() <= kBufferSize); | |
| intptr_t block_size = dart::Utils::Minimum(free_size(), available); | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| intptr_t bytes = NO_RETRY_EXPECTED( | |
| read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size)); | |
| #else | |
| intptr_t bytes = TEMP_FAILURE_RETRY( | |
| read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size)); | |
| #endif // defined(DART_HOST_OS_FUCHSIA) | |
| if (bytes < 0) { | |
| return false; | |
| } | |
| set_data_size(data_size() + bytes); | |
| set_free_size(free_size() - bytes); | |
| available -= bytes; | |
| } | |
| return true; | |
| } | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(BufferList); | |
| }; | |
| #endif // defined(DART_HOST_OS_ANDROID) ... | |
| } // namespace bin | |
| } // namespace dart | |
| #endif // RUNTIME_BIN_PROCESS_H_ |