| // 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, |
| 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); |
| |
| // 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_ |