// 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 BIN_PROCESS_H_ #define BIN_PROCESS_H_ #include "bin/builtin.h" #include "bin/io_buffer.h" #include "bin/lockers.h" #include "bin/thread.h" #include "platform/globals.h" #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 }; // To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart. enum ProcessStartMode { kNormal = 0, kDetached = 1, kDetachedWithStdio = 2, }; class Process { public: // Start a new process providing access to stdin, stdout, stderr and // process exit streams. static int Start(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; } static intptr_t CurrentProcessId(); static intptr_t SetSignalHandler(intptr_t signal); static void ClearSignalHandler(intptr_t signal); static Dart_Handle GetProcessIdNativeField(Dart_Handle process, intptr_t* pid); static Dart_Handle SetProcessIdNativeField(Dart_Handle process, intptr_t pid); private: static int global_exit_code_; static Mutex* global_exit_code_mutex_; DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(Process); }; class SignalInfo { public: SignalInfo(intptr_t fd, intptr_t signal, SignalInfo* next) : fd_(fd), signal_(signal), // SignalInfo is expected to be created when in a isolate. port_(Dart_GetMainPortId()), next_(next), prev_(NULL) { if (next_ != NULL) { next_->prev_ = this; } } ~SignalInfo(); void Unlink() { if (prev_ != NULL) { prev_->next_ = next_; } if (next_ != NULL) { next_->prev_ = prev_; } } intptr_t fd() const { return fd_; } intptr_t signal() const { return signal_; } Dart_Port port() const { return port_; } SignalInfo* next() const { return next_; } private: intptr_t fd_; intptr_t signal_; // The port_ is used to identify what isolate the signal-info belongs to. Dart_Port port_; SignalInfo* next_; SignalInfo* prev_; }; // 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 const intptr_t kBufferSize = 16 * 1024; class BufferListNode { public: explicit BufferListNode(intptr_t size) { data_ = new uint8_t[size]; if (data_ == NULL) FATAL("Allocation failed"); next_ = NULL; } ~BufferListNode() { delete[] data_; } uint8_t* data_; BufferListNode* next_; private: DISALLOW_IMPLICIT_CONSTRUCTORS(BufferListNode); }; public: BufferListBase() : head_(NULL), tail_(NULL), data_size_(0), free_size_(0) {} ~BufferListBase() { ASSERT(head_ == NULL); ASSERT(tail_ == NULL); } // Returns the collected data as a Uint8List. If an error occours 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_IsError(result)) { Free(); return result; } for (BufferListNode* current = head_; current != NULL; 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; } protected: void Allocate() { ASSERT(free_size_ == 0); BufferListNode* node = new BufferListNode(kBufferSize); if (head_ == NULL) { head_ = node; tail_ = node; } else { ASSERT(tail_->next_ == NULL); tail_->next_ = node; tail_ = node; } free_size_ = kBufferSize; } void Free() { BufferListNode* current = head_; while (current != NULL) { BufferListNode* tmp = current; current = current->next_; delete tmp; } head_ = NULL; tail_ = NULL; 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_); } // 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_; }; } // namespace bin } // namespace dart #endif // BIN_PROCESS_H_