Files
sdk/runtime/bin/process_fuchsia.cc
T
asiva 7b25ce88d3 [dartdev] Use VmInteropHandler for invoking sub commands
Use VmInteropHandler for invoking sub commands instead of running them
in an isolate. Running sub commands in an isolate causes an increased footprint.
Changing this to use VmInteropHandler avoids the additional memory footprint.

Commands that need to use an AOT runtime for execution now exec the AOT
runtime and run the command.

TEST=ci

Change-Id: Ic96845b19951170effea3dd3619f798e2c72968a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402781
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
2025-01-16 09:15:01 -08:00

911 lines
28 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(DART_HOST_OS_FUCHSIA)
#include "bin/process.h"
#include <errno.h>
#include <fcntl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/fdio/directory.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_ = nullptr;
// 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 != nullptr) {
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 = nullptr;
ProcessInfo* current = active_processes_;
while (current != nullptr) {
if (current->process() == process) {
if (prev == nullptr) {
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_ = nullptr;
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) {
FATAL("ExitCodeHandler: zx_port_create failed: %s\n",
zx_status_get_string(status));
return;
}
// Start thread that handles process exits when wait returns.
Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0);
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 constexpr 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) {
FATAL("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), nullptr, nullptr);
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), nullptr, nullptr);
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 != nullptr) || (err_tmp != nullptr) ||
(exit_tmp != nullptr)) {
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 = nullptr;
}
} 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 = nullptr;
}
} 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 = nullptr;
}
} else {
Syslog::PrintErr("Process::Wait: Unexpected wait key: %p\n",
event_handle);
}
if (out_tmp != nullptr) {
if (!out_tmp->AsyncWait(port, events, out_key)) {
return false;
}
}
if (err_tmp != nullptr) {
if (!err_tmp->AsyncWait(port, events, err_key)) {
return false;
}
}
if (exit_tmp != nullptr) {
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,
const 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<const 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] = nullptr;
program_environment_ = nullptr;
if (environment != nullptr) {
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] = nullptr;
}
}
~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_);
int pathfd = -1;
zx_status_t status;
if (ns.fd() == AT_FDCWD) {
status = fdio_open_fd(
ns.path(),
static_cast<uint32_t>(fuchsia::io::OpenFlags::RIGHT_READABLE |
fuchsia::io::OpenFlags::RIGHT_EXECUTABLE),
&pathfd);
} else {
status = fdio_open_fd_at(
ns.fd(), ns.path(),
static_cast<uint32_t>(fuchsia::io::OpenFlags::RIGHT_READABLE |
fuchsia::io::OpenFlags::RIGHT_EXECUTABLE),
&pathfd);
}
if (status != ZX_OK) {
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
ReportStartError(
"Failed to load executable for process start (fdio_open_fd_at %s).",
zx_status_get_string(status));
return status;
}
zx_handle_t vmo = ZX_HANDLE_INVALID;
status = fdio_get_vmo_exec(pathfd, &vmo);
close(pathfd);
if (status != ZX_OK) {
close(exit_pipe_fds[0]);
close(exit_pipe_fds[1]);
ReportStartError(
"Failed to load executable for process start (fdio_get_vmo_exec %s).",
zx_status_get_string(status));
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("Process start failed: %s\n", 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("Process start failed: %s\n",
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* format, ...) PRINTF_ATTRIBUTE(2, 3) {
const intptr_t kMaxMessageSize = 256;
char* message = DartUtils::ScopedCString(kMaxMessageSize);
va_list args;
va_start(args, format);
vsnprintf(message, kMaxMessageSize, format, args);
va_end(args);
*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;
}
// clang-format off
actions[3] = {
.action = FDIO_SPAWN_ACTION_SET_NAME,
.name = {
.data = program_arguments_[0],
},
};
// clang-format on
// Then fill in the namespace actions.
if (ns != nullptr) {
for (size_t i = 0; i < flat_ns->count; i++) {
// clang-format off
actions[fixed_actions_cnt + i] = {
.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
.ns = {
.prefix = DartUtils::ScopedCopyCString(flat_ns->path[i]),
.handle = flat_ns->handle[i],
},
};
// clang-format on
flat_ns->handle[i] = ZX_HANDLE_INVALID;
}
fdio_ns_free_flat_ns(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.
const 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,
const 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();
}
// The command line dart utility does not run on Fuchsia, this functionality
// is not supported on that platform.
int Process::Exec(Namespace* namespc,
const char* path,
const char** arguments,
intptr_t arguments_length,
const char* working_directory,
char* errmsg,
intptr_t errmsg_len) {
snprintf(errmsg, errmsg_len,
"Process::Exec is not supported on this platform");
return -1;
}
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() {
active_processes_ = nullptr;
ASSERT(ProcessInfoList::mutex_ == nullptr);
ProcessInfoList::mutex_ = new Mutex();
}
void ProcessInfoList::Cleanup() {
ASSERT(ProcessInfoList::mutex_ != nullptr);
delete ProcessInfoList::mutex_;
ProcessInfoList::mutex_ = nullptr;
}
void ExitCodeHandler::Init() {
port_ = ZX_HANDLE_INVALID;
running_ = false;
terminate_done_ = false;
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(DART_HOST_OS_FUCHSIA)