blob: f89984a6ee7477bff80c25321d43ebf2c68bd1d2 [file] [log] [blame]
// Copyright 2017 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 <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <bootdata/decompress.h>
#include <fbl/unique_fd.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/watcher.h>
#include <lib/fit/defer.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <loader-service/loader-service.h>
#include <ramdevice-client/ramdisk.h>
#include <zircon/device/vfs.h>
#include <zircon/dlfcn.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include "../shared/env.h"
#include "block-watcher.h"
#include "fs-manager.h"
namespace devmgr {
namespace {
zx_status_t MiscDeviceAdded(int dirfd, int event, const char* fn, void* cookie) {
if (event != WATCH_EVENT_ADD_FILE || strcmp(fn, "ramctl") != 0) {
return ZX_OK;
}
zx::vmo ramdisk_vmo = std::move(*static_cast<zx::vmo*>(cookie));
size_t size;
zx_status_t status = ramdisk_vmo.get_size(&size);
if (status != ZX_OK) {
return ZX_ERR_STOP;
}
const char* errmsg;
zx::vmo vmo;
status = decompress_bootdata(zx_vmar_root_self(), ramdisk_vmo.get(), 0, size,
vmo.reset_and_get_address(), &errmsg);
if (status != ZX_OK) {
printf("fshost: failed to decompress ramdisk: %s\n", errmsg);
return ZX_ERR_STOP;
}
ramdisk_client* client;
status = ramdisk_create_from_vmo(vmo.release(), &client);
if (status != ZX_OK) {
printf("fshost: failed to create ramdisk from BOOTDATA_RAMDISK\n");
} else {
printf("fshost: BOOTDATA_RAMDISK attached\n");
}
return ZX_ERR_STOP;
}
int RamctlWatcher(void* arg) {
fbl::unique_fd dirfd(open("/dev/misc", O_DIRECTORY | O_RDONLY));
if (!dirfd) {
printf("fshost: failed to open /dev/misc: %s\n", strerror(errno));
return -1;
}
fdio_watch_directory(dirfd.get(), &MiscDeviceAdded, ZX_TIME_INFINITE, arg);
return 0;
}
// Setup the loader service to be used by all processes spawned by devmgr.
void setup_loader_service(zx::channel devmgr_loader) {
loader_service_t* svc;
zx_status_t status = loader_service_create_fs(nullptr, &svc);
;
if (status != ZX_OK) {
fprintf(stderr, "fshost: failed to create loader service %d\n", status);
}
auto defer = fit::defer([svc] { loader_service_release(svc); });
status = loader_service_attach(svc, devmgr_loader.release());
if (status != ZX_OK) {
fprintf(stderr, "fshost: failed to attach to loader service: %d\n", status);
return;
}
zx_handle_t fshost_loader;
status = loader_service_connect(svc, &fshost_loader);
if (status != ZX_OK) {
fprintf(stderr, "fshost: failed to connect to loader service: %d\n", status);
return;
}
zx_handle_close(dl_set_loader_service(fshost_loader));
}
// Initialize the fshost namespace.
//
// |fs_root_client| is mapped to "/fs", and represents the filesystem of devmgr.
zx_status_t BindNamespace(zx::channel fs_root_client) {
fdio_ns_t* ns;
zx_status_t status;
if ((status = fdio_ns_get_installed(&ns)) != ZX_OK) {
printf("fshost: cannot get namespace: %d\n", status);
return status;
}
// Bind "/fs".
if ((status = fdio_ns_bind(ns, "/fs", fs_root_client.release())) != ZX_OK) {
printf("fshost: cannot bind /fs to namespace: %d\n", status);
return status;
}
// Bind "/system".
{
zx::channel client, server;
if ((status = zx::channel::create(0, &client, &server)) != ZX_OK) {
return status;
}
if ((status = fdio_open("/fs/system", ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_ADMIN,
server.release())) != ZX_OK) {
printf("fshost: cannot open connection to /system: %d\n", status);
return status;
}
if ((status = fdio_ns_bind(ns, "/system", client.release())) != ZX_OK) {
printf("fshost: cannot bind /system to namespace: %d\n", status);
return status;
}
}
return ZX_OK;
}
} // namespace
} // namespace devmgr
int main(int argc, char** argv) {
bool netboot = false;
bool disable_block_watcher = false;
enum {
kNetboot,
kDisableBlockWatcher,
};
option options[] = {
{"netboot", no_argument, nullptr, kNetboot},
{"disable-block-watcher", no_argument, nullptr, kDisableBlockWatcher},
};
int opt;
while ((opt = getopt_long(argc, argv, "", options, nullptr)) != -1) {
switch (opt) {
case kNetboot:
netboot = true;
break;
case kDisableBlockWatcher:
disable_block_watcher = true;
break;
}
}
zx::channel fs_root_server(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
zx::channel devmgr_loader(zx_take_startup_handle(PA_HND(PA_USER0, 2)));
zx::channel fshost_export_server(zx_take_startup_handle(PA_HND(PA_USER0, 3)));
zx::event fshost_event(zx_take_startup_handle(PA_HND(PA_USER1, 0)));
// First, initialize the local filesystem in isolation.
fbl::unique_ptr<devmgr::FsManager> fs_manager;
zx_status_t status = devmgr::FsManager::Create(std::move(fshost_event), &fs_manager);
if (status != ZX_OK) {
printf("fshost: Cannot create FsManager\n");
return status;
}
// First, begin serving the "fs_root" on behalf of devmgr.
status = fs_manager->ServeRoot(std::move(fs_root_server));
if (status != ZX_OK) {
printf("fshost: Cannot serve devmgr's root filesystem\n");
return status;
}
status = fs_manager->ServeFshostRoot(std::move(fshost_export_server));
if (status != ZX_OK) {
printf("fshost: Cannot serve export directory\n");
return status;
}
// Now that we are serving the fs_root, acquire a new connection
// to place in our own namespace.
zx::channel fs_root_client;
status = zx::channel::create(0, &fs_root_client, &fs_root_server);
if (status != ZX_OK) {
return ZX_OK;
}
status = fs_manager->ServeRoot(std::move(fs_root_server));
if (status != ZX_OK) {
printf("fshost: Cannot serve devmgr's root filesystem\n");
return status;
}
// Initialize namespace, and begin monitoring the |fshost_event| for a termination event.
status = devmgr::BindNamespace(std::move(fs_root_client));
if (status != ZX_OK) {
printf("fshost: cannot bind namespace\n");
return status;
}
fs_manager->WatchExit();
// Setup the devmgr loader service.
devmgr::setup_loader_service(std::move(devmgr_loader));
// If there is a ramdisk, setup the ramctl filesystems.
zx::vmo ramdisk_vmo(zx_take_startup_handle(PA_HND(PA_VMO_BOOTDATA, 0)));
if (ramdisk_vmo.is_valid()) {
thrd_t t;
int err = thrd_create_with_name(&t, &devmgr::RamctlWatcher, &ramdisk_vmo, "ramctl-filesystems");
if (err != thrd_success) {
printf("fshost: failed to start ramctl-filesystems: %d\n", err);
}
thrd_detach(t);
}
if (!disable_block_watcher) {
bool check_filesystems = devmgr::getenv_bool("zircon.system.filesystem-check", false);
BlockDeviceWatcher(std::move(fs_manager), netboot, check_filesystems);
} else {
// Keep the process alive so that the loader service continues to be supplied
// to the devmgr. Otherwise the devmgr will segfault.
zx::nanosleep(zx::time::infinite());
}
printf("fshost: terminating (block device filesystems finished?)\n");
return 0;
}