blob: 3efe7f795b20f7b1d91b024990ba6166f38e8c35 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/lib/process/process_builder.h"
#include <fcntl.h>
#include <lib/fdio/io.h>
#include <lib/fdio/limits.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/util.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <zircon/dlfcn.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include "lib/component/cpp/environment_services.h"
namespace process {
ProcessBuilder::ProcessBuilder() {
component::ConnectToEnvironmentService(launcher_.NewRequest());
}
ProcessBuilder::ProcessBuilder(zx::job job) : ProcessBuilder() {
launch_info_.job = std::move(job);
}
ProcessBuilder::~ProcessBuilder() = default;
void ProcessBuilder::LoadVMO(zx::vmo executable) {
launch_info_.executable = std::move(executable);
}
zx_status_t ProcessBuilder::LoadPath(const std::string& path) {
int fd = open(path.c_str(), O_RDONLY);
if (fd < 0)
return ZX_ERR_IO;
zx_status_t status =
fdio_get_vmo_clone(fd, launch_info_.executable.reset_and_get_address());
close(fd);
if (status == ZX_OK) {
const char* name = path.c_str();
if (path.length() >= ZX_MAX_NAME_LEN) {
size_t offset = path.rfind('/');
if (offset != std::string::npos) {
name += offset + 1;
}
}
launch_info_.executable.set_property(ZX_PROP_NAME, name, strlen(name));
}
return status;
}
void ProcessBuilder::AddArgs(const std::vector<std::string>& argv) {
if (argv.empty())
return;
if (launch_info_.name->empty())
launch_info_.name.reset(argv[0]);
fidl::VectorPtr<fidl::StringPtr> args;
for (const auto& arg : argv)
args.push_back(arg);
launcher_->AddArgs(std::move(args));
}
void ProcessBuilder::AddHandle(uint32_t id, zx::handle handle) {
handles_.push_back(fuchsia::process::HandleInfo{
.id = id,
.handle = std::move(handle),
});
}
void ProcessBuilder::AddHandles(
std::vector<fuchsia::process::HandleInfo> handles) {
handles_->insert(handles_->end(), std::make_move_iterator(handles.begin()),
std::make_move_iterator(handles.end()));
}
void ProcessBuilder::SetDefaultJob(zx::job job) {
handles_.push_back(fuchsia::process::HandleInfo{
.id = PA_JOB_DEFAULT,
.handle = std::move(job),
});
}
void ProcessBuilder::SetName(std::string name) {
launch_info_.name.reset(std::move(name));
}
void ProcessBuilder::CloneJob() {
zx::job duplicate_job;
if (launch_info_.job)
launch_info_.job.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_job);
else
zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_job);
SetDefaultJob(std::move(duplicate_job));
}
void ProcessBuilder::CloneLdsvc() {
fuchsia::process::HandleInfo handle_info;
handle_info.id = PA_LDSVC_LOADER;
zx_status_t status =
dl_clone_loader_service(handle_info.handle.reset_and_get_address());
ZX_ASSERT(status == ZX_OK);
handles_.push_back(std::move(handle_info));
}
void ProcessBuilder::CloneNamespace() {
fdio_flat_namespace_t* flat = nullptr;
zx_status_t status = fdio_ns_export_root(&flat);
if (status == ZX_OK) {
fidl::VectorPtr<fuchsia::process::NameInfo> names;
for (size_t i = 0; i < flat->count; ++i) {
names.push_back(fuchsia::process::NameInfo{
.path = flat->path[i],
.directory = zx::channel(flat->handle[i]),
});
}
launcher_->AddNames(std::move(names));
}
free(flat);
}
void ProcessBuilder::CloneStdio() {
// These file descriptors might be closed. Skip over erros cloning them.
CloneFileDescriptor(STDIN_FILENO, STDIN_FILENO);
CloneFileDescriptor(STDOUT_FILENO, STDOUT_FILENO);
CloneFileDescriptor(STDERR_FILENO, STDERR_FILENO);
}
void ProcessBuilder::CloneEnvironment() {
fidl::VectorPtr<fidl::StringPtr> env;
for (size_t i = 0; environ[i]; ++i)
env.push_back(environ[i]);
launcher_->AddEnvirons(std::move(env));
}
void ProcessBuilder::CloneAll() {
CloneJob();
CloneLdsvc();
CloneNamespace();
CloneStdio();
CloneEnvironment();
}
zx_status_t ProcessBuilder::CloneFileDescriptor(int local_fd, int target_fd) {
zx_handle_t fdio_handles[FDIO_MAX_HANDLES];
uint32_t fdio_types[FDIO_MAX_HANDLES];
zx_status_t status =
fdio_clone_fd(local_fd, target_fd, fdio_handles, fdio_types);
if (status < ZX_OK)
return status;
for (int i = 0; i < status; ++i) {
handles_.push_back(fuchsia::process::HandleInfo{
.id = fdio_types[i],
.handle = zx::handle(fdio_handles[i]),
});
}
return ZX_OK;
}
zx_status_t ProcessBuilder::Prepare(std::string* error_message) {
zx_status_t status = ZX_OK;
launcher_->AddHandles(std::move(handles_));
if (!launch_info_.job) {
status = zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS,
&launch_info_.job);
if (status != ZX_OK)
return status;
}
fuchsia::process::CreateWithoutStartingResult result;
status = launcher_->CreateWithoutStarting(std::move(launch_info_), &result);
if (status != ZX_OK)
return status;
if (result.status != ZX_OK) {
if (error_message)
*error_message = result.error_message;
return result.status;
}
if (!result.data)
return ZX_ERR_INVALID_ARGS;
data_ = std::move(*result.data);
return ZX_OK;
}
zx_status_t ProcessBuilder::Start(zx::process* process_out) {
zx_status_t status =
zx_process_start(data_.process.get(), data_.thread.get(), data_.entry,
data_.sp, data_.bootstrap.release(), data_.vdso_base);
if (status == ZX_OK && process_out)
*process_out = std::move(data_.process);
return status;
}
} // namespace process