blob: 3b4dc7f6c0baa129443b3f3546e964c7b302347a [file] [log] [blame]
// 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 <fuchsia/sys/cpp/fidl.h>
#include <lib/async/cpp/executor.h>
#include <lib/async/cpp/wait.h>
#include <lib/fpromise/promise.h>
#include <lib/zx/process.h>
#include <lib/zx/status.h>
#include <zircon/assert.h>
#include <zircon/types.h>
#include <vector>
#include "lib/fidl/cpp/binding.h"
#include "src/lib/storage/vfs/cpp/pseudo_dir.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( Solve the general problem this solves.
class ComponentRequestWrapper {
explicit ComponentRequestWrapper(
fidl::InterfaceRequest<fuchsia::sys::ComponentController> request,
int64_t default_return = -1,
fuchsia::sys::TerminationReason default_reason = fuchsia::sys::TerminationReason::UNKNOWN);
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;
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 {
FailedComponentController(int64_t return_code, fuchsia::sys::TerminationReason termination_reason,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller);
~FailedComponentController() override;
void Kill() override;
void Detach() override;
fidl::Binding<fuchsia::sys::ComponentController> binding_;
int64_t return_code_;
fuchsia::sys::TerminationReason termination_reason_;
class ComponentControllerBase : public fuchsia::sys::ComponentController {
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;
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.
fpromise::promise<fidl::InterfaceHandle<fuchsia::io::Directory>, zx_status_t> GetDiagnosticsDir();
// Provides a handle to the component out/svc directory if one exists.
fpromise::promise<fidl::InterfaceHandle<fuchsia::io::Directory>, zx_status_t> GetServiceDir();
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 {
return ns_->services();
void SendOnDirectoryReadyEvent();
void SendOnTerminationEvent(int64_t, fuchsia::sys::TerminationReason);
// 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.
fpromise::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://
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
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 {
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_; }
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_;
// This class acts as a bridge between the components created by ComponentRunner
// and |request|.
class ComponentBridge : public ComponentControllerBase {
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();
fuchsia::sys::ComponentControllerPtr remote_controller_;
ComponentContainer<ComponentBridge>* container_;
fuchsia::sys::TerminationReason termination_reason_;
OnTerminatedCallback on_terminated_event_;
} // namespace component