| // 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 |