| 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
|
|
|
|
|