blob: 136c5db41a2160cc2bd32c339493fdbe932ceabc [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 <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/vector.h>
namespace devmgr {
#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 component doesn't
// exist on bringup builds
//{ "/hub", "hub", FS_HUB, FdioAction::CloneDir },
{ "/install", "install", FS_INSTALL, 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) {
printf("launch failed %s\n", 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;
// 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 + fbl::count_of(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 < fbl::count_of(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;
}
}
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) {
printf("driver_manager: spawn %s (%s) failed: %s: %d\n", argv[0], name, err_msg, status);
return status;
}
printf("driver_manager: launch %s (%s) OK\n", 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);
}
ArgumentVector ArgumentVector::FromCmdline(const char* cmdline) {
ArgumentVector argv;
const size_t cmdline_len = strlen(cmdline) + 1;
argv.raw_bytes_.reset(new char[cmdline_len]);
memcpy(argv.raw_bytes_.get(), cmdline, cmdline_len);
// Get the full commandline by splitting on '+'.
size_t argc = 0;
char* token;
char* rest = argv.raw_bytes_.get();
while (argc < fbl::count_of(argv.argv_) && (token = strtok_r(rest, "+", &rest))) {
argv.argv_[argc++] = token;
}
argv.argv_[argc] = nullptr;
return argv;
}
void ArgumentVector::Print(const char* prefix) const {
const char* const* argv = argv_;
printf("%s: starting", prefix);
for (const char* arg = *argv; arg != nullptr; ++argv, arg = *argv) {
printf(" '%s'", *argv);
}
printf("...\n");
}
} // namespace devmgr