| // 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. |
| |
| #ifndef SRC_SYS_APPMGR_COMPONENT_CONTROLLER_IMPL_H_ |
| #define SRC_SYS_APPMGR_COMPONENT_CONTROLLER_IMPL_H_ |
| |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/async/cpp/executor.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/fit/promise.h> |
| #include <lib/zx/process.h> |
| #include <lib/zx/status.h> |
| #include <zircon/assert.h> |
| #include <zircon/types.h> |
| |
| #include <vector> |
| |
| #include <fs/pseudo_dir.h> |
| |
| #include "lib/fidl/cpp/binding.h" |
| #include "src/sys/appmgr/component_container.h" |
| #include "src/sys/appmgr/debug_info_retriever.h" |
| #include "src/sys/appmgr/hub/component_hub.h" |
| #include "src/sys/appmgr/hub/hub_info.h" |
| #include "src/sys/appmgr/namespace.h" |
| #include "src/sys/appmgr/system_diagnostics_directory.h" |
| |
| namespace component { |
| |
| // ComponentRequestWrapper wraps failure behavior in the event a Component fails |
| // to start. It wraps the behavior of binding to an incoming interface request |
| // and sending error events to clients before closing the channel. |
| // If there is no error, the wrapped request and callback may be Extract()ed |
| // and bound to a concrete interface. |
| // TODO(fxbug.dev/3981): Solve the general problem this solves. |
| class ComponentRequestWrapper { |
| public: |
| explicit ComponentRequestWrapper( |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> request, |
| int64_t default_return = -1, |
| fuchsia::sys::TerminationReason default_reason = fuchsia::sys::TerminationReason::UNKNOWN); |
| ~ComponentRequestWrapper(); |
| ComponentRequestWrapper(ComponentRequestWrapper&& other); |
| void operator=(ComponentRequestWrapper&& other); |
| |
| void SetReturnValues(int64_t return_code, fuchsia::sys::TerminationReason reason); |
| |
| bool Extract(fidl::InterfaceRequest<fuchsia::sys::ComponentController>* out_request) { |
| if (!request_.is_valid()) { |
| return false; |
| } |
| *out_request = std::move(request_); |
| return true; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ComponentRequestWrapper); |
| |
| private: |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> request_; |
| int64_t return_code_; |
| fuchsia::sys::TerminationReason reason_; |
| }; |
| |
| // FailedComponentController implements the component controller interface for |
| // components that failed to start. This class serves the purpose of actually |
| // binding to a ComponentController channel and passing back a termination |
| // event. |
| class FailedComponentController final : public fuchsia::sys::ComponentController { |
| public: |
| FailedComponentController(int64_t return_code, fuchsia::sys::TerminationReason termination_reason, |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller); |
| ~FailedComponentController() override; |
| void Kill() override; |
| void Detach() override; |
| |
| private: |
| fidl::Binding<fuchsia::sys::ComponentController> binding_; |
| int64_t return_code_; |
| fuchsia::sys::TerminationReason termination_reason_; |
| }; |
| |
| class ComponentControllerBase : public fuchsia::sys::ComponentController { |
| public: |
| ComponentControllerBase(fidl::InterfaceRequest<fuchsia::sys::ComponentController> request, |
| std::string url, std::string args, std::string label, |
| std::string hub_instance_id, fxl::RefPtr<Namespace> ns, |
| zx::channel exported_dir, zx::channel client_request, |
| uint32_t diagnostics_max_retries = 0); |
| virtual ~ComponentControllerBase() override; |
| |
| public: |
| HubInfo HubInfo(); |
| const std::string& label() const { return label_; } |
| const fbl::RefPtr<fs::PseudoDir>& hub_dir() const { return hub_.dir(); } |
| |
| // The instance ID (process koid) of the component in the hub. |
| const std::string& hub_instance_id() const { return hub_instance_id_; } |
| |
| // The url of this component. |
| const std::string& url() const { return url_; } |
| |
| fxl::WeakPtr<Realm> realm() { return ns_->realm(); } |
| |
| // |fuchsia::sys::ComponentController| implementation: |
| void Detach() override; |
| |
| // Provides a handle to the component out/diagnostics directory if one exists. |
| fit::promise<fidl::InterfaceHandle<fuchsia::io::Directory>, zx_status_t> GetDiagnosticsDir(); |
| |
| // Provides a handle to the component out/svc directory if one exists. |
| fit::promise<fidl::InterfaceHandle<fuchsia::io::Directory>, zx_status_t> GetServiceDir(); |
| |
| protected: |
| ComponentHub* hub() { return &hub_; } |
| fxl::RefPtr<Namespace> ns() const { return ns_; } |
| |
| // Returns the incoming services from the namespace. |
| const fbl::RefPtr<ServiceProviderDirImpl>& incoming_services() const { |
| ZX_DEBUG_ASSERT(ns_); |
| return ns_->services(); |
| }; |
| |
| void SendOnDirectoryReadyEvent(); |
| |
| void SendOnTerminationEvent(int64_t, fuchsia::sys::TerminationReason); |
| |
| private: |
| // Notifies a realm's ComponentEventListener with the out/diagnostics directory for a component. |
| void NotifyDiagnosticsDirReady(uint32_t max_retries); |
| |
| // This is the implementation of |GetDiagnosticsDir| and |GetServiceDir| as the only difference is |
| // the name of the directory they are requesting. |
| fit::promise<fidl::InterfaceHandle<fuchsia::io::Directory>, zx_status_t> GetDir(std::string path); |
| |
| async::Executor executor_; |
| |
| fidl::Binding<fuchsia::sys::ComponentController> binding_; |
| |
| // The name of this component: e.g., my_component.cmx |
| std::string label_; |
| |
| // The instance id of this component in the hub (process koid) |
| std::string hub_instance_id_; |
| |
| // The url of this component: e.g., fuchsia-pkg://fuchsia.com/my_package#meta/my_component.cmx |
| std::string url_; |
| |
| ComponentHub hub_; |
| |
| fxl::RefPtr<Namespace> ns_; |
| |
| fxl::WeakPtrFactory<ComponentControllerBase> weak_ptr_factory_; |
| |
| fuchsia::io::NodePtr cloned_exported_dir_; |
| |
| fuchsia::io::DirectoryPtr exported_dir_; |
| |
| // guards against sending this event two times |
| bool on_terminated_event_sent_ = false; |
| |
| // whether the out directory is ready or not. |
| bool out_ready_ = false; |
| |
| uint32_t diagnostics_max_retries_ = 0; |
| |
| // Current retry backoff ms for observing a diagnostics directory. This increases every failure up |
| // to OUT_DIAGNOSTICS_MAXIMUM_DELAY_MS. |
| uint32_t diagnostics_retry_backoff_ms_ = 0; |
| }; |
| |
| // The path to an instance of a component. Includes the realm path, component name, and the koid of |
| // the component's main job. |
| using InstancePath = std::vector<std::string>; |
| |
| class ComponentControllerImpl : public ComponentControllerBase { |
| public: |
| ComponentControllerImpl(fidl::InterfaceRequest<fuchsia::sys::ComponentController> request, |
| ComponentContainer<ComponentControllerImpl>* container, zx::job job, |
| zx::process process, std::string url, std::string args, std::string label, |
| fxl::RefPtr<Namespace> ns, zx::channel exported_dir, |
| zx::channel client_request, zx::channel package_handle); |
| ~ComponentControllerImpl() override; |
| |
| const std::string& koid() const { return process_koid_; } |
| |
| zx_status_t AddSubComponentHub(const component::HubInfo& hub_info); |
| zx_status_t RemoveSubComponentHub(const component::HubInfo& hub_info); |
| |
| // |fuchsia::sys::ComponentController| implementation: |
| void Kill() override; |
| |
| const zx::job& job() const { return job_; } |
| |
| private: |
| void Handler(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, |
| const zx_packet_signal* signal); |
| |
| // Compute the path from the component tree root to this component instance, and store it. |
| // |
| // The path includes the names of all realms, the component name, and the koid of the component's |
| // job. |
| void ComputeComponentInstancePath(); |
| |
| bool SendReturnCodeIfTerminated(); |
| |
| ComponentContainer<ComponentControllerImpl>* container_; |
| zx::job job_; |
| zx::process process_; |
| const std::string process_koid_; |
| const std::string job_koid_; |
| |
| async::WaitMethod<ComponentControllerImpl, &ComponentControllerImpl::Handler> wait_; |
| |
| SystemDiagnosticsDirectory system_diagnostics_; |
| |
| InstancePath instance_path_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(ComponentControllerImpl); |
| }; |
| |
| // This class acts as a bridge between the components created by ComponentRunner |
| // and |request|. |
| class ComponentBridge : public ComponentControllerBase { |
| public: |
| ComponentBridge(fidl::InterfaceRequest<fuchsia::sys::ComponentController> request, |
| fuchsia::sys::ComponentControllerPtr remote_controller, |
| ComponentContainer<ComponentBridge>* container, std::string url, std::string args, |
| std::string label, std::string hub_instance_id, fxl::RefPtr<Namespace> ns, |
| zx::channel exported_dir, zx::channel client_request, |
| std::optional<zx::channel> package_handle); |
| |
| ~ComponentBridge() override; |
| |
| void SetParentJobId(const std::string& id); |
| |
| // Set the termination reason for this bridge. |
| // This should be used when a runner itself terminates and needs to report |
| // back a failure over the bridge when it is closed. |
| void SetTerminationReason(fuchsia::sys::TerminationReason termination_reason); |
| |
| // |fuchsia::sys::ComponentController| implementation: |
| void Kill() override; |
| |
| void OnTerminated(OnTerminatedCallback callback) { on_terminated_event_ = std::move(callback); } |
| |
| void NotifyStopped(); |
| |
| private: |
| fuchsia::sys::ComponentControllerPtr remote_controller_; |
| ComponentContainer<ComponentBridge>* container_; |
| fuchsia::sys::TerminationReason termination_reason_; |
| OnTerminatedCallback on_terminated_event_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(ComponentBridge); |
| }; |
| |
| } // namespace component |
| |
| #endif // SRC_SYS_APPMGR_COMPONENT_CONTROLLER_IMPL_H_ |