| // Copyright 2016 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 "src/sys/appmgr/namespace.h" |
| |
| #include <fuchsia/process/cpp/fidl.h> |
| #include <fuchsia/sys/internal/cpp/fidl.h> |
| #include <fuchsia/sys2/cpp/fidl.h> |
| #include <lib/async/default.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/trace/event.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| |
| #include <utility> |
| |
| #include "fbl/ref_ptr.h" |
| #include "fuchsia/sys/cpp/fidl.h" |
| #include "lib/async/cpp/task.h" |
| #include "src/sys/appmgr/job_provider_impl.h" |
| #include "src/sys/appmgr/realm.h" |
| #include "src/sys/appmgr/util.h" |
| |
| namespace component { |
| |
| Namespace::Namespace(fxl::WeakPtr<Realm> realm, fuchsia::sys::ServiceListPtr additional_services, |
| const std::vector<std::string>* service_allowlist) |
| : Namespace(PrivateConstructor{}, nullptr, std::move(realm), std::move(additional_services), |
| service_allowlist) {} |
| |
| Namespace::Namespace(PrivateConstructor p, fxl::RefPtr<Namespace> parent, fxl::WeakPtr<Realm> realm, |
| fuchsia::sys::ServiceListPtr additional_services, |
| const std::vector<std::string>* service_allowlist) |
| : vfs_(async_get_default_dispatcher()), weak_ptr_factory_(this), status_(Status::RUNNING) { |
| fbl::RefPtr<LogConnectorImpl> connector; |
| if (realm) { |
| connector = realm->log_connector(); |
| } |
| services_ = fbl::AdoptRef(new ServiceProviderDirImpl(connector, service_allowlist)); |
| job_provider_ = fbl::AdoptRef(new JobProviderImpl(realm.get())); |
| realm_ = std::move(realm); |
| // WARNING! Do not add new services here! This makes services available in all |
| // component namespaces ambiently without requiring proper routing between |
| // realms, and this list should not be expanded. |
| services_->AddService( |
| fuchsia::sys::Environment::Name_, fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| if (status_ == Status::RUNNING) { |
| environment_bindings_.AddBinding( |
| this, fidl::InterfaceRequest<fuchsia::sys::Environment>(std::move(channel))); |
| } |
| return ZX_OK; |
| }))); |
| services_->AddService(Launcher::Name_, fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| if (status_ == Status::RUNNING) { |
| launcher_bindings_.AddBinding( |
| this, fidl::InterfaceRequest<Launcher>(std::move(channel))); |
| } |
| return ZX_OK; |
| }))); |
| services_->AddService( |
| fuchsia::process::Launcher::Name_, fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| if (realm_) { |
| realm_->environment_services()->Connect( |
| fidl::InterfaceRequest<fuchsia::process::Launcher>(std::move(channel))); |
| return ZX_OK; |
| } |
| return ZX_ERR_BAD_STATE; |
| }))); |
| services_->AddService( |
| fuchsia::process::Resolver::Name_, fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| resolver_bindings_.AddBinding( |
| this, fidl::InterfaceRequest<fuchsia::process::Resolver>(std::move(channel))); |
| return ZX_OK; |
| }))); |
| |
| // WARNING! Do not add new services here! This makes services available in all |
| // component namespaces ambiently without requiring proper routing between |
| // realms, and this list should not be expanded. |
| |
| if (additional_services) { |
| auto& names = additional_services->names; |
| service_provider_ = additional_services->provider.Bind(); |
| service_host_directory_ = std::move(additional_services->host_directory); |
| for (auto& name : names) { |
| if (service_host_directory_) { |
| services_->AddService(name, |
| fbl::AdoptRef(new fs::Service([this, name](zx::channel channel) { |
| fdio_service_connect_at(service_host_directory_.get(), name.c_str(), |
| channel.release()); |
| return ZX_OK; |
| }))); |
| } else { |
| services_->AddService(name, |
| fbl::AdoptRef(new fs::Service([this, name](zx::channel channel) { |
| service_provider_->ConnectToService(name, std::move(channel)); |
| return ZX_OK; |
| }))); |
| } |
| } |
| } |
| |
| // If any services in |parent| share a name with |additional_services|, |
| // |additional_services| takes priority. |
| if (parent) { |
| services_->set_parent(parent->services()); |
| parent_ = parent->weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| services_->InitLogging(); |
| } |
| |
| Namespace::~Namespace() {} |
| |
| fxl::RefPtr<Namespace> Namespace::CreateChildNamespace( |
| fxl::RefPtr<Namespace>& parent, fxl::WeakPtr<Realm> realm, |
| fuchsia::sys::ServiceListPtr additional_services, |
| const std::vector<std::string>* service_allowlist) { |
| ZX_ASSERT(parent); |
| if (parent->status_ != Status::RUNNING) { |
| return nullptr; |
| } |
| fxl::RefPtr<Namespace> ns = |
| fxl::MakeRefCounted<Namespace>(PrivateConstructor{}, parent, std::move(realm), |
| std::move(additional_services), service_allowlist); |
| parent->AddChild(ns); |
| return ns; |
| } |
| |
| void Namespace::AddChild(fxl::RefPtr<Namespace> child) { |
| children_.emplace(child.get(), std::move(child)); |
| } |
| |
| void Namespace::AddBinding(fidl::InterfaceRequest<fuchsia::sys::Environment> environment) { |
| environment_bindings_.AddBinding(this, std::move(environment)); |
| } |
| |
| void Namespace::CreateNestedEnvironment( |
| fidl::InterfaceRequest<fuchsia::sys::Environment> environment, |
| fidl::InterfaceRequest<fuchsia::sys::EnvironmentController> controller, std::string label, |
| fuchsia::sys::ServiceListPtr additional_services, fuchsia::sys::EnvironmentOptions options) { |
| if (realm_ && status_ == Status::RUNNING) { |
| realm_->CreateNestedEnvironment(std::move(environment), std::move(controller), std::move(label), |
| std::move(additional_services), options); |
| } |
| } |
| |
| void Namespace::GetLauncher(fidl::InterfaceRequest<Launcher> launcher) { |
| launcher_bindings_.AddBinding(this, std::move(launcher)); |
| } |
| |
| void Namespace::GetServices(fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> services) { |
| services_->AddBinding(std::move(services)); |
| } |
| |
| zx_status_t Namespace::ServeServiceDirectory(zx::channel directory_request) { |
| return vfs_.ServeDirectory(services_, std::move(directory_request)); |
| } |
| |
| void Namespace::CreateComponent( |
| fuchsia::sys::LaunchInfo launch_info, |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) { |
| if (status_ != Status::RUNNING) { |
| ComponentRequestWrapper component_request(std::move(controller)); |
| component_request.SetReturnValues(-1, fuchsia::sys::TerminationReason::REALM_SHUTTING_DOWN); |
| return; |
| } |
| auto cc_trace_id = TRACE_NONCE(); |
| TRACE_ASYNC_BEGIN("appmgr", "Namespace::CreateComponent", cc_trace_id, "launch_info.url", |
| launch_info.url); |
| if (realm_) { |
| realm_->CreateComponent(std::move(launch_info), std::move(controller), |
| [cc_trace_id](std::weak_ptr<ComponentControllerImpl> component) { |
| TRACE_ASYNC_END("appmgr", "Namespace::CreateComponent", cc_trace_id); |
| }); |
| } else { |
| ComponentRequestWrapper component_request(std::move(controller)); |
| component_request.SetReturnValues(-1, fuchsia::sys::TerminationReason::REALM_SHUTTING_DOWN); |
| } |
| } |
| |
| zx::channel Namespace::OpenServicesAsDirectory() { return Util::OpenAsDirectory(&vfs_, services_); } |
| |
| void Namespace::Resolve(std::string name, fuchsia::process::Resolver::ResolveCallback callback) { |
| if (realm_) { |
| realm_->Resolve(name, std::move(callback)); |
| } else { |
| zx::vmo binary; |
| fidl::InterfaceHandle<fuchsia::ldsvc::Loader> loader; |
| callback(ZX_ERR_UNAVAILABLE, std::move(binary), std::move(loader)); |
| } |
| } |
| |
| void Namespace::NotifyComponentDiagnosticsDirReady( |
| const std::string& component_url, const std::string& component_name, |
| const std::string& component_id, fidl::InterfaceHandle<fuchsia::io::Directory> directory) { |
| if (realm_) { |
| realm_->NotifyComponentDiagnosticsDirReady(component_url, component_name, component_id, |
| std::move(directory)); |
| } |
| } |
| |
| void Namespace::NotifyComponentStarted(const std::string& component_url, |
| const std::string& component_name, |
| const std::string& component_id) { |
| if (realm_) { |
| realm_->NotifyComponentStarted(component_url, component_name, component_id); |
| } |
| } |
| |
| void Namespace::NotifyComponentStopped(const std::string& component_url, |
| const std::string& component_name, |
| const std::string& component_id) { |
| if (realm_) { |
| realm_->NotifyComponentStopped(component_url, component_name, component_id); |
| } |
| } |
| |
| void Namespace::MaybeAddComponentEventProvider() { |
| if (services_->IsServiceAllowlisted(fuchsia::sys::internal::ComponentEventProvider::Name_)) { |
| services_->AddService( |
| fuchsia::sys::internal::ComponentEventProvider::Name_, |
| fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| if (realm_) { |
| return realm_->BindComponentEventProvider( |
| fidl::InterfaceRequest<fuchsia::sys::internal::ComponentEventProvider>( |
| std::move(channel))); |
| } |
| return ZX_ERR_BAD_STATE; |
| }))); |
| } |
| } |
| |
| void Namespace::RunShutdownIfNoChildren(fxl::RefPtr<Namespace> ns) { |
| if (ns->status_ == Status::SHUTTING_DOWN && ns->children_.empty()) { |
| ns->vfs_.CloseAllConnectionsForVnode(*ns->services_, [ns]() { |
| ns->status_ = Status::STOPPED; |
| for (auto& callback : ns->shutdown_callbacks_) { |
| async::PostTask(async_get_default_dispatcher(), |
| [callback = std::move(callback)]() mutable { callback(); }); |
| } |
| ns->shutdown_callbacks_.clear(); |
| ns->vfs_.Shutdown([ns](zx_status_t /*unused*/) mutable { ns.reset(); }); |
| }); |
| } |
| } |
| |
| void Namespace::ExtractChild(fxl::RefPtr<Namespace> ns, Namespace* child) { |
| ns->children_.erase(child); |
| RunShutdownIfNoChildren(std::move(ns)); |
| } |
| |
| void Namespace::FlushAndShutdown(fxl::RefPtr<Namespace> self, |
| fs::ManagedVfs::CloseAllConnectionsForVnodeCallback callback) { |
| ZX_ASSERT(self.get() == this); |
| switch (status_) { |
| case Status::SHUTTING_DOWN: |
| // We are already shutting down. Store callback and return. |
| if (callback) { |
| shutdown_callbacks_.push_back(std::move(callback)); |
| } |
| return; |
| case Status::STOPPED: |
| if (callback) { |
| async::PostTask(async_get_default_dispatcher(), |
| [self, callback = std::move(callback)]() mutable { callback(); }); |
| } |
| return; |
| case Status::RUNNING: |
| if (callback) { |
| shutdown_callbacks_.push_back(std::move(callback)); |
| } |
| break; |
| } |
| status_ = Status::SHUTTING_DOWN; |
| environment_bindings_.CloseAll(); |
| launcher_bindings_.CloseAll(); |
| if (children_.empty()) { |
| RunShutdownIfNoChildren(std::move(self)); |
| return; |
| } |
| |
| for (auto& child : children_) { |
| async::PostTask(async_get_default_dispatcher(), [ns = child.second, self]() { |
| ns->FlushAndShutdown(ns, [ptr = ns.get(), self]() { ExtractChild(self, ptr); }); |
| }); |
| } |
| } |
| |
| } // namespace component |