blob: 7d499b52ab8a547206d9c7d89ff2fddeff46353a [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 "managed_launcher.h"
#include <lib/fdio/io.h>
#include <zircon/status.h>
#include <src/lib/pkg_url/fuchsia_pkg_url.h>
#include "garnet/lib/process/process_builder.h"
#include "managed_environment.h"
#include "src/lib/cmx/cmx.h"
#include "src/lib/files/unique_fd.h"
#include "src/lib/fsl/io/fd.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/strings/concatenate.h"
namespace netemul {
using fuchsia::sys::TerminationReason;
using process::ProcessBuilder;
// Helper function to respond with a failure termination reason
// to component controller requests.
void EmitComponentFailure(fidl::InterfaceRequest<fuchsia::sys::ComponentController> req,
fuchsia::sys::TerminationReason reason) {
// Internal helper class to be able to use fidl::Binding to emit the event:
class ErrorComponentController : public fuchsia::sys::ComponentController {
public:
ErrorComponentController(fidl::InterfaceRequest<fuchsia::sys::ComponentController> req,
fuchsia::sys::TerminationReason reason)
: binding_(this, std::move(req)) {
binding_.events().OnTerminated(-1, reason);
}
void Kill() override { /* Do nothing */
}
void Detach() override { /* Do nothing */
}
private:
fidl::Binding<fuchsia::sys::ComponentController> binding_;
};
ErrorComponentController err(std::move(req), reason);
}
static void CreateFlatNamespace(fuchsia::sys::LaunchInfo* linfo) {
if (!linfo->flat_namespace) {
linfo->flat_namespace = std::make_unique<fuchsia::sys::FlatNamespace>();
}
}
struct LaunchArgs {
fuchsia::sys::LaunchInfo launch_info;
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller;
};
void ManagedLauncher::CreateComponent(
fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
// because fuchsia.sys.loader uses legacy callbacks, we need to
// save the info in a shared ptr for the closure
auto args = std::make_shared<LaunchArgs>();
args->launch_info = std::move(launch_info);
args->controller = std::move(controller);
// load package information
loader_->LoadUrl(args->launch_info.url, [this, args](fuchsia::sys::PackagePtr package) {
CreateComponent(std::move(package), std::move(args->launch_info), std::move(args->controller));
});
}
ManagedLauncher::ManagedLauncher(ManagedEnvironment* environment) : env_(environment) {
env_->environment().ConnectToService(real_launcher_.NewRequest());
env_->environment().ConnectToService(loader_.NewRequest());
env_->environment().ConnectToService(loader_sync_.NewRequest());
}
void ManagedLauncher::Bind(fidl::InterfaceRequest<fuchsia::sys::Launcher> request) {
bindings_.AddBinding(this, std::move(request));
}
void ManagedLauncher::CreateComponent(
fuchsia::sys::PackagePtr package, fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
// Before launching, we'll check the component's sandbox
// so we can inject virtual devices
if (!package) {
FX_LOGS(ERROR) << "Can't load package \"" << launch_info.url << "\"";
EmitComponentFailure(std::move(controller), fuchsia::sys::TerminationReason::PACKAGE_NOT_FOUND);
return;
}
if (!UpdateLaunchInfo(std::move(package), &launch_info)) {
EmitComponentFailure(std::move(controller), fuchsia::sys::TerminationReason::INTERNAL_ERROR);
return;
}
real_launcher_->CreateComponent(std::move(launch_info), std::move(controller));
}
bool ManagedLauncher::MakeServiceLaunchInfo(fuchsia::sys::LaunchInfo* launch_info) {
fuchsia::sys::PackagePtr package;
auto status = loader_sync_->LoadUrl(launch_info->url, &package);
if (status != ZX_OK || !package) {
FX_LOGS(ERROR) << "Failed to load service package contents for " << launch_info->url;
return false;
}
return UpdateLaunchInfo(std::move(package), launch_info);
}
bool ManagedLauncher::UpdateLaunchInfo(fuchsia::sys::PackagePtr package,
fuchsia::sys::LaunchInfo* launch_info) {
if (!package->directory.is_valid()) {
FX_LOGS(ERROR) << "Package directory not provided";
return false;
}
// let's open and parse the cmx
component::FuchsiaPkgUrl fp;
if (!fp.Parse(package->resolved_url)) {
FX_LOGS(ERROR) << "Can't parse package url " << package->resolved_url;
return false;
}
component::CmxMetadata cmx;
fbl::unique_fd fd = fsl::OpenChannelAsFileDescriptor(std::move(package->directory));
json::JSONParser json_parser;
if (!cmx.ParseFromFileAt(fd.get(), fp.resource_path(), &json_parser)) {
FX_LOGS(ERROR) << "cmx file failed to parse: " << json_parser.error_str();
return false;
}
// we have devices in sandbox meta, here
// we just add our own /vdev to the flat namespace
// this could be improved by filtering /vdev to requested classes only
// like appmgr does,
// but seems overkill for testing environments
if (!cmx.sandbox_meta().dev().empty()) {
CreateFlatNamespace(launch_info);
// add all devices to flat namespace:
launch_info->flat_namespace->paths.emplace_back(kVdevRoot);
launch_info->flat_namespace->directories.push_back(env_->OpenVdevDirectory());
}
if (!launch_info->out) {
launch_info->out = env_->loggers().CreateLogger(package->resolved_url, false);
}
if (!launch_info->err) {
launch_info->err = env_->loggers().CreateLogger(package->resolved_url, true);
}
// increment counter
env_->loggers().IncrementCounter();
return true;
}
ManagedLauncher::~ManagedLauncher() = default;
} // namespace netemul