blob: 66cf73feb5543064f823f8e3847b5bb145aa7852 [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 <ctype.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include <unistd.h>
#include <utility>
#include <fbl/unique_fd.h>
#include <fbl/vector.h>
#include <launchpad/launchpad.h>
#include <loader-service/loader-service.h>
#include <zircon/boot/bootdata.h>
#include <zircon/device/vfs.h>
#include <zircon/dlfcn.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/log.h>
#include <zircon/syscalls/object.h>
#include <zircon/syscalls/policy.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/devmgr-launcher/processargs.h>
#include <lib/fdio/io.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/util.h>
#include <lib/fdio/watcher.h>
#include <lib/zx/debuglog.h>
#include <lib/zx/event.h>
#include <lib/zx/port.h>
#include <lib/zx/resource.h>
#include <lib/zx/time.h>
#include <lib/zx/vmo.h>
#include "../shared/env.h"
#include "../shared/fdio.h"
#include "../shared/log.h"
#include "coordinator.h"
#include "devmgr.h"
#include "devhost-loader-service.h"
namespace {
struct {
// The handle used to transmit messages to appmgr.
zx::channel appmgr_client;
// The handle used by appmgr to serve incoming requests.
// If appmgr cannot be launched within a timeout, this handle is closed.
zx::channel appmgr_server;
zx::resource root_resource;
zx::unowned_job root_job;
zx::job svc_job;
zx::job fuchsia_job;
zx::channel svchost_outgoing;
zx::channel fs_root;
} g_handles;
// Wait for the requested file. Its parent directory must exist.
zx_status_t wait_for_file(const char* path, zx::time deadline) {
char path_copy[PATH_MAX];
if (strlen(path) >= PATH_MAX) {
return ZX_ERR_INVALID_ARGS;
}
strcpy(path_copy, path);
char* last_slash = strrchr(path_copy, '/');
// Waiting on the root of the fs or paths with no slashes is not supported by this function
if (last_slash == path_copy || last_slash == nullptr) {
return ZX_ERR_NOT_SUPPORTED;
}
last_slash[0] = 0;
char* dirname = path_copy;
char* basename = last_slash + 1;
auto watch_func = [](int dirfd, int event, const char* fn, void* cookie) -> zx_status_t {
auto basename = static_cast<const char*>(cookie);
if (event != WATCH_EVENT_ADD_FILE) {
return ZX_OK;
}
if (!strcmp(fn, basename)) {
return ZX_ERR_STOP;
}
return ZX_OK;
};
fbl::unique_fd dirfd(open(dirname, O_RDONLY));
if (!dirfd.is_valid()) {
return ZX_ERR_INVALID_ARGS;
}
zx_status_t status = fdio_watch_directory(dirfd.get(), watch_func, deadline.get(),
reinterpret_cast<void*>(basename));
if (status == ZX_ERR_STOP) {
return ZX_OK;
}
return status;
}
zx_status_t devmgr_launch_load(void* ctx, launchpad_t* lp, const char* file) {
return launchpad_load_from_file(lp, file);
}
void do_autorun(const char* name, const char* env) {
const char* cmd = getenv(env);
if (cmd != nullptr) {
devmgr::devmgr_launch_cmdline(env, g_handles.svc_job, name, &devmgr_launch_load, nullptr,
cmd, nullptr, nullptr, 0, nullptr, FS_ALL);
}
}
// Get the root resource from the startup handle. Not receiving the startup
// handle is logged, but not fatal. In test environments, it would not be
// present.
zx_status_t fetch_root_resource(zx::resource* root_resource) {
// Read the root resource out of its channel
zx::channel root_resource_channel(
zx_take_startup_handle(DEVMGR_LAUNCHER_ROOT_RESOURCE_CHANNEL_HND));
if (!root_resource_channel) {
fprintf(stderr, "devmgr: did not receive root resource channel, assuming test "
"environment and continuing\n");
return ZX_OK;
}
uint32_t actual_handles = 0;
zx_status_t status = root_resource_channel.read(0, nullptr, 0, nullptr,
root_resource->reset_and_get_address(), 1,
&actual_handles);
if (status != ZX_OK) {
return status;
}
return actual_handles == 1 ? ZX_OK : ZX_ERR_UNAVAILABLE;
}
int fuchsia_starter(void* arg) {
auto coordinator = static_cast<devmgr::Coordinator*>(arg);
bool appmgr_started = false;
bool autorun_started = false;
bool drivers_loaded = false;
size_t appmgr_timeout = 20;
zx::time deadline = zx::deadline_after(zx::sec(appmgr_timeout));
do {
zx_status_t status = coordinator->fshost_event().wait_one(FSHOST_SIGNAL_READY, deadline,
nullptr);
if (status == ZX_ERR_TIMED_OUT) {
if (g_handles.appmgr_server.is_valid()) {
if (coordinator->require_system()) {
fprintf(stderr, "devmgr: appmgr not launched in %zus, closing appmgr handle\n",
appmgr_timeout);
}
g_handles.appmgr_server.reset();
}
deadline = zx::time::infinite();
continue;
}
if (status != ZX_OK) {
fprintf(stderr, "devmgr: error waiting on fuchsia start event: %d\n", status);
break;
}
status = coordinator->fshost_event().signal(FSHOST_SIGNAL_READY, 0);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: error signaling fshost: %d\n", status);
}
if (!drivers_loaded) {
// we're starting the appmgr because /system is present
// so we also signal the device coordinator that those
// drivers are now loadable
coordinator->set_system_available(true);
coordinator->ScanSystemDrivers();
drivers_loaded = true;
}
const char* argv_appmgr[] = {"/system/bin/appmgr"};
struct stat s;
if (!appmgr_started && stat(argv_appmgr[0], &s) == 0) {
unsigned int appmgr_hnd_count = 0;
zx_handle_t appmgr_hnds[2] = {};
uint32_t appmgr_ids[2] = {};
if (g_handles.appmgr_server.is_valid()) {
assert(appmgr_hnd_count < fbl::count_of(appmgr_hnds));
appmgr_hnds[appmgr_hnd_count] = g_handles.appmgr_server.release();
appmgr_ids[appmgr_hnd_count] = PA_DIRECTORY_REQUEST;
appmgr_hnd_count++;
}
devmgr::devmgr_launch(g_handles.fuchsia_job, "appmgr", &devmgr_launch_load, nullptr,
fbl::count_of(argv_appmgr), argv_appmgr, nullptr, -1, appmgr_hnds,
appmgr_ids, appmgr_hnd_count, nullptr, FS_FOR_APPMGR);
appmgr_started = true;
}
if (!autorun_started) {
do_autorun("autorun:system", "zircon.autorun.system");
autorun_started = true;
}
} while (!appmgr_started);
return 0;
}
int console_starter(void* arg) {
// if no kernel shell on serial uart, start a sh there
printf("devmgr: shell startup\n");
// If we got a TERM environment variable (aka a TERM=... argument on
// the kernel command line), pass this down; otherwise pass TERM=uart.
const char* term = getenv("TERM");
if (term == nullptr) {
term = "TERM=uart";
} else {
term -= sizeof("TERM=") - 1;
}
const char* device = getenv("console.path");
if (!device) {
device = "/dev/misc/console";
}
const char* envp[] = {
term,
nullptr,
};
zx_status_t status = wait_for_file(device, zx::time::infinite());
if (status != ZX_OK) {
printf("devmgr: failed to wait for console '%s'\n", device);
return 1;
}
fbl::unique_fd fd(open(device, O_RDWR));
if (!fd.is_valid()) {
printf("devmgr: failed to open console '%s'\n", device);
return 1;
}
const char* argv_sh[] = {"/boot/bin/sh"};
devmgr::devmgr_launch(g_handles.svc_job, "sh:console", &devmgr_launch_load, nullptr,
fbl::count_of(argv_sh), argv_sh, envp, fd.release(), nullptr, nullptr, 0,
nullptr, FS_ALL);
return 0;
}
int pwrbtn_monitor_starter(void* arg) {
const char* name = "pwrbtn-monitor";
const char* argv[] = {"/boot/bin/pwrbtn-monitor"};
int argc = 1;
zx::job job_copy;
zx_status_t status =
g_handles.svc_job.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_WRITE, &job_copy);
if (status != ZX_OK) {
printf("svc_job.duplicate failed %s\n", zx_status_get_string(status));
return 1;
}
launchpad_t* lp;
launchpad_create(job_copy.get(), name, &lp);
status = launchpad_load_from_file(lp, argv[0]);
if (status != ZX_OK) {
launchpad_abort(lp, status, "cannot load file");
}
launchpad_set_args(lp, argc, argv);
// create a namespace containing /dev/class/input and /dev/misc
const char* nametable[2] = {};
uint32_t count = 0;
zx::channel fs_handle = devmgr::fs_clone("dev/class/input");
if (fs_handle.is_valid()) {
nametable[count] = "/input";
launchpad_add_handle(lp, fs_handle.release(), PA_HND(PA_NS_DIR, count++));
} else {
launchpad_abort(lp, ZX_ERR_BAD_STATE, "devmgr: failed to clone /dev/class/input");
}
// Ideally we'd only expose /dev/misc/dmctl, but we do not support exposing
// single files
fs_handle = devmgr::fs_clone("dev/misc");
if (fs_handle.is_valid()) {
nametable[count] = "/misc";
launchpad_add_handle(lp, fs_handle.release(), PA_HND(PA_NS_DIR, count++));
} else {
launchpad_abort(lp, ZX_ERR_BAD_STATE, "devmgr: failed to clone /dev/misc");
}
launchpad_set_nametable(lp, count, nametable);
zx::debuglog debuglog;
if ((status = zx::debuglog::create(zx::resource(), 0, &debuglog) < 0)) {
launchpad_abort(lp, status, "devmgr: cannot create debuglog handle");
} else {
launchpad_add_handle(lp, debuglog.release(), PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO | 0));
}
const char* errmsg;
if ((status = launchpad_go(lp, nullptr, &errmsg)) < 0) {
printf("devmgr: launchpad %s (%s) failed: %s: %d\n",
argv[0], name, errmsg, status);
} else {
printf("devmgr: launch %s (%s) OK\n", argv[0], name);
}
return 0;
}
void start_console_shell() {
// start a shell on the kernel console if it isn't already running a shell
if (!devmgr::getenv_bool("kernel.shell", false)) {
thrd_t t;
if ((thrd_create_with_name(&t, console_starter, nullptr, "console-starter")) == thrd_success) {
thrd_detach(t);
}
}
}
zx_status_t fuchsia_create_job() {
zx_status_t status = zx::job::create(*g_handles.root_job, 0u, &g_handles.fuchsia_job);
if (status != ZX_OK) {
printf("devmgr: unable to create fuchsia job: %d (%s)\n", status,
zx_status_get_string(status));
return status;
}
g_handles.fuchsia_job.set_property(ZX_PROP_NAME, "fuchsia", 7);
const zx_policy_basic_t fuchsia_job_policy[] = {
{.condition = ZX_POL_NEW_PROCESS, .policy = ZX_POL_ACTION_DENY}};
status = g_handles.fuchsia_job.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC,
fuchsia_job_policy,
fbl::count_of(fuchsia_job_policy));
if (status != ZX_OK) {
printf("devmgr: unable to set policy fuchsia job: %d (%s)\n", status,
zx_status_get_string(status));
return status;
}
return ZX_OK;
}
zx_status_t svchost_start(bool require_system) {
printf("devmgr: svc init\n");
zx::channel dir_request;
zx::debuglog logger;
zx::channel appmgr_svc_req;
zx::channel appmgr_svc;
zx_status_t status = zx::channel::create(0, &dir_request, &g_handles.svchost_outgoing);
if (status != ZX_OK) {
return status;
}
status = zx::debuglog::create(zx::resource(), 0, &logger);
if (status != ZX_OK) {
return status;
}
status = zx::channel::create(0, &appmgr_svc_req, &appmgr_svc);
if (status != ZX_OK) {
return status;
}
status = fdio_service_connect_at(g_handles.appmgr_client.get(), "svc", appmgr_svc_req.release());
if (status != ZX_OK) {
return status;
}
const char* name = "svchost";
const char* argv[2] = {
"/boot/bin/svchost",
require_system ? "--require-system" : nullptr,
};
int argc = require_system ? 2 : 1;
zx::job svc_job_copy;
status = g_handles.svc_job.duplicate(
ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS, &svc_job_copy);
if (status != ZX_OK) {
return status;
}
zx::job root_job_copy;
status = g_handles.root_job->duplicate(ZX_RIGHTS_BASIC | ZX_RIGHTS_IO | ZX_RIGHTS_PROPERTY |
ZX_RIGHT_ENUMERATE | ZX_RIGHT_MANAGE_PROCESS,
&root_job_copy);
if (status != ZX_OK) {
return status;
}
launchpad_t* lp = nullptr;
launchpad_create(svc_job_copy.get(), name, &lp);
launchpad_load_from_file(lp, argv[0]);
launchpad_set_args(lp, argc, argv);
launchpad_add_handle(lp, dir_request.release(), PA_DIRECTORY_REQUEST);
launchpad_add_handle(lp, logger.release(), PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO));
// Remove once svchost hosts the tracelink service itself.
launchpad_add_handle(lp, appmgr_svc.release(), PA_HND(PA_USER0, 0));
// Give svchost a restricted root job handle. svchost is already a privileged system service
// as it controls system-wide process launching. With the root job it can consolidate a few
// services such as crashsvc and the profile service.
launchpad_add_handle(lp, root_job_copy.release(), PA_HND(PA_USER0, 1));
// Give svchost access to /dev/class/sysmem, to enable svchost to forward sysmem service
// requests to the sysmem driver. Create a namespace containing /dev/class/sysmem.
const char* nametable[1] = {};
uint32_t count = 0;
zx::channel fs_handle = devmgr::fs_clone("dev/class/sysmem");
if (fs_handle.is_valid()) {
nametable[count] = "/sysmem";
launchpad_add_handle(lp, fs_handle.release(), PA_HND(PA_NS_DIR, count++));
} else {
launchpad_abort(lp, ZX_ERR_BAD_STATE, "devmgr: failed to clone /dev/class/sysmem");
// The launchpad_go() call below will fail, but will still free lp.
}
launchpad_set_nametable(lp, count, nametable);
const char* errmsg = nullptr;
if ((status = launchpad_go(lp, nullptr, &errmsg)) < 0) {
printf("devmgr: launchpad %s (%s) failed: %s: %d\n",
argv[0], name, errmsg, status);
} else {
printf("devmgr: launch %s (%s) OK\n", argv[0], name);
}
return ZX_OK;
}
void fshost_start(devmgr::Coordinator* coordinator) {
// assemble handles to pass down to fshost
zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
uint32_t types[fbl::count_of(handles)];
size_t n = 0;
zx_handle_t ldsvc;
// pass / and /svc handles to fsboot
if (zx_channel_create(0, g_handles.fs_root.reset_and_get_address(), &handles[0]) == ZX_OK) {
types[n++] = PA_HND(PA_USER0, 0);
}
if ((handles[n] = devmgr::fs_clone("svc").release()) != ZX_HANDLE_INVALID) {
types[n++] = PA_HND(PA_USER0, 2);
}
if (zx_channel_create(0, &ldsvc, &handles[n]) == ZX_OK) {
types[n++] = PA_HND(PA_USER0, 3);
} else {
ldsvc = ZX_HANDLE_INVALID;
}
// pass fuchsia start event to fshost
zx::event fshost_event_duplicate;
if (coordinator->fshost_event().duplicate(ZX_RIGHT_SAME_RIGHTS, &fshost_event_duplicate) ==
ZX_OK) {
handles[n] = fshost_event_duplicate.release();
types[n++] = PA_HND(PA_USER1, 0);
}
// pass bootdata VMOs to fshost
for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
uint32_t type = PA_HND(PA_VMO_BOOTDATA, m);
if ((handles[n] = zx_take_startup_handle(type)) == ZX_HANDLE_INVALID) {
break;
}
zx_status_t status = coordinator->SetBootdata(zx::unowned_vmo(handles[n]));
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to set bootdata: %d\n", status);
break;
}
types[n++] = type;
}
// pass VDSO VMOS to fshost
for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
uint32_t type = PA_HND(PA_VMO_VDSO, m);
if (m == 0) {
// By this point, launchpad has already moved PA_HND(PA_VMO_VDSO, 0) into a static.
handles[n] = ZX_HANDLE_INVALID;
launchpad_get_vdso_vmo(&handles[n]);
} else {
handles[n] = zx_take_startup_handle(type);
}
if (handles[n] != ZX_HANDLE_INVALID) {
types[n++] = type;
} else {
break;
}
}
// pass KERNEL FILE VMOS to fsboot
for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
uint32_t type = PA_HND(PA_VMO_KERNEL_FILE, m);
if ((handles[n] = zx_take_startup_handle(type)) != ZX_HANDLE_INVALID) {
types[n++] = type;
} else {
break;
}
}
const char* argv[] = {"/boot/bin/fshost", "--netboot"};
int argc = (devmgr::getenv_bool("netsvc.netboot", false) ||
devmgr::getenv_bool("zircon.system.disable-automount", false))
? 2
: 1;
// Pass zircon.system.* options to the fshost as environment variables
const char* envp[16];
unsigned envc = 0;
char** e = environ;
while (*e && (envc < fbl::count_of(envp))) {
if (!strncmp(*e, "zircon.system", strlen("zircon.system"))) {
envp[envc++] = *e;
}
e++;
}
envp[envc] = nullptr;
devmgr::devmgr_launch(g_handles.svc_job, "fshost", &devmgr_launch_load, nullptr, argc, argv,
envp, -1, handles, types, n, nullptr, FS_BOOT | FS_DEV);
// switch to system loader service provided by fshost
zx_handle_close(dl_set_loader_service(ldsvc));
}
zx::channel bootfs_root_clone() {
zx::channel boot, boot_remote;
zx_status_t status = zx::channel::create(0, &boot, &boot_remote);
if (status != ZX_OK) {
return zx::channel();
}
fdio_ns_t* ns;
status = fdio_ns_get_installed(&ns);
ZX_ASSERT(status == ZX_OK);
status = fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, boot_remote.release());
if (status != ZX_OK) {
return zx::channel();
}
return boot;
}
void devmgr_vfs_init(devmgr::Coordinator* coordinator) {
printf("devmgr: vfs init\n");
fdio_ns_t* ns;
zx_status_t r;
r = fdio_ns_get_installed(&ns);
ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot get namespace: %s\n", zx_status_get_string(r));
r = fdio_ns_bind(ns, "/dev", devmgr::fs_clone("dev").release());
ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot bind /dev to namespace: %s\n",
zx_status_get_string(r));
// Start fshost before binding /system, since it publishes it.
fshost_start(coordinator);
if ((r = fdio_ns_bind(ns, "/system", devmgr::fs_clone("system").release())) != ZX_OK) {
printf("devmgr: cannot bind /system to namespace: %d\n", r);
}
}
int service_starter(void* arg) {
// Features like Intel Processor Trace need a dump of ld.so activity.
// The output has a specific format, and will eventually be recorded
// via a specific mechanism (magenta tracing support), so we use a specific
// env var (and don't, for example, piggyback on LD_DEBUG).
// We enable this pretty early so that we get a trace of as many processes
// as possible.
if (getenv(LDSO_TRACE_CMDLINE)) {
// This takes care of places that clone our environment.
putenv(strdup(LDSO_TRACE_ENV));
// There is still devmgr_launch() which does not clone our enviroment.
// It has its own check.
}
char vcmd[64];
bool netboot = false;
bool vruncmd = false;
if (!devmgr::getenv_bool("netsvc.disable", false)) {
const char* args[] = {"/boot/bin/netsvc", nullptr, nullptr, nullptr, nullptr, nullptr};
int argc = 1;
if (devmgr::getenv_bool("netsvc.netboot", false)) {
args[argc++] = "--netboot";
netboot = true;
vruncmd = true;
}
if (devmgr::getenv_bool("netsvc.advertise", true)) {
args[argc++] = "--advertise";
}
const char* interface;
if ((interface = getenv("netsvc.interface")) != nullptr) {
args[argc++] = "--interface";
args[argc++] = interface;
}
const char* nodename = getenv("zircon.nodename");
if (nodename) {
args[argc++] = nodename;
}
zx::process proc;
if (devmgr::devmgr_launch(g_handles.svc_job, "netsvc", &devmgr_launch_load, nullptr, argc,
args, nullptr, -1, nullptr, nullptr, 0, &proc, FS_ALL) == ZX_OK) {
if (vruncmd) {
zx_info_handle_basic_t info = {};
proc.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
proc.reset();
snprintf(vcmd, sizeof(vcmd), "dlog -f -t -p %zu", info.koid);
}
} else {
vruncmd = false;
}
__UNUSED auto leaked_handle = proc.release();
}
auto coordinator = static_cast<devmgr::Coordinator*>(arg);
if (!devmgr::getenv_bool("virtcon.disable", false)) {
// pass virtcon.* options along
const char* envp[16];
unsigned envc = 0;
char** e = environ;
while (*e && (envc < fbl::count_of(envp))) {
if (!strncmp(*e, "virtcon.", 8)) {
envp[envc++] = *e;
}
e++;
}
envp[envc] = nullptr;
const char* num_shells = coordinator->require_system() && !netboot ? "0" : "3";
size_t handle_count = 0;
zx_handle_t handles[2];
uint32_t types[2];
zx::channel virtcon_client, virtcon_server;
zx_status_t status = zx::channel::create(0, &virtcon_client, &virtcon_server);
if (status == ZX_OK) {
coordinator->set_virtcon_channel(std::move(virtcon_client));
handles[handle_count] = virtcon_server.release();
types[handle_count] = PA_HND(PA_USER0, 0);
++handle_count;
}
zx::debuglog debuglog;
status = zx::debuglog::create(coordinator->root_resource(), ZX_LOG_FLAG_READABLE,
&debuglog);
if (status == ZX_OK) {
handles[handle_count] = debuglog.release();
types[handle_count] = PA_HND(PA_USER0, 1);
++handle_count;
}
const char* args[] = {"/boot/bin/virtual-console", "--shells", num_shells, "--run", vcmd};
devmgr::devmgr_launch(g_handles.svc_job, "virtual-console", &devmgr_launch_load, nullptr,
vruncmd ? 5 : 3, args, envp, -1, handles, types, handle_count,
nullptr, FS_ALL);
}
const char* epoch = getenv("devmgr.epoch");
if (epoch) {
zx_time_t offset = ZX_SEC(atoi(epoch));
zx_clock_adjust(coordinator->root_resource().get(), ZX_CLOCK_UTC, offset);
}
do_autorun("autorun:boot", "zircon.autorun.boot");
thrd_t t;
if ((thrd_create_with_name(&t, fuchsia_starter, coordinator, "fuchsia-starter")) ==
thrd_success) {
thrd_detach(t);
}
return 0;
}
void ParseArgs(int argc, char** argv, devmgr::DevmgrArgs* out) {
enum {
kDriverSearchPath,
kLoadDriver,
kSysDeviceDriver,
};
option options[] = {
{"driver-search-path", required_argument, nullptr, kDriverSearchPath},
{"load-driver", required_argument, nullptr, kLoadDriver},
{"sys-device-driver", required_argument, nullptr, kSysDeviceDriver},
};
auto print_usage_and_exit = [options]() {
printf("devmgr: supported arguments:\n");
for (const auto& option : options) {
printf(" --%s\n", option.name);
}
exit(1);
};
auto check_not_duplicated = [print_usage_and_exit](const char* arg) {
if (arg != nullptr) {
printf("devmgr: duplicated argument\n");
print_usage_and_exit();
}
};
// Reset the args state
*out = devmgr::DevmgrArgs();
int opt;
while ((opt = getopt_long(argc, argv, "", options, nullptr)) != -1) {
switch (opt) {
case kDriverSearchPath:
out->driver_search_paths.push_back(optarg);
break;
case kLoadDriver:
out->load_drivers.push_back(optarg);
break;
case kSysDeviceDriver:
check_not_duplicated(out->sys_device_driver);
out->sys_device_driver = optarg;
break;
default:
print_usage_and_exit();
}
}
}
zx_status_t CreateDevhostJob(const zx::job& root_job, zx::job* devhost_job_out) {
printf("devmgr: coordinator_init()\n");
zx::job devhost_job;
zx_status_t status = zx::job::create(root_job, 0u, &devhost_job);
if (status != ZX_OK) {
log(ERROR, "devcoord: unable to create devhost job\n");
return status;
}
static const zx_policy_basic_t policy[] = {
{ ZX_POL_BAD_HANDLE, ZX_POL_ACTION_EXCEPTION },
};
status = devhost_job.set_policy(ZX_JOB_POL_RELATIVE,
ZX_JOB_POL_BASIC, &policy, fbl::count_of(policy));
if (status != ZX_OK) {
log(ERROR, "devcoord: zx_job_set_policy() failed\n");
return status;
}
status = devhost_job.set_property(ZX_PROP_NAME, "zircon-drivers", 15);
if (status != ZX_OK) {
log(ERROR, "devcoord: zx_job_set_property() failed\n");
return status;
}
*devhost_job_out = std::move(devhost_job);
return ZX_OK;
}
} // namespace
namespace devmgr {
zx::channel fs_clone(const char* path) {
if (!strcmp(path, "dev")) {
return devfs_root_clone();
}
if (!strcmp(path, "boot")) {
return bootfs_root_clone();
}
zx::channel h0, h1;
if (zx::channel::create(0, &h0, &h1) != ZX_OK) {
return zx::channel();
}
zx::unowned_channel fs(g_handles.fs_root);
int flags = FS_DIR_FLAGS;
if (!strcmp(path, "hub")) {
fs = zx::unowned_channel(g_handles.appmgr_client);
} else if (!strcmp(path, "svc")) {
flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE;
fs = zx::unowned_channel(g_handles.svchost_outgoing);
path = "public";
} else if (!strncmp(path, "dev/", 4)) {
fs = devfs_root_borrow();
path += 4;
}
zx_status_t status = fdio_open_at(fs->get(), path, flags, h1.release());
if (status != ZX_OK) {
return zx::channel();
}
return h0;
}
} // namespace devmgr
int main(int argc, char** argv) {
printf("devmgr: main()\n");
for (char** e = environ; *e != nullptr; e++) {
printf("cmdline: %s\n", *e);
}
devmgr::DevmgrArgs args;
ParseArgs(argc, argv, &args);
g_handles.root_job = zx::job::default_job();
g_handles.root_job->set_property(ZX_PROP_NAME, "root", 4);
bool require_system = devmgr::getenv_bool("devmgr.require-system", false);
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
devmgr::CoordinatorConfig config;
config.dispatcher = loop.dispatcher();
config.require_system = require_system;
config.asan_drivers = devmgr::getenv_bool("devmgr.devhost.asan", false);
zx_status_t status = fetch_root_resource(&config.root_resource);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: did not receive root resource: %d\n", status);
return 1;
}
//TODO: limit to enumerate rights
status = g_handles.root_job->duplicate(ZX_RIGHT_SAME_RIGHTS, &config.sysinfo_job);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to duplicate root job for sysinfo: %d\n", status);
}
status = CreateDevhostJob(*g_handles.root_job, &config.devhost_job);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to create devhost job: %d\n", status);
return 1;
}
status = zx::event::create(0, &config.fshost_event);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to create fshost event: %d\n", status);
return 1;
}
devmgr::Coordinator coordinator(std::move(config));
devmgr::devfs_init(&coordinator.root_device(), loop.dispatcher());
// Check if whatever launched devmgr gave a channel to be connected to /dev.
// This is for use in tests to let the test environment see devfs.
zx::channel devfs_client(zx_take_startup_handle(DEVMGR_LAUNCHER_DEVFS_ROOT_HND));
if (devfs_client.is_valid()) {
fdio_service_clone_to(devmgr::devfs_root_borrow()->get(), devfs_client.release());
}
status = zx::job::create(*g_handles.root_job, 0u, &g_handles.svc_job);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to create service job: %d\n", status);
return 1;
}
g_handles.svc_job.set_property(ZX_PROP_NAME, "zircon-services", 16);
status = fuchsia_create_job();
if (status != ZX_OK) {
return 1;
}
zx::channel::create(0, &g_handles.appmgr_client, &g_handles.appmgr_server);
status = svchost_start(require_system);
if (status != ZX_OK) {
fprintf(stderr, "devmgr: failed to start svchost: %d", status);
return 1;
}
devmgr_vfs_init(&coordinator);
// If this is not a full Fuchsia build, do not setup appmgr services, as
// this will delay startup.
if (!require_system) {
devmgr::devmgr_disable_appmgr_services();
}
thrd_t t;
if ((thrd_create_with_name(&t, pwrbtn_monitor_starter, nullptr, "pwrbtn-monitor-starter")) ==
thrd_success) {
thrd_detach(t);
}
start_console_shell();
if ((thrd_create_with_name(&t, service_starter, &coordinator, "service-starter")) ==
thrd_success) {
thrd_detach(t);
}
fbl::unique_ptr<devmgr::DevhostLoaderService> loader_service;
if (devmgr::getenv_bool("devmgr.devhost.strict-linking", false)) {
status = devmgr::DevhostLoaderService::Create(loop.dispatcher(), &loader_service);
if (status != ZX_OK) {
return 1;
}
coordinator.set_loader_service(loader_service.get());
}
coordinator_setup(&coordinator, std::move(args));
status = loop.Run();
fprintf(stderr, "devmgr: coordinator exited unexpectedly: %d\n", status);
return status == ZX_OK ? 0 : 1;
}