blob: 3275f83c68c9e191829ee892933b6bf13fb057df [file] [log] [blame] [edit]
// 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_