blob: a4e38b8052a8b99ac55819481ba1310cf93da559 [file] [log] [blame]
// Copyright 2016 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 "fdio.h"
#include <fuchsia/boot/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/io.h>
#include <lib/fdio/spawn.h>
#include <lib/zircon-internal/paths.h>
#include <lib/zx/channel.h>
#include <lib/zx/debuglog.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/resource.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/array.h>
#include <fbl/string_buffer.h>
#include <fbl/vector.h>
#include "src/devices/lib/log/log.h"
#define CHILD_JOB_RIGHTS (ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS)
enum class FdioAction {
AddNsEntry = 1,
CloneDir = 2,
};
// clang-format off
const static struct {
const char* mount;
const char* name;
uint32_t flags;
FdioAction action;
} FSTAB[] = {
{ "/bin", "bin", FS_BIN, FdioAction::CloneDir },
{ "/blob", "blob", FS_BLOB, FdioAction::CloneDir },
{ "/boot", "boot", FS_BOOT, FdioAction::CloneDir },
{ "/data", "data", FS_DATA, FdioAction::CloneDir },
{ "/dev", "dev", FS_DEV, FdioAction::AddNsEntry },
// TODO(dgonyeo): add this path back in once the appmgr fragment doesn't
// exist on bringup builds
//{ "/hub", "hub", FS_HUB, FdioAction::CloneDir },
{ "/install", "install", FS_INSTALL, FdioAction::CloneDir },
{ "/durable", "durable", FS_DURABLE, FdioAction::CloneDir },
{ "/factory", "factory", FS_FACTORY, FdioAction::CloneDir },
{ "/pkgfs", "pkgfs", FS_PKGFS, FdioAction::CloneDir },
{ "/svc", "svc", FS_SVC, FdioAction::AddNsEntry },
{ "/system", "system", FS_SYSTEM, FdioAction::CloneDir },
{ "/tmp", "tmp", FS_TMP, FdioAction::CloneDir },
{ "/volume", "volume", FS_VOLUME, FdioAction::CloneDir },
};
// clang-format on
//
FsProvider::~FsProvider() {}
DevmgrLauncher::DevmgrLauncher(FsProvider* fs_provider) : fs_provider_(fs_provider) {}
zx_status_t DevmgrLauncher::LaunchWithLoader(const zx::job& job, const char* name,
zx::vmo executable, zx::channel loader,
const char* const* argv, const char** initial_envp,
int stdiofd, const zx::resource& root_resource,
const zx_handle_t* handles, const uint32_t* types,
size_t hcount, zx::process* out_proc, uint32_t flags) {
zx::job job_copy;
zx_status_t status = job.duplicate(CHILD_JOB_RIGHTS, &job_copy);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to launch %s (%s), cannot duplicate job: %s", argv[0], name,
zx_status_get_string(status));
return status;
}
zx::debuglog debuglog;
if (stdiofd < 0) {
if ((status = zx::debuglog::create(root_resource, 0, &debuglog) != ZX_OK)) {
return status;
}
}
uint32_t spawn_flags = FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_UTC_CLOCK;
// Set up the environ for the new process
fbl::Vector<const char*> env;
if (getenv(LDSO_TRACE_CMDLINE)) {
env.push_back(LDSO_TRACE_ENV);
}
env.push_back(ZX_SHELL_ENV_PATH);
while (initial_envp && initial_envp[0]) {
env.push_back(*initial_envp++);
}
env.push_back(nullptr);
fbl::Vector<fdio_spawn_action_t> actions;
actions.reserve(3 + std::size(FSTAB) + hcount);
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_SET_NAME,
.name = {.data = name},
});
if (loader.is_valid()) {
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h = {.id = PA_HND(PA_LDSVC_LOADER, 0), .handle = loader.release()},
});
} else {
spawn_flags |= FDIO_SPAWN_DEFAULT_LDSVC;
}
// create namespace based on FS_* flags
for (unsigned n = 0; n < std::size(FSTAB); n++) {
if (!(FSTAB[n].flags & flags)) {
continue;
}
switch (FSTAB[n].action) {
case FdioAction::AddNsEntry: {
zx_handle_t h;
if ((h = fs_provider_->CloneFs(FSTAB[n].name).release()) != ZX_HANDLE_INVALID) {
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_ADD_NS_ENTRY,
.ns = {.prefix = FSTAB[n].mount, .handle = h},
});
}
} break;
case FdioAction::CloneDir:
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_CLONE_DIR,
.dir = {.prefix = FSTAB[n].mount},
});
break;
default:
__UNREACHABLE;
}
}
auto action_uses_fd0 = [](const fdio_spawn_action_t& action) {
switch (action.action) {
case FDIO_SPAWN_ACTION_ADD_HANDLE:
if (PA_HND_TYPE(action.h.id) == PA_FD) {
int fd = PA_HND_ARG(action.h.id);
return (fd & ~FDIO_FLAG_USE_FOR_STDIO) == 0;
}
break;
case FDIO_SPAWN_ACTION_CLONE_FD:
case FDIO_SPAWN_ACTION_TRANSFER_FD: {
int fd = action.fd.target_fd;
return (fd & ~FDIO_FLAG_USE_FOR_STDIO) == 0;
}
}
return false;
};
if (std::none_of(actions.begin(), actions.end(), action_uses_fd0)) {
if (debuglog.is_valid()) {
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h = {.id = PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO | 0), .handle = debuglog.release()},
});
} else {
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_TRANSFER_FD,
.fd = {.local_fd = stdiofd, .target_fd = FDIO_FLAG_USE_FOR_STDIO | 0},
});
}
}
for (size_t i = 0; i < hcount; ++i) {
actions.push_back((fdio_spawn_action_t){
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h = {.id = types[i], .handle = handles[i]},
});
}
zx::process proc;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
if (executable.is_valid()) {
status = fdio_spawn_vmo(job_copy.get(), spawn_flags, executable.release(), argv, env.data(),
actions.size(), actions.data(), proc.reset_and_get_address(), err_msg);
} else {
status = fdio_spawn_etc(job_copy.get(), spawn_flags, argv[0], argv, env.data(), actions.size(),
actions.data(), proc.reset_and_get_address(), err_msg);
}
if (status != ZX_OK) {
LOGF(ERROR, "Failed to launch %s (%s): %s", argv[0], name, err_msg);
return status;
}
LOGF(INFO, "Launching %s (%s)", argv[0], name);
if (out_proc != nullptr) {
*out_proc = std::move(proc);
}
return ZX_OK;
}
zx_status_t DevmgrLauncher::Launch(const zx::job& job, const char* name, const char* const* argv,
const char** initial_envp, int stdiofd,
const zx::resource& root_resource, const zx_handle_t* handles,
const uint32_t* types, size_t hcount, zx::process* out_proc,
uint32_t flags) {
return LaunchWithLoader(job, name, zx::vmo(), zx::channel(), argv, initial_envp, stdiofd,
root_resource, handles, types, hcount, out_proc, flags);
}