blob: b13f41fadf13bec7472d6227d31afc15a9e7df9c [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/debugdata/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/fidl/cpp/clone.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/fit/function.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/sys/cpp/testing/enclosing_environment.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <zircon/assert.h>
#include <memory>
namespace sys {
namespace testing {
EnvironmentServices::ParentOverrides::ParentOverrides(ParentOverrides&&) =
default;
EnvironmentServices::ParentOverrides::ParentOverrides() = default;
EnvironmentServices::EnvironmentServices(
const fuchsia::sys::EnvironmentPtr& parent_env,
ParentOverrides parent_overrides, async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher) {
zx::channel request;
parent_svc_ = sys::ServiceDirectory::CreateWithRequest(&request);
parent_env->GetDirectory(std::move(request));
if (parent_overrides.loader_service_) {
AddSharedService(std::move(parent_overrides.loader_service_),
fuchsia::sys::Loader::Name_);
} else {
AllowParentService(fuchsia::sys::Loader::Name_);
}
if (parent_overrides.debug_data_service_) {
AddSharedService(std::move(parent_overrides.debug_data_service_),
fuchsia::debugdata::DebugData::Name_);
} else {
AllowParentService(fuchsia::debugdata::DebugData::Name_);
}
}
// static
std::unique_ptr<EnvironmentServices> EnvironmentServices::Create(
const fuchsia::sys::EnvironmentPtr& parent_env,
async_dispatcher_t* dispatcher) {
return std::unique_ptr<EnvironmentServices>(
new EnvironmentServices(parent_env, ParentOverrides{}, dispatcher));
}
// static
std::unique_ptr<EnvironmentServices>
EnvironmentServices::CreateWithParentOverrides(
const fuchsia::sys::EnvironmentPtr& parent_env,
ParentOverrides parent_overrides, async_dispatcher_t* dispatcher) {
return std::unique_ptr<EnvironmentServices>(new EnvironmentServices(
parent_env, std::move(parent_overrides), dispatcher));
}
zx_status_t EnvironmentServices::AddSharedService(
const std::shared_ptr<vfs::Service>& service,
const std::string& service_name) {
svc_names_.push_back(service_name);
return svc_.AddSharedEntry(service_name, service);
}
zx_status_t EnvironmentServices::AddService(
std::unique_ptr<vfs::Service> service, const std::string& service_name) {
svc_names_.push_back(service_name);
return svc_.AddEntry(service_name, std::move(service));
}
zx_status_t EnvironmentServices::AddServiceWithLaunchInfo(
fuchsia::sys::LaunchInfo launch_info, const std::string& service_name) {
return AddServiceWithLaunchInfo(
launch_info.url,
[launch_info = std::move(launch_info)]() {
// clone only URL and Arguments
fuchsia::sys::LaunchInfo dup_launch_info;
fidl::Clone(launch_info.url, &dup_launch_info.url);
fidl::Clone(launch_info.arguments, &dup_launch_info.arguments);
return dup_launch_info;
},
service_name);
}
zx_status_t EnvironmentServices::AddServiceWithLaunchInfo(
std::string singleton_id, fit::function<fuchsia::sys::LaunchInfo()> handler,
const std::string& service_name) {
auto child = std::make_unique<vfs::Service>(
[this, service_name, handler = std::move(handler),
singleton_id = std::move(singleton_id),
controller = fuchsia::sys::ComponentControllerPtr()](
zx::channel client_handle, async_dispatcher_t* dispatcher) mutable {
auto it = singleton_services_.find(singleton_id);
if (it == singleton_services_.end()) {
fuchsia::sys::LaunchInfo launch_info = handler();
auto services = sys::ServiceDirectory::CreateWithRequest(
&launch_info.directory_request);
enclosing_env_->CreateComponent(std::move(launch_info),
controller.NewRequest());
controller.set_error_handler(
[this, singleton_id, &controller](zx_status_t status) {
// TODO: show error? where on stderr?
controller.Unbind(); // kills the singleton application
singleton_services_.erase(singleton_id);
});
controller.events().OnTerminated =
[this, singleton_id](int64_t exit_code,
fuchsia::sys::TerminationReason reason) {
if (service_terminated_callback_) {
service_terminated_callback_(singleton_id, exit_code, reason);
}
};
std::tie(it, std::ignore) =
singleton_services_.emplace(singleton_id, std::move(services));
}
it->second->Connect(service_name, std::move(client_handle));
});
svc_names_.push_back(service_name);
return svc_.AddEntry(service_name, std::move(child));
}
zx_status_t EnvironmentServices::AllowParentService(
const std::string& service_name) {
svc_names_.push_back(service_name);
return svc_.AddEntry(
service_name.c_str(),
std::make_unique<vfs::Service>(
[this, service_name](zx::channel channel,
async_dispatcher_t* dispatcher) {
parent_svc_->Connect(service_name, std::move(channel));
}));
}
fidl::InterfaceHandle<fuchsia::io::Directory>
EnvironmentServices::ServeServiceDir(uint32_t flags) {
fidl::InterfaceHandle<fuchsia::io::Directory> dir;
ZX_ASSERT(ServeServiceDir(dir.NewRequest(), flags) == ZX_OK);
return dir;
}
zx_status_t EnvironmentServices::ServeServiceDir(
fidl::InterfaceRequest<fuchsia::io::Directory> request, uint32_t flags) {
return ServeServiceDir(request.TakeChannel(), flags);
}
zx_status_t EnvironmentServices::ServeServiceDir(zx::channel request,
uint32_t flags) {
return svc_.Serve(flags, std::move(request), dispatcher_);
}
EnclosingEnvironment::EnclosingEnvironment(
const std::string& label, const fuchsia::sys::EnvironmentPtr& parent_env,
std::unique_ptr<EnvironmentServices> services,
const fuchsia::sys::EnvironmentOptions& options)
: label_(label), services_(std::move(services)) {
services_->set_enclosing_env(this);
// Start environment with services.
fuchsia::sys::ServiceListPtr service_list(new fuchsia::sys::ServiceList);
service_list->names = std::move(services_->svc_names_);
service_list->host_directory = services_->ServeServiceDir().TakeChannel();
fuchsia::sys::EnvironmentPtr env;
parent_env->CreateNestedEnvironment(env.NewRequest(),
env_controller_.NewRequest(), label_,
std::move(service_list), options);
env_controller_.set_error_handler(
[this](zx_status_t status) { SetRunning(false); });
// Connect to launcher
env->GetLauncher(launcher_.NewRequest());
zx::channel request;
service_provider_ = sys::ServiceDirectory::CreateWithRequest(&request);
// Connect to service
env->GetDirectory(std::move(request));
env_controller_.events().OnCreated = [this]() { SetRunning(true); };
}
// static
std::unique_ptr<EnclosingEnvironment> EnclosingEnvironment::Create(
const std::string& label, const fuchsia::sys::EnvironmentPtr& parent_env,
std::unique_ptr<EnvironmentServices> services,
const fuchsia::sys::EnvironmentOptions& options) {
auto* env =
new EnclosingEnvironment(label, parent_env, std::move(services), options);
return std::unique_ptr<EnclosingEnvironment>(env);
}
EnclosingEnvironment::~EnclosingEnvironment() {
auto channel = env_controller_.Unbind();
if (channel) {
fuchsia::sys::EnvironmentControllerSyncPtr controller;
controller.Bind(std::move(channel));
controller->Kill();
}
}
void EnclosingEnvironment::Kill(fit::function<void()> callback) {
env_controller_->Kill([callback = std::move(callback)]() {
if (callback) {
callback();
}
});
}
std::unique_ptr<EnclosingEnvironment>
EnclosingEnvironment::CreateNestedEnclosingEnvironment(
const std::string& label) {
fuchsia::sys::EnvironmentPtr env;
service_provider_->Connect(env.NewRequest());
return Create(label, env, EnvironmentServices::Create(env));
}
void EnclosingEnvironment::CreateComponent(
fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> request) {
launcher_.CreateComponent(std::move(launch_info), std::move(request));
}
fuchsia::sys::ComponentControllerPtr EnclosingEnvironment::CreateComponent(
fuchsia::sys::LaunchInfo launch_info) {
fuchsia::sys::ComponentControllerPtr controller;
CreateComponent(std::move(launch_info), controller.NewRequest());
return controller;
}
fuchsia::sys::ComponentControllerPtr
EnclosingEnvironment::CreateComponentFromUrl(std::string component_url) {
fuchsia::sys::LaunchInfo launch_info;
launch_info.url = component_url;
return CreateComponent(std::move(launch_info));
}
void EnclosingEnvironment::SetRunning(bool running) {
running_ = running;
if (running_changed_callback_) {
running_changed_callback_(running_);
}
}
} // namespace testing
} // namespace sys