blob: 49acfed53b552947c10d92cdb8ffdebbe92c9051 [file] [log] [blame] [edit]
// Copyright 2019 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/component_event_provider_impl.h"
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include "src/lib/fxl/memory/weak_ptr.h"
namespace component {
ComponentEventProviderImpl::ComponentEventProviderImpl(fxl::WeakPtr<Realm> realm,
async_dispatcher_t* dispatcher)
: executor_(dispatcher), binding_(this), realm_(realm), weak_ptr_factory_(this) {}
ComponentEventProviderImpl::~ComponentEventProviderImpl() = default;
void ComponentEventProviderImpl::SetListener(
fidl::InterfaceHandle<fuchsia::sys::internal::ComponentEventListener> listener) {
if (listener_.is_bound()) {
return;
}
listener_ = listener.Bind();
listener_.set_error_handler([this](zx_status_t status) {
listener_.Unbind();
listener_.set_error_handler(nullptr);
});
const zx_status_t status =
async::PostTask(executor_.dispatcher(), [self = weak_ptr_factory_.GetWeakPtr()] {
if (!self) {
FX_DLOGS(WARNING) << "called posted task after exit, skipping callback";
return;
}
self->NotifyOfExistingComponents();
});
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Could not synthesize events for existing components: "
<< zx_status_get_string(status);
}
}
zx_status_t ComponentEventProviderImpl::Connect(
fidl::InterfaceRequest<fuchsia::sys::internal::ComponentEventProvider> request) {
if (binding_.is_bound()) {
return ZX_ERR_BAD_STATE;
}
binding_.Bind(std::move(request));
return ZX_OK;
}
void ComponentEventProviderImpl::NotifyComponentStarted(
fuchsia::sys::internal::SourceIdentity component) {
if (listener_.is_bound()) {
listener_->OnStart(std::move(component));
}
}
void ComponentEventProviderImpl::NotifyComponentStopped(
fuchsia::sys::internal::SourceIdentity component) {
if (listener_.is_bound()) {
listener_->OnStop(std::move(component));
}
}
void ComponentEventProviderImpl::NotifyComponentDirReady(
fuchsia::sys::internal::SourceIdentity component,
fidl::InterfaceHandle<fuchsia::io::Directory> directory) {
if (listener_.is_bound()) {
listener_->OnDiagnosticsDirReady(std::move(component), std::move(directory));
}
}
std::vector<std::string> ComponentEventProviderImpl::RelativeRealmPath(
const fxl::WeakPtr<Realm>& leaf_realm) {
std::vector<std::string> relative_realm_path;
auto realm = leaf_realm;
// We stop traversing the realm tree bottom up until we arrive to this realm_ or the root.
while (realm && realm.get() != realm_.get()) {
relative_realm_path.push_back(realm->label());
realm = realm->parent();
}
// We arrived to root and we couldn't find |realm_| therefore this realm is not in the path.
// Just a sanity check, this shouldn't occur given that this provider only calls this method with
// realms under it.
if (realm.get() != realm_.get()) {
FX_LOGS(ERROR) << "Unreachable: ComponentEventProvider attempted to get a relative realm path "
<< "from a realm not in its tree";
return {};
}
std::reverse(relative_realm_path.begin(), relative_realm_path.end());
return relative_realm_path;
}
void ComponentEventProviderImpl::NotifyOfExistingComponents() {
std::queue<fxl::WeakPtr<Realm>> pending_realms;
pending_realms.push(realm_);
while (!pending_realms.empty()) {
auto realm = pending_realms.front();
pending_realms.pop();
// Make sure we notify about all components in sub-realms of this realm which don't have an
// event listener attached.
for (auto& pair : realm->children()) {
if (!pair.second->realm()->HasComponentEventListenerBound()) {
pending_realms.push(pair.second->realm()->weak_ptr());
}
}
auto relative_realm_path = RelativeRealmPath(realm);
// Notify about all components in this realm.
for (auto& pair : realm->applications()) {
NotifyAboutExistingComponent(relative_realm_path, pair.second);
}
// Notify about all components in runners in this realm.
for (auto& pair : realm->runners()) {
const auto& runner = pair.second;
for (auto& comp_pair : runner->components()) {
const auto& component_bridge = comp_pair.second;
// Given that an environment might have been created with use_parent_runners, we need to get
// its actual realm which might not be the realm where the runner is.
fxl::WeakPtr<Realm> realm = component_bridge->realm();
if (realm) {
NotifyAboutExistingComponent(RelativeRealmPath(realm), component_bridge);
}
}
}
}
}
void ComponentEventProviderImpl::NotifyAboutExistingComponent(
std::vector<std::string> relative_realm_path,
std::shared_ptr<ComponentControllerBase> application) {
fuchsia::sys::internal::SourceIdentity identity;
identity.set_component_url(application->url());
identity.set_component_name(application->label());
identity.set_instance_id(application->hub_instance_id());
identity.set_realm_path(relative_realm_path);
NotifyComponentStarted(fidl::Clone(identity));
// If the component doesn't have an out/diagnostics directory or its out/ directory ready
// doesn't exist, the and_then combinator won't be executed. Once the component exposes a
// diagnostics directory (if ever), the listener will be notified through the regular flow.
executor_.schedule_task(application->GetDiagnosticsDir().and_then(
[self = weak_ptr_factory_.GetWeakPtr(),
identity = std::move(identity)](fidl::InterfaceHandle<fuchsia::io::Directory>& dir) mutable {
if (self) {
self->NotifyComponentDirReady(std::move(identity), std::move(dir));
}
}));
}
} // namespace component