blob: f8988fbbce55db797db46f9d00c81b0b0aa5e6ed [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 <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/virtualization/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <fs/pseudo_dir.h>
#include <fs/remote_dir.h>
#include <fs/synchronous_vfs.h>
#include "src/virtualization/lib/guest_config/guest_config.h"
namespace {
zx_status_t ReadGuestCfg(const fuchsia::io::DirectoryHandle& dir, const std::string& path,
fuchsia::virtualization::GuestConfig* cfg) {
auto open_at = [&dir](const std::string& path, fidl::InterfaceRequest<fuchsia::io::File> file) {
return fdio_open_at(dir.channel().get(), path.data(), fuchsia::io::OPEN_RIGHT_READABLE,
file.TakeChannel().release());
};
fuchsia::io::FileSyncPtr file;
zx_status_t status = open_at(path.data(), file.NewRequest());
if (status != ZX_OK) {
return status;
}
zx_status_t buffer_status;
std::unique_ptr<fuchsia::mem::Buffer> buffer;
status = file->GetBuffer(fuchsia::io::VMO_FLAG_READ, &buffer_status, &buffer);
if (status != ZX_OK) {
return status;
} else if (buffer_status != ZX_OK) {
return buffer_status;
}
std::string str;
str.resize(buffer->size);
status = buffer->vmo.read(str.data(), 0, buffer->size);
if (status != ZX_OK) {
return status;
}
return guest_config::ParseConfig(str, std::move(open_at), cfg);
}
class ServiceProviderImpl : public fuchsia::sys::ServiceProvider,
public fuchsia::virtualization::GuestConfigProvider {
public:
static void CreateAndServe(fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> request,
fuchsia::io::DirectoryHandle pkg_dir,
fuchsia::io::DirectoryHandle svc_dir) {
new ServiceProviderImpl(std::move(request), std::move(pkg_dir), std::move(svc_dir));
}
private:
ServiceProviderImpl(fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> request,
fuchsia::io::DirectoryHandle pkg_dir, fuchsia::io::DirectoryHandle svc_dir)
: service_provider_binding_(this, std::move(request)),
pkg_dir_(std::move(pkg_dir)),
svc_dir_(std::move(svc_dir)) {
// The service provider will self-delete on disconnection.
service_provider_binding_.set_error_handler([this](...) { delete this; });
}
// |fuchsia::sys::ServiceProvider|
void ConnectToService(std::string service_name, zx::channel channel) override {
if (service_name == fuchsia::virtualization::GuestConfigProvider::Name_) {
fidl::InterfaceRequest<fuchsia::virtualization::GuestConfigProvider> request(
std::move(channel));
bindings_.AddBinding(this, std::move(request));
}
}
// |fuchsia::virtualization::GuestConfigProvider|
void Get(GetCallback callback) override {
fuchsia::virtualization::GuestConfig cfg;
// Read configuration provided by the component that launched us.
fuchsia::virtualization::GuestConfigProviderSyncPtr provider;
zx_status_t status = fdio_service_connect_at(
svc_dir_.channel().get(), fuchsia::virtualization::GuestConfigProvider::Name_,
provider.NewRequest().TakeChannel().release());
if (status == ZX_OK) {
// Get returns a status code, but failure is non-fatal, so ignore it.
/* status = */ provider->Get(&cfg);
}
auto block_devices = std::move(*cfg.mutable_block_devices());
// Read configuration from the guest's package.
status = ReadGuestCfg(pkg_dir_, "data/guest.cfg", &cfg);
if (status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to read guest configuration";
}
// Make sure that block devices provided by the configuration in the guest's
// package take precedence, as the order matters.
for (auto& block_device : block_devices) {
cfg.mutable_block_devices()->emplace_back(std::move(block_device));
}
// Merge the command-line additions into the main kernel command-line field.
for (auto& cmdline : *cfg.mutable_cmdline_add()) {
cfg.mutable_cmdline()->append(" " + cmdline);
}
cfg.clear_cmdline_add();
// Set any defaults, before returning the configuration.
guest_config::SetDefaults(&cfg);
callback(std::move(cfg));
}
fidl::Binding<fuchsia::sys::ServiceProvider> service_provider_binding_;
fidl::BindingSet<fuchsia::virtualization::GuestConfigProvider> bindings_;
fuchsia::io::DirectoryHandle pkg_dir_;
fuchsia::io::DirectoryHandle svc_dir_;
};
class RunnerImpl : public fuchsia::sys::Runner {
public:
explicit RunnerImpl(async_dispatcher_t* dispatcher) : vfs_(dispatcher) {
context_->svc()->Connect(launcher_.NewRequest());
context_->outgoing()->AddPublicService(bindings_.GetHandler(this));
}
private:
// |fuchsia::sys::Runner|
void StartComponent(
fuchsia::sys::Package application, fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) override {
fuchsia::sys::LaunchInfo launch_info;
fuchsia::io::DirectoryHandle pkg_dir;
fuchsia::io::DirectoryHandle svc_dir;
// Create a bridge between directory_request that we got and VMM's
// directory_request.
fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_dir;
launch_info.directory_request = outgoing_dir.NewRequest().TakeChannel();
fbl::RefPtr<fs::PseudoDir> dir = fbl::AdoptRef(new fs::PseudoDir());
dir->AddEntry("svc", fbl::AdoptRef(new fs::RemoteDir(outgoing_dir.TakeChannel())));
vfs_.ServeDirectory(std::move(dir), std::move(startup_info.launch_info.directory_request));
// Pass-through some arguments directly to the VMM package.
launch_info.url = "fuchsia-pkg://fuchsia.com/vmm#meta/vmm.cmx";
launch_info.arguments = std::move(startup_info.launch_info.arguments);
launch_info.flat_namespace = fuchsia::sys::FlatNamespace::New();
for (size_t i = 0; i < startup_info.flat_namespace.paths.size(); ++i) {
auto& path = startup_info.flat_namespace.paths[i];
auto& directory = startup_info.flat_namespace.directories[i];
if (path == "/pkg") {
pkg_dir.set_channel(std::move(directory));
} else if (path == "/svc") {
svc_dir.set_channel(std::move(directory));
}
}
// We list the GuestConfigProvider service, so that the VMM can connect back
// to the guest runner in order to get its GuestConfig.
auto service_list = fuchsia::sys::ServiceList::New();
service_list->names.emplace_back(fuchsia::virtualization::GuestConfigProvider::Name_);
ServiceProviderImpl::CreateAndServe(service_list->provider.NewRequest(), std::move(pkg_dir),
std::move(svc_dir));
launch_info.additional_services = std::move(service_list);
launcher_->CreateComponent(std::move(launch_info), std::move(controller));
}
std::unique_ptr<sys::ComponentContext> context_ =
sys::ComponentContext::CreateAndServeOutgoingDirectory();
fuchsia::sys::LauncherPtr launcher_;
fidl::BindingSet<fuchsia::sys::Runner> bindings_;
fs::SynchronousVfs vfs_;
};
} // namespace
int main(int argc, char** argv) {
syslog::SetTags({"guest_runner"});
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
RunnerImpl runner(loop.dispatcher());
return loop.Run();
}