Index: runtime/bin/process_fuchsia.cc |
diff --git a/runtime/bin/process_fuchsia.cc b/runtime/bin/process_fuchsia.cc |
index 058234f752229bcf5d988ced5e85527cbca00583..2bcef56c3a7f91cf1ac143240db8a4a5f8eb97d3 100644 |
--- a/runtime/bin/process_fuchsia.cc |
+++ b/runtime/bin/process_fuchsia.cc |
@@ -9,9 +9,35 @@ |
#include "bin/process.h" |
+#include <errno.h> |
+#include <fcntl.h> |
+#include <launchpad/launchpad.h> |
+#include <launchpad/vmo.h> |
+#include <magenta/status.h> |
+#include <magenta/syscalls.h> |
+#include <magenta/syscalls/object.h> |
+#include <mxio/util.h> |
+#include <pthread.h> |
+#include <stdbool.h> |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <unistd.h> |
+ |
+#include "bin/dartutils.h" |
+#include "bin/fdutils.h" |
#include "bin/lockers.h" |
-#include "platform/assert.h" |
+#include "bin/log.h" |
+#include "platform/signal_blocker.h" |
+// #define PROCESS_LOGGING 1 |
+#if defined(PROCESS_LOGGING) |
+#define LOG_ERR(msg, ...) Log::PrintErr("Dart Process: " msg, ##__VA_ARGS__) |
+#define LOG_INFO(msg, ...) Log::Print("Dart Process: " msg, ##__VA_ARGS__) |
+#else |
+#define LOG_ERR(msg, ...) |
+#define LOG_INFO(msg, ...) |
+#endif // defined(PROCESS_LOGGING) |
namespace dart { |
namespace bin { |
@@ -20,23 +46,385 @@ int Process::global_exit_code_ = 0; |
Mutex* Process::global_exit_code_mutex_ = new Mutex(); |
Process::ExitHook Process::exit_hook_ = NULL; |
-void Process::TerminateExitCodeHandler() {} |
+// ProcessInfo is used to map a process id to the file descriptor for |
+// the pipe used to communicate the exit code of the process to Dart. |
+// ProcessInfo objects are kept in the static singly-linked |
+// ProcessInfoList. |
+class ProcessInfo { |
+ public: |
+ ProcessInfo(mx_handle_t process, intptr_t fd) |
+ : process_(process), exit_pipe_fd_(fd) {} |
+ ~ProcessInfo() { |
+ int closed = NO_RETRY_EXPECTED(close(exit_pipe_fd_)); |
+ if (closed != 0) { |
+ FATAL("Failed to close process exit code pipe"); |
+ } |
+ } |
+ mx_handle_t process() const { return process_; } |
+ intptr_t exit_pipe_fd() const { return exit_pipe_fd_; } |
+ ProcessInfo* next() const { return next_; } |
+ void set_next(ProcessInfo* info) { next_ = info; } |
-intptr_t Process::CurrentProcessId() { |
- UNIMPLEMENTED(); |
- return 0; |
-} |
+ private: |
+ mx_handle_t process_; |
+ intptr_t exit_pipe_fd_; |
+ ProcessInfo* next_; |
-intptr_t Process::SetSignalHandler(intptr_t signal) { |
- UNIMPLEMENTED(); |
- return -1; |
+ DISALLOW_COPY_AND_ASSIGN(ProcessInfo); |
+}; |
+ |
+ |
+// Singly-linked list of ProcessInfo objects for all active processes |
+// started from Dart. |
+class ProcessInfoList { |
+ public: |
+ static void AddProcess(mx_handle_t process, intptr_t fd) { |
+ MutexLocker locker(mutex_); |
+ ProcessInfo* info = new ProcessInfo(process, fd); |
+ info->set_next(active_processes_); |
+ active_processes_ = info; |
+ } |
+ |
+ |
+ static intptr_t LookupProcessExitFd(mx_handle_t process) { |
+ MutexLocker locker(mutex_); |
+ ProcessInfo* current = active_processes_; |
+ while (current != NULL) { |
+ if (current->process() == process) { |
+ return current->exit_pipe_fd(); |
+ } |
+ current = current->next(); |
+ } |
+ return 0; |
+ } |
+ |
+ |
+ static void RemoveProcess(mx_handle_t process) { |
+ MutexLocker locker(mutex_); |
+ ProcessInfo* prev = NULL; |
+ ProcessInfo* current = active_processes_; |
+ while (current != NULL) { |
+ if (current->process() == process) { |
+ if (prev == NULL) { |
+ active_processes_ = current->next(); |
+ } else { |
+ prev->set_next(current->next()); |
+ } |
+ delete current; |
+ return; |
+ } |
+ prev = current; |
+ current = current->next(); |
+ } |
+ } |
+ |
+ private: |
+ // Linked list of ProcessInfo objects for all active processes |
+ // started from Dart code. |
+ static ProcessInfo* active_processes_; |
+ // Mutex protecting all accesses to the linked list of active |
+ // processes. |
+ static Mutex* mutex_; |
+ |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); |
+}; |
+ |
+ProcessInfo* ProcessInfoList::active_processes_ = NULL; |
+Mutex* ProcessInfoList::mutex_ = new Mutex(); |
+ |
+// The exit code handler sets up a separate thread which waits for child |
+// processes to terminate. That separate thread can then get the exit code from |
+// processes that have exited and communicate it to Dart through the |
+// event loop. |
+class ExitCodeHandler { |
+ public: |
+ // Notify the ExitCodeHandler that another process exists. |
+ static void Start() { |
+ // Multiple isolates could be starting processes at the same |
+ // time. Make sure that only one ExitCodeHandler thread exists. |
+ MonitorLocker locker(monitor_); |
+ if (running_) { |
+ return; |
+ } |
+ |
+ LOG_INFO("ExitCodeHandler Starting\n"); |
+ |
+ mx_status_t status = mx_socket_create(0, &interrupt_in_, &interrupt_out_); |
+ if (status < 0) { |
+ FATAL1("Failed to create exit code handler interrupt socket: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ |
+ // Start thread that handles process exits when wait returns. |
+ intptr_t result = |
+ Thread::Start(ExitCodeHandlerEntry, static_cast<uword>(interrupt_out_)); |
+ if (result != 0) { |
+ FATAL1("Failed to start exit code handler worker thread %ld", result); |
+ } |
+ |
+ running_ = true; |
+ } |
+ |
+ static void Add(mx_handle_t process) { |
+ MonitorLocker locker(monitor_); |
+ LOG_INFO("ExitCodeHandler Adding Process: %ld\n", process); |
+ SendMessage(Message::kAdd, process); |
+ } |
+ |
+ static void Terminate() { |
+ MonitorLocker locker(monitor_); |
+ if (!running_) { |
+ return; |
+ } |
+ running_ = false; |
+ |
+ LOG_INFO("ExitCodeHandler Terminating\n"); |
+ SendMessage(Message::kShutdown, MX_HANDLE_INVALID); |
+ |
+ while (!terminate_done_) { |
+ monitor_->Wait(Monitor::kNoTimeout); |
+ } |
+ mx_handle_close(interrupt_in_); |
+ LOG_INFO("ExitCodeHandler Terminated\n"); |
+ } |
+ |
+ private: |
+ class Message { |
+ public: |
+ enum Command { |
+ kAdd, |
+ kShutdown, |
+ }; |
+ Command command; |
+ mx_handle_t handle; |
+ }; |
+ |
+ static void SendMessage(Message::Command command, mx_handle_t handle) { |
+ Message msg; |
+ msg.command = command; |
+ msg.handle = handle; |
+ size_t actual; |
+ mx_status_t status = |
+ mx_socket_write(interrupt_in_, 0, &msg, sizeof(msg), &actual); |
+ if (status < 0) { |
+ FATAL1("Write to exit handler interrupt handle failed: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ ASSERT(actual == sizeof(msg)); |
+ } |
+ |
+ // Entry point for the separate exit code handler thread started by |
+ // the ExitCodeHandler. |
+ static void ExitCodeHandlerEntry(uword param) { |
+ LOG_INFO("ExitCodeHandler Entering ExitCodeHandler thread\n"); |
+ item_capacity_ = 16; |
+ items_ = reinterpret_cast<mx_wait_item_t*>( |
+ malloc(item_capacity_ * sizeof(*items_))); |
+ items_to_remove_ = reinterpret_cast<intptr_t*>( |
+ malloc(item_capacity_ * sizeof(*items_to_remove_))); |
+ |
+ // The interrupt handle is fixed to the first entry. |
+ items_[0].handle = interrupt_out_; |
+ items_[0].waitfor = MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED; |
+ items_[0].pending = MX_SIGNAL_NONE; |
+ item_count_ = 1; |
+ |
+ while (!do_shutdown_) { |
+ LOG_INFO("ExitCodeHandler Calling mx_handle_wait_many: %ld items\n", |
+ item_count_); |
+ mx_status_t status = |
+ mx_handle_wait_many(items_, item_count_, MX_TIME_INFINITE); |
+ if (status < 0) { |
+ FATAL1("Exit code handler handle wait failed: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ LOG_INFO("ExitCodeHandler mx_handle_wait_many returned\n"); |
+ |
+ bool have_interrupt = false; |
+ intptr_t remove_count = 0; |
+ for (intptr_t i = 0; i < item_count_; i++) { |
+ if (items_[i].pending == MX_SIGNAL_NONE) { |
+ continue; |
+ } |
+ if (i == 0) { |
+ LOG_INFO("ExitCodeHandler thread saw interrupt\n"); |
+ have_interrupt = true; |
+ continue; |
+ } |
+ ASSERT(items_[i].waitfor == MX_TASK_TERMINATED); |
+ ASSERT((items_[i].pending & MX_TASK_TERMINATED) != 0); |
+ LOG_INFO("ExitCodeHandler signal for %ld\n", items_[i].handle); |
+ SendProcessStatus(items_[i].handle); |
+ items_to_remove_[remove_count++] = i; |
+ } |
+ for (intptr_t i = 0; i < remove_count; i++) { |
+ RemoveItem(items_to_remove_[i]); |
+ } |
+ if (have_interrupt) { |
+ HandleInterruptMsg(); |
+ } |
+ } |
+ |
+ LOG_INFO("ExitCodeHandler thread shutting down\n"); |
+ mx_handle_close(interrupt_out_); |
+ free(items_); |
+ items_ = NULL; |
+ free(items_to_remove_); |
+ items_to_remove_ = NULL; |
+ item_count_ = 0; |
+ item_capacity_ = 0; |
+ |
+ terminate_done_ = true; |
+ monitor_->Notify(); |
+ } |
+ |
+ static void SendProcessStatus(mx_handle_t process) { |
+ LOG_INFO("ExitCodeHandler thread getting process status: %ld\n", process); |
+ mx_info_process_t proc_info; |
+ mx_status_t status = mx_object_get_info( |
+ process, MX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL); |
+ if (status < 0) { |
+ FATAL1("mx_object_get_info failed on process handle: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ |
+ const int return_code = proc_info.return_code; |
+ status = mx_handle_close(process); |
+ if (status < 0) { |
+ FATAL1("Failed to close process handle: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ LOG_INFO("ExitCodeHandler thread process %ld exited with %d\n", process, |
+ return_code); |
+ |
+ const intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(process); |
+ LOG_INFO("ExitCodeHandler thread sending %ld code %d on fd %ld\n", process, |
+ return_code, exit_code_fd); |
+ if (exit_code_fd != 0) { |
+ int exit_message[2]; |
+ exit_message[0] = return_code; |
+ exit_message[1] = 0; // Do not negate return_code. |
+ intptr_t result = FDUtils::WriteToBlocking(exit_code_fd, &exit_message, |
+ sizeof(exit_message)); |
+ ASSERT((result == -1) || (result == sizeof(exit_code_fd))); |
+ if ((result == -1) && (errno != EPIPE)) { |
+ int err = errno; |
+ FATAL1("Failed to write exit code to pipe: %d\n", err); |
+ } |
+ LOG_INFO("ExitCodeHandler thread wrote %ld bytes to fd %ld\n", result, |
+ exit_code_fd); |
+ LOG_INFO("ExitCodeHandler thread removing process %ld from list\n", |
+ process); |
+ ProcessInfoList::RemoveProcess(process); |
+ } |
+ } |
+ |
+ static void HandleInterruptMsg() { |
+ ASSERT(items_[0].handle == interrupt_out_); |
+ ASSERT(items_[0].waitfor == MX_SOCKET_READABLE); |
+ ASSERT((items_[0].pending & MX_SOCKET_READABLE) != 0); |
+ while (true) { |
+ Message msg; |
+ size_t actual = 0; |
+ LOG_INFO("ExitCodeHandler thread reading interrupt message\n"); |
+ mx_status_t status = |
+ mx_socket_read(interrupt_out_, 0, &msg, sizeof(msg), &actual); |
+ if (status == ERR_SHOULD_WAIT) { |
+ LOG_INFO("ExitCodeHandler thread done reading interrupt messages\n"); |
+ return; |
+ } |
+ if (status < 0) { |
+ FATAL1("Failed to read exit handler interrupt handle: %s\n", |
+ mx_status_get_string(status)); |
+ } |
+ if (actual < sizeof(msg)) { |
+ FATAL1("Short read from exit handler interrupt handle: %ld\n", actual); |
+ } |
+ switch (msg.command) { |
+ case Message::kShutdown: |
+ LOG_INFO("ExitCodeHandler thread got shutdown message\n"); |
+ do_shutdown_ = true; |
+ break; |
+ case Message::kAdd: |
+ LOG_INFO("ExitCodeHandler thread got add message: %ld\n", msg.handle); |
+ AddItem(msg.handle); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ static void AddItem(mx_handle_t h) { |
+ if (item_count_ == item_capacity_) { |
+ item_capacity_ = item_capacity_ + (item_capacity_ >> 1); |
+ items_ = |
+ reinterpret_cast<mx_wait_item_t*>(realloc(items_, item_capacity_)); |
+ items_to_remove_ = reinterpret_cast<intptr_t*>( |
+ realloc(items_to_remove_, item_capacity_)); |
+ } |
+ LOG_INFO("ExitCodeHandler thread adding item %ld at %ld\n", h, item_count_); |
+ items_[item_count_].handle = h; |
+ items_[item_count_].waitfor = MX_TASK_TERMINATED; |
+ items_[item_count_].pending = MX_SIGNAL_NONE; |
+ item_count_++; |
+ } |
+ |
+ static void RemoveItem(intptr_t idx) { |
+ LOG_INFO("ExitCodeHandler thread removing item %ld at %ld\n", |
+ items_[idx].handle, idx); |
+ ASSERT(idx != 0); |
+ const intptr_t last = item_count_ - 1; |
+ items_[idx].handle = MX_HANDLE_INVALID; |
+ items_[idx].waitfor = MX_SIGNAL_NONE; |
+ items_[idx].pending = MX_SIGNAL_NONE; |
+ if (idx != last) { |
+ items_[idx] = items_[last]; |
+ } |
+ item_count_--; |
+ } |
+ |
+ // Interrupt message pipe. |
+ static mx_handle_t interrupt_in_; |
+ static mx_handle_t interrupt_out_; |
+ |
+ // Accessed only by the ExitCodeHandler thread. |
+ static mx_wait_item_t* items_; |
+ static intptr_t* items_to_remove_; |
+ static intptr_t item_count_; |
+ static intptr_t item_capacity_; |
+ |
+ // Protected by monitor_. |
+ static bool do_shutdown_; |
+ static bool terminate_done_; |
+ static bool running_; |
+ static Monitor* monitor_; |
+ |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); |
+}; |
+ |
+mx_handle_t ExitCodeHandler::interrupt_in_ = MX_HANDLE_INVALID; |
+mx_handle_t ExitCodeHandler::interrupt_out_ = MX_HANDLE_INVALID; |
+mx_wait_item_t* ExitCodeHandler::items_ = NULL; |
+intptr_t* ExitCodeHandler::items_to_remove_ = NULL; |
+intptr_t ExitCodeHandler::item_count_ = 0; |
+intptr_t ExitCodeHandler::item_capacity_ = 0; |
+ |
+bool ExitCodeHandler::do_shutdown_ = false; |
+bool ExitCodeHandler::running_ = false; |
+bool ExitCodeHandler::terminate_done_ = false; |
+Monitor* ExitCodeHandler::monitor_ = new Monitor(); |
+ |
+void Process::TerminateExitCodeHandler() { |
+ ExitCodeHandler::Terminate(); |
} |
-void Process::ClearSignalHandler(intptr_t signal) { |
- UNIMPLEMENTED(); |
+intptr_t Process::CurrentProcessId() { |
+ return static_cast<intptr_t>(getpid()); |
} |
+ |
bool Process::Wait(intptr_t pid, |
intptr_t in, |
intptr_t out, |
@@ -47,11 +435,230 @@ bool Process::Wait(intptr_t pid, |
return false; |
} |
+ |
bool Process::Kill(intptr_t id, int signal) { |
UNIMPLEMENTED(); |
return false; |
} |
+ |
+class ProcessStarter { |
+ public: |
+ ProcessStarter(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_event, |
+ char** os_error_message) |
+ : path_(path), |
+ working_directory_(working_directory), |
+ mode_(mode), |
+ in_(in), |
+ out_(out), |
+ err_(err), |
+ id_(id), |
+ exit_event_(exit_event), |
+ os_error_message_(os_error_message) { |
+ LOG_INFO("ProcessStarter: ctor %s with %ld args, mode = %d\n", path, |
+ arguments_length, mode); |
+ |
+ read_in_ = -1; |
+ read_err_ = -1; |
+ write_out_ = -1; |
+ |
+ program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
+ (arguments_length + 2) * sizeof(*program_arguments_))); |
+ program_arguments_[0] = const_cast<char*>(path_); |
+ for (int i = 0; i < arguments_length; i++) { |
+ program_arguments_[i + 1] = arguments[i]; |
+ } |
+ program_arguments_[arguments_length + 1] = NULL; |
+ program_arguments_count_ = arguments_length + 1; |
+ |
+ program_environment_ = NULL; |
+ if (environment != NULL) { |
+ program_environment_ = reinterpret_cast<char**>(Dart_ScopeAllocate( |
+ (environment_length + 1) * sizeof(*program_environment_))); |
+ for (int i = 0; i < environment_length; i++) { |
+ program_environment_[i] = environment[i]; |
+ } |
+ program_environment_[environment_length] = NULL; |
+ } |
+ |
+ binary_vmo_ = MX_HANDLE_INVALID; |
+ launchpad_ = NULL; |
+ } |
+ |
+ ~ProcessStarter() { |
+ if (binary_vmo_ != MX_HANDLE_INVALID) { |
+ mx_handle_close(binary_vmo_); |
+ } |
+ if (launchpad_ != NULL) { |
+ launchpad_destroy(launchpad_); |
+ } |
+ if (read_in_ != -1) { |
+ close(read_in_); |
+ } |
+ if (read_err_ != -1) { |
+ close(read_err_); |
+ } |
+ if (write_out_ != -1) { |
+ close(write_out_); |
+ } |
+ } |
+ |
+ int Start() { |
+ LOG_INFO("ProcessStarter: Start()\n"); |
+ int exit_pipe_fds[2]; |
+ intptr_t result = NO_RETRY_EXPECTED(pipe(exit_pipe_fds)); |
+ if (result != 0) { |
+ *os_error_message_ = DartUtils::ScopedCopyCString( |
+ "Failed to create exit code pipe for process start."); |
+ return result; |
+ } |
+ LOG_INFO("ProcessStarter: Start() set up exit_pipe_fds (%d, %d)\n", |
+ exit_pipe_fds[0], exit_pipe_fds[1]); |
+ |
+ mx_status_t status = SetupLaunchpad(); |
+ if (status != NO_ERROR) { |
+ close(exit_pipe_fds[0]); |
+ close(exit_pipe_fds[1]); |
+ return status; |
+ } |
+ |
+ LOG_INFO("ProcessStarter: Start() Calling launchpad_start\n"); |
+ mx_handle_t process = launchpad_start(launchpad_); |
+ launchpad_destroy(launchpad_); |
+ launchpad_ = NULL; |
+ if (process < 0) { |
+ LOG_INFO("ProcessStarter: Start() launchpad_start failed\n"); |
+ const intptr_t kMaxMessageSize = 256; |
+ close(exit_pipe_fds[0]); |
+ close(exit_pipe_fds[1]); |
+ char* message = DartUtils::ScopedCString(kMaxMessageSize); |
+ snprintf(message, kMaxMessageSize, "%s:%d: launchpad_start failed: %s\n", |
+ __FILE__, __LINE__, mx_status_get_string(process)); |
+ *os_error_message_ = message; |
+ return process; |
+ } |
+ |
+ LOG_INFO("ProcessStarter: Start() adding %ld to list with exit_pipe %d\n", |
+ process, exit_pipe_fds[1]); |
+ ProcessInfoList::AddProcess(process, exit_pipe_fds[1]); |
+ ExitCodeHandler::Start(); |
+ ExitCodeHandler::Add(process); |
+ |
+ *id_ = process; |
+ FDUtils::SetNonBlocking(read_in_); |
+ *in_ = read_in_; |
+ read_in_ = -1; |
+ FDUtils::SetNonBlocking(read_err_); |
+ *err_ = read_err_; |
+ read_err_ = -1; |
+ FDUtils::SetNonBlocking(write_out_); |
+ *out_ = write_out_; |
+ write_out_ = -1; |
+ FDUtils::SetNonBlocking(exit_pipe_fds[0]); |
+ *exit_event_ = exit_pipe_fds[0]; |
+ return 0; |
+ } |
+ |
+ private: |
+#define CHECK_FOR_ERROR(status, msg) \ |
+ if (status < 0) { \ |
+ const intptr_t kMaxMessageSize = 256; \ |
+ char* message = DartUtils::ScopedCString(kMaxMessageSize); \ |
+ snprintf(message, kMaxMessageSize, "%s:%d: %s: %s\n", __FILE__, __LINE__, \ |
+ msg, mx_status_get_string(status)); \ |
+ *os_error_message_ = message; \ |
+ return status; \ |
+ } |
+ |
+ mx_status_t SetupLaunchpad() { |
+ mx_handle_t binary_vmo = launchpad_vmo_from_file(path_); |
+ CHECK_FOR_ERROR(binary_vmo, "launchpad_vmo_from_file"); |
+ binary_vmo_ = binary_vmo; |
+ |
+ launchpad_t* lp; |
+ mx_status_t status; |
+ |
+ status = launchpad_create(0, program_arguments_[0], &lp); |
+ CHECK_FOR_ERROR(status, "launchpad_create"); |
+ launchpad_ = lp; |
+ |
+ status = |
+ launchpad_arguments(lp, program_arguments_count_, program_arguments_); |
+ CHECK_FOR_ERROR(status, "launchpad_arguments"); |
+ |
+ status = launchpad_environ(lp, program_environment_); |
+ CHECK_FOR_ERROR(status, "launchpad_environ"); |
+ |
+ // TODO(zra): Use the supplied working directory when launchpad adds an |
+ // API to set it. |
+ |
+ status = launchpad_clone_mxio_root(lp); |
+ CHECK_FOR_ERROR(status, "launchpad_clone_mxio_root"); |
+ |
+ status = launchpad_add_pipe(lp, &write_out_, 0); |
+ CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
+ |
+ status = launchpad_add_pipe(lp, &read_in_, 1); |
+ CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
+ |
+ status = launchpad_add_pipe(lp, &read_err_, 2); |
+ CHECK_FOR_ERROR(status, "launchpad_add_pipe"); |
+ |
+ status = launchpad_add_vdso_vmo(lp); |
+ CHECK_FOR_ERROR(status, "launchpad_add_vdso_vmo"); |
+ |
+ status = launchpad_elf_load(lp, binary_vmo); |
+ CHECK_FOR_ERROR(status, "launchpad_elf_load"); |
+ binary_vmo_ = MX_HANDLE_INVALID; // launchpad_elf_load consumes the handle. |
+ |
+ status = launchpad_load_vdso(lp, MX_HANDLE_INVALID); |
+ CHECK_FOR_ERROR(status, "launchpad_load_vdso"); |
+ |
+ status = launchpad_clone_mxio_cwd(lp); |
+ CHECK_FOR_ERROR(status, "launchpad_clone_mxio_cwd"); |
+ |
+ return NO_ERROR; |
+ } |
+ |
+#undef CHECK_FOR_ERROR |
+ |
+ int read_in_; // Pipe for stdout to child process. |
+ int read_err_; // Pipe for stderr to child process. |
+ int write_out_; // Pipe for stdin to child process. |
+ |
+ char** program_arguments_; |
+ intptr_t program_arguments_count_; |
+ char** program_environment_; |
+ |
+ mx_handle_t binary_vmo_; |
+ launchpad_t* launchpad_; |
+ |
+ const char* path_; |
+ const char* working_directory_; |
+ ProcessStartMode mode_; |
+ intptr_t* in_; |
+ intptr_t* out_; |
+ intptr_t* err_; |
+ intptr_t* id_; |
+ intptr_t* exit_event_; |
+ char** os_error_message_; |
+ |
+ DISALLOW_ALLOCATION(); |
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); |
+}; |
+ |
+ |
int Process::Start(const char* path, |
char* arguments[], |
intptr_t arguments_length, |
@@ -65,10 +672,28 @@ int Process::Start(const char* path, |
intptr_t* id, |
intptr_t* exit_event, |
char** os_error_message) { |
+ if (mode != kNormal) { |
+ *os_error_message = DartUtils::ScopedCopyCString( |
+ "Only ProcessStartMode.NORMAL is supported on this platform"); |
+ return -1; |
+ } |
+ ProcessStarter starter(path, arguments, arguments_length, working_directory, |
+ environment, environment_length, mode, in, out, err, |
+ id, exit_event, os_error_message); |
+ return starter.Start(); |
+} |
+ |
+ |
+intptr_t Process::SetSignalHandler(intptr_t signal) { |
UNIMPLEMENTED(); |
return -1; |
} |
+ |
+void Process::ClearSignalHandler(intptr_t signal) { |
+ UNIMPLEMENTED(); |
+} |
+ |
} // namespace bin |
} // namespace dart |