Files
sdk/runtime/bin/process_fuchsia.cc
T
Adam Lesinski 919f68d8ac [process][fuchsia] Clone UTC clock when launching processes
Fuchsia is transitioning from UTC time being tracked in the kernel,
to userspace UTC clock handles.

This preserves the existing behavior that each dart process
has access to the global UTC clock.

Bug: 43629
Change-Id: I23005bccd8bec09ed6db7c6f475b6f5d6e84613c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/165500
Auto-Submit: Adam Lesinski <adamlesinski@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: Zach Anderson <zra@google.com>
2020-10-01 15:25:58 +00:00

871 lines
27 KiB
C++

// Copyright (c) 2016, 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.
#include "platform/globals.h"
#if defined(HOST_OS_FUCHSIA)
#include "bin/process.h"
#include <errno.h>
#include <fcntl.h>
#include <lib/fdio/io.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/spawn.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include "bin/dartutils.h"
#include "bin/eventhandler.h"
#include "bin/fdutils.h"
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/namespace.h"
#include "bin/namespace_fuchsia.h"
#include "platform/signal_blocker.h"
#include "platform/syslog.h"
#include "platform/utils.h"
// #define PROCESS_LOGGING 1
#if defined(PROCESS_LOGGING)
#define LOG_ERR(msg, ...) Syslog::PrintErr("Dart Process: " msg, ##__VA_ARGS__)
#define LOG_INFO(msg, ...) Syslog::Print("Dart Process: " msg, ##__VA_ARGS__)
#else
#define LOG_ERR(msg, ...)
#define LOG_INFO(msg, ...)
#endif // defined(PROCESS_LOGGING)
namespace dart {
namespace bin {
int Process::global_exit_code_ = 0;
Mutex* Process::global_exit_code_mutex_ = nullptr;
Process::ExitHook Process::exit_hook_ = NULL;
// 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(zx_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) {
LOG_ERR("Failed to close process exit code pipe");
}
zx_handle_close(process_);
}
zx_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; }
private:
zx_handle_t process_;
intptr_t exit_pipe_fd_;
ProcessInfo* next_;
DISALLOW_COPY_AND_ASSIGN(ProcessInfo);
};
// Singly-linked list of ProcessInfo objects for all active processes
// started from Dart.
class ProcessInfoList {
public:
static void Init();
static void Cleanup();
static void AddProcess(zx_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(zx_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 bool Exists(zx_handle_t process) {
return LookupProcessExitFd(process) != 0;
}
static void RemoveProcess(zx_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_ = nullptr;
// 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:
static void Init();
static void Cleanup();
// 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");
zx_status_t status = zx_port_create(0, &port_);
if (status != ZX_OK) {
FATAL1("ExitCodeHandler: zx_port_create failed: %s\n",
zx_status_get_string(status));
return;
}
// Start thread that handles process exits when wait returns.
intptr_t result =
Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
if (result != 0) {
FATAL1("Failed to start exit code handler worker thread %ld", result);
}
running_ = true;
}
static zx_status_t Add(zx_handle_t process) {
MonitorLocker locker(monitor_);
LOG_INFO("ExitCodeHandler Adding Process: %u\n", process);
return zx_object_wait_async(process, port_, static_cast<uint64_t>(process),
ZX_TASK_TERMINATED, ZX_WAIT_ASYNC_ONCE);
}
static void Terminate() {
MonitorLocker locker(monitor_);
if (!running_) {
return;
}
running_ = false;
LOG_INFO("ExitCodeHandler Terminating\n");
SendShutdownMessage();
while (!terminate_done_) {
monitor_->Wait(Monitor::kNoTimeout);
}
zx_handle_close(port_);
LOG_INFO("ExitCodeHandler Terminated\n");
}
private:
static const uint64_t kShutdownPacketKey = 1;
static void SendShutdownMessage() {
zx_port_packet_t pkt;
pkt.key = kShutdownPacketKey;
zx_status_t status = zx_port_queue(port_, &pkt);
if (status != ZX_OK) {
Syslog::PrintErr("ExitCodeHandler: zx_port_queue failed: %s\n",
zx_status_get_string(status));
}
}
// 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");
zx_port_packet_t pkt;
while (true) {
zx_status_t status = zx_port_wait(port_, ZX_TIME_INFINITE, &pkt);
if (status != ZX_OK) {
FATAL1("ExitCodeHandler: zx_port_wait failed: %s\n",
zx_status_get_string(status));
}
if (pkt.type == ZX_PKT_TYPE_USER) {
ASSERT(pkt.key == kShutdownPacketKey);
break;
}
zx_handle_t process = static_cast<zx_handle_t>(pkt.key);
zx_signals_t observed = pkt.signal.observed;
if ((observed & ZX_TASK_TERMINATED) == ZX_SIGNAL_NONE) {
LOG_ERR("ExitCodeHandler: Unexpected signals, process %u: %ux\n",
process, observed);
}
SendProcessStatus(process);
}
LOG_INFO("ExitCodeHandler thread shutting down\n");
terminate_done_ = true;
monitor_->Notify();
}
static void SendProcessStatus(zx_handle_t process) {
LOG_INFO("ExitCodeHandler thread getting process status: %u\n", process);
int return_code = -1;
zx_info_process_t proc_info;
zx_status_t status = zx_object_get_info(
process, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), NULL, NULL);
if (status != ZX_OK) {
Syslog::PrintErr("ExitCodeHandler: zx_object_get_info failed: %s\n",
zx_status_get_string(status));
} else {
return_code = proc_info.return_code;
}
zx_handle_close(process);
LOG_INFO("ExitCodeHandler thread process %u exited with %d\n", process,
return_code);
const intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(process);
LOG_INFO("ExitCodeHandler thread sending %u code %d on fd %ld\n", process,
return_code, exit_code_fd);
if (exit_code_fd != 0) {
int exit_message[2];
exit_message[0] = abs(return_code);
exit_message[1] = return_code >= 0 ? 0 : 1;
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;
Syslog::PrintErr("Failed to write exit code for process %d: errno=%d\n",
process, err);
}
LOG_INFO("ExitCodeHandler thread wrote %ld bytes to fd %ld\n", result,
exit_code_fd);
LOG_INFO("ExitCodeHandler thread removing process %u from list\n",
process);
ProcessInfoList::RemoveProcess(process);
} else {
LOG_ERR("ExitCodeHandler: Process %u not found\n", process);
}
}
static zx_handle_t port_;
// Protected by monitor_.
static bool terminate_done_;
static bool running_;
static Monitor* monitor_;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler);
};
zx_handle_t ExitCodeHandler::port_ = ZX_HANDLE_INVALID;
bool ExitCodeHandler::running_ = false;
bool ExitCodeHandler::terminate_done_ = false;
Monitor* ExitCodeHandler::monitor_ = nullptr;
void Process::TerminateExitCodeHandler() {
ExitCodeHandler::Terminate();
}
intptr_t Process::CurrentProcessId() {
return static_cast<intptr_t>(getpid());
}
int64_t Process::CurrentRSS() {
zx_info_task_stats_t task_stats;
zx_handle_t process = zx_process_self();
zx_status_t status = zx_object_get_info(
process, ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), NULL, NULL);
if (status != ZX_OK) {
// TODO(zra): Translate this to a Unix errno.
errno = status;
return -1;
}
return task_stats.mem_private_bytes + task_stats.mem_shared_bytes;
}
int64_t Process::MaxRSS() {
// There is currently no way to get the high watermark value on Fuchsia, so
// just return the current RSS value.
return CurrentRSS();
}
class IOHandleScope {
public:
explicit IOHandleScope(IOHandle* io_handle) : io_handle_(io_handle) {}
~IOHandleScope() {
io_handle_->Close();
io_handle_->Release();
}
private:
IOHandle* io_handle_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(IOHandleScope);
};
bool Process::Wait(intptr_t pid,
intptr_t in,
intptr_t out,
intptr_t err,
intptr_t exit_event,
ProcessResult* result) {
IOHandle* out_iohandle = reinterpret_cast<IOHandle*>(out);
IOHandle* err_iohandle = reinterpret_cast<IOHandle*>(err);
IOHandle* exit_iohandle = reinterpret_cast<IOHandle*>(exit_event);
// There is no return from this function using Dart_PropagateError
// as memory used by the buffer lists is freed through their
// destructors.
BufferList out_data;
BufferList err_data;
union {
uint8_t bytes[8];
int32_t ints[2];
} exit_code_data;
// Create a port, which is like an epoll() fd on Linux.
zx_handle_t port;
zx_status_t status = zx_port_create(0, &port);
if (status != ZX_OK) {
Syslog::PrintErr("Process::Wait: zx_port_create failed: %s\n",
zx_status_get_string(status));
return false;
}
IOHandle* out_tmp = out_iohandle;
IOHandle* err_tmp = err_iohandle;
IOHandle* exit_tmp = exit_iohandle;
const uint64_t out_key = reinterpret_cast<uint64_t>(out_tmp);
const uint64_t err_key = reinterpret_cast<uint64_t>(err_tmp);
const uint64_t exit_key = reinterpret_cast<uint64_t>(exit_tmp);
const uint32_t events = POLLRDHUP | POLLIN;
if (!out_tmp->AsyncWait(port, events, out_key)) {
return false;
}
if (!err_tmp->AsyncWait(port, events, err_key)) {
return false;
}
if (!exit_tmp->AsyncWait(port, events, exit_key)) {
return false;
}
while ((out_tmp != NULL) || (err_tmp != NULL) || (exit_tmp != NULL)) {
zx_port_packet_t pkt;
status = zx_port_wait(port, ZX_TIME_INFINITE, &pkt);
if (status != ZX_OK) {
Syslog::PrintErr("Process::Wait: zx_port_wait failed: %s\n",
zx_status_get_string(status));
return false;
}
IOHandle* event_handle = reinterpret_cast<IOHandle*>(pkt.key);
const intptr_t event_mask = event_handle->WaitEnd(pkt.signal.observed);
if (event_handle == out_tmp) {
if ((event_mask & POLLIN) != 0) {
const intptr_t avail = FDUtils::AvailableBytes(out_tmp->fd());
if (!out_data.Read(out_tmp->fd(), avail)) {
return false;
}
}
if ((event_mask & POLLRDHUP) != 0) {
out_tmp->CancelWait(port, out_key);
out_tmp = NULL;
}
} else if (event_handle == err_tmp) {
if ((event_mask & POLLIN) != 0) {
const intptr_t avail = FDUtils::AvailableBytes(err_tmp->fd());
if (!err_data.Read(err_tmp->fd(), avail)) {
return false;
}
}
if ((event_mask & POLLRDHUP) != 0) {
err_tmp->CancelWait(port, err_key);
err_tmp = NULL;
}
} else if (event_handle == exit_tmp) {
if ((event_mask & POLLIN) != 0) {
const intptr_t avail = FDUtils::AvailableBytes(exit_tmp->fd());
if (avail == 8) {
intptr_t b =
NO_RETRY_EXPECTED(read(exit_tmp->fd(), exit_code_data.bytes, 8));
if (b != 8) {
return false;
}
}
}
if ((event_mask & POLLRDHUP) != 0) {
exit_tmp->CancelWait(port, exit_key);
exit_tmp = NULL;
}
} else {
Syslog::PrintErr("Process::Wait: Unexpected wait key: %p\n",
event_handle);
}
if (out_tmp != NULL) {
if (!out_tmp->AsyncWait(port, events, out_key)) {
return false;
}
}
if (err_tmp != NULL) {
if (!err_tmp->AsyncWait(port, events, err_key)) {
return false;
}
}
if (exit_tmp != NULL) {
if (!exit_tmp->AsyncWait(port, events, exit_key)) {
return false;
}
}
}
// All handles closed and all data read.
result->set_stdout_data(out_data.GetData());
result->set_stderr_data(err_data.GetData());
DEBUG_ASSERT(out_data.IsEmpty());
DEBUG_ASSERT(err_data.IsEmpty());
// Calculate the exit code.
intptr_t exit_code = exit_code_data.ints[0];
intptr_t negative = exit_code_data.ints[1];
if (negative != 0) {
exit_code = -exit_code;
}
result->set_exit_code(exit_code);
// Close the process handle.
zx_handle_t process = static_cast<zx_handle_t>(pid);
zx_handle_close(process);
return true;
}
bool Process::Kill(intptr_t id, int signal) {
LOG_INFO("Sending signal %d to process with id %ld\n", signal, id);
// zx_task_kill is definitely going to kill the process.
if ((signal != SIGTERM) && (signal != SIGKILL)) {
LOG_ERR("Signal %d not supported\n", signal);
errno = ENOSYS;
return false;
}
// We can only use zx_task_kill if we know id is a process handle, and we only
// know that for sure if it's in our list.
zx_handle_t process = static_cast<zx_handle_t>(id);
if (!ProcessInfoList::Exists(process)) {
LOG_ERR("Process %ld wasn't in the ProcessInfoList\n", id);
errno = ESRCH; // No such process.
return false;
}
zx_status_t status = zx_task_kill(process);
if (status != ZX_OK) {
LOG_ERR("zx_task_kill failed: %s\n", zx_status_get_string(status));
errno = EPERM; // TODO(zra): Figure out what it really should be.
return false;
}
LOG_INFO("Signal %d sent successfully to process %ld\n", signal, id);
return true;
}
class ProcessStarter {
public:
ProcessStarter(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_event,
char** os_error_message)
: namespc_(namespc),
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_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;
}
}
~ProcessStarter() {
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]);
NamespaceScope ns(namespc_, path_);
const int pathfd =
TEMP_FAILURE_RETRY(openat(ns.fd(), ns.path(), O_RDONLY));
zx_handle_t vmo = ZX_HANDLE_INVALID;
zx_status_t status = fdio_get_vmo_clone(pathfd, &vmo);
close(pathfd);
if (status != ZX_OK) {
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
*os_error_message_ = DartUtils::ScopedCopyCString(
"Failed to load executable for process start.");
return status;
}
// After reading the binary into a VMO, we need to mark it as executable,
// since the VMO returned by fdio_get_vmo_clone should be read-only.
status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
if (status != ZX_OK) {
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
*os_error_message_ = DartUtils::ScopedCopyCString(
"Failed to mark binary as executable for process start.");
return status;
}
fdio_spawn_action_t* actions;
const intptr_t actions_count = BuildSpawnActions(
namespc_->namespc()->fdio_ns(), &actions);
if (actions_count < 0) {
zx_handle_close(vmo);
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
*os_error_message_ = DartUtils::ScopedCopyCString(
"Failed to build spawn actions array.");
return ZX_ERR_IO;
}
// TODO(zra): Use the supplied working directory when fdio_spawn_vmo adds an
// API to set it.
LOG_INFO("ProcessStarter: Start() Calling fdio_spawn_vmo\n");
zx_handle_t process = ZX_HANDLE_INVALID;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
uint32_t flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_DEFAULT_LDSVC |
FDIO_SPAWN_CLONE_UTC_CLOCK;
status = fdio_spawn_vmo(ZX_HANDLE_INVALID, flags, vmo, program_arguments_,
program_environment_, actions_count, actions,
&process, err_msg);
// Handles are consumed by fdio_spawn_vmo even if it fails.
delete[] actions;
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: Start() fdio_spawn_vmo failed\n");
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
ReportStartError(err_msg);
return status;
}
LOG_INFO("ProcessStarter: Start() adding %u to list with exit_pipe %d\n",
process, exit_pipe_fds[1]);
ProcessInfoList::AddProcess(process, exit_pipe_fds[1]);
ExitCodeHandler::Start();
status = ExitCodeHandler::Add(process);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: ExitCodeHandler: Add failed: %s\n",
zx_status_get_string(status));
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
zx_task_kill(process);
ProcessInfoList::RemoveProcess(process);
ReportStartError(zx_status_get_string(status));
return status;
}
// The IOHandles allocated below are returned to Dart code. The Dart code
// calls into the runtime again to allocate a C++ Socket object, which
// becomes the native field of a Dart _NativeSocket object. The C++ Socket
// object and the EventHandler manage the lifetime of these IOHandles.
*id_ = process;
FDUtils::SetNonBlocking(read_in_);
*in_ = reinterpret_cast<intptr_t>(new IOHandle(read_in_));
read_in_ = -1;
FDUtils::SetNonBlocking(read_err_);
*err_ = reinterpret_cast<intptr_t>(new IOHandle(read_err_));
read_err_ = -1;
FDUtils::SetNonBlocking(write_out_);
*out_ = reinterpret_cast<intptr_t>(new IOHandle(write_out_));
write_out_ = -1;
FDUtils::SetNonBlocking(exit_pipe_fds[0]);
*exit_event_ = reinterpret_cast<intptr_t>(new IOHandle(exit_pipe_fds[0]));
return 0;
}
private:
void ReportStartError(const char* errormsg) {
const intptr_t kMaxMessageSize = 256;
char* message = DartUtils::ScopedCString(kMaxMessageSize);
snprintf(message, kMaxMessageSize, "Process start failed: %s\n", errormsg);
*os_error_message_ = message;
}
zx_status_t AddPipe(int target_fd, int* local_fd,
fdio_spawn_action_t* action) {
zx_status_t status = fdio_pipe_half(local_fd, &action->h.handle);
if (status != ZX_OK) return status;
action->action = FDIO_SPAWN_ACTION_ADD_HANDLE;
action->h.id = PA_HND(PA_HND_TYPE(PA_FD), target_fd);
return ZX_OK;
}
// Fills in 'actions_out' and returns action count.
intptr_t BuildSpawnActions(fdio_ns_t* ns, fdio_spawn_action_t** actions_out) {
const intptr_t fixed_actions_cnt = 4;
intptr_t ns_cnt = 0;
zx_status_t status;
// First, figure out how many namespace actions are needed.
fdio_flat_namespace_t* flat_ns = nullptr;
if (ns != nullptr) {
status = fdio_ns_export(ns, &flat_ns);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: BuildSpawnActions: fdio_ns_export: %s\n",
zx_status_get_string(status));
return -1;
}
ns_cnt = flat_ns->count;
}
// Allocate the actions array.
const intptr_t actions_cnt = ns_cnt + fixed_actions_cnt;
fdio_spawn_action_t* actions = new fdio_spawn_action_t[actions_cnt];
// Fill in the entries for passing stdin/out/err handles, and the program
// name.
status = AddPipe(0, &write_out_, &actions[0]);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: BuildSpawnActions: stdout AddPipe failed: %s\n",
zx_status_get_string(status));
if (flat_ns != nullptr) {
fdio_ns_free_flat_ns(flat_ns);
}
return -1;
}
status = AddPipe(1, &read_in_, &actions[1]);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: BuildSpawnActions: stdin AddPipe failed: %s\n",
zx_status_get_string(status));
if (flat_ns != nullptr) {
fdio_ns_free_flat_ns(flat_ns);
}
return -1;
}
status = AddPipe(2, &read_err_, &actions[2]);
if (status != ZX_OK) {
LOG_ERR("ProcessStarter: BuildSpawnActions: stderr AddPipe failed: %s\n",
zx_status_get_string(status));
if (flat_ns != nullptr) {
fdio_ns_free_flat_ns(flat_ns);
}
return -1;
}
actions[3] = {
.action = FDIO_SPAWN_ACTION_SET_NAME,
.name = {
.data = program_arguments_[0],
},
};
// Then fill in the namespace actions.
if (ns != nullptr) {
for (size_t i = 0; i < flat_ns->count; i++) {
actions[fixed_actions_cnt + i] = {
.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
.ns = {
.prefix = flat_ns->path[i],
.handle = flat_ns->handle[i],
},
};
}
free(flat_ns);
flat_ns = nullptr;
}
*actions_out = actions;
return actions_cnt;
}
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_;
char** program_environment_;
Namespace* namespc_;
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(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_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(namespc, 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) {
errno = ENOSYS;
return -1;
}
void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) {}
void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) {}
void ProcessInfoList::Init() {
ASSERT(ProcessInfoList::mutex_ == nullptr);
ProcessInfoList::mutex_ = new Mutex();
}
void ProcessInfoList::Cleanup() {
ASSERT(ProcessInfoList::mutex_ != nullptr);
delete ProcessInfoList::mutex_;
ProcessInfoList::mutex_ = nullptr;
}
void ExitCodeHandler::Init() {
ASSERT(ExitCodeHandler::monitor_ == nullptr);
ExitCodeHandler::monitor_ = new Monitor();
}
void ExitCodeHandler::Cleanup() {
ASSERT(ExitCodeHandler::monitor_ != nullptr);
delete ExitCodeHandler::monitor_;
ExitCodeHandler::monitor_ = nullptr;
}
void Process::Init() {
ExitCodeHandler::Init();
ProcessInfoList::Init();
ASSERT(Process::global_exit_code_mutex_ == nullptr);
Process::global_exit_code_mutex_ = new Mutex();
}
void Process::Cleanup() {
ASSERT(Process::global_exit_code_mutex_ != nullptr);
delete Process::global_exit_code_mutex_;
Process::global_exit_code_mutex_ = nullptr;
ProcessInfoList::Cleanup();
ExitCodeHandler::Cleanup();
}
} // namespace bin
} // namespace dart
#endif // defined(HOST_OS_FUCHSIA)