blob: e210ece70bdbefcd1816404ec61703aa1164db41 [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 "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 "lib/syslog/cpp/macros.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::MakeRefCounted<ServiceProviderDirImpl>(connector, service_allowlist);
job_provider_ = fbl::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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::MakeRefCounted<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->status_ = Status::STOPPING;
ns->vfs_.CloseAllConnectionsForVnode(*ns->services_, [ns]() {
ns->vfs_.Shutdown([ns](zx_status_t /*unused*/) mutable {
ns->status_ = Status::STOPPED;
if (ns->parent_) {
bool ret = ns->parent_->RemoveChild(ns.get());
FX_DCHECK(ret);
}
for (auto& callback : ns->shutdown_callbacks_) {
async::PostTask(async_get_default_dispatcher(),
[callback = std::move(callback)]() mutable { callback(); });
}
ns->shutdown_callbacks_.clear();
ns.reset();
});
});
}
}
bool Namespace::RemoveChild(Namespace* child) { return children_.erase(child) == 1; }
void Namespace::FlushAndShutdown(fxl::RefPtr<Namespace> self,
fs::ManagedVfs::CloseAllConnectionsForVnodeCallback callback) {
ZX_ASSERT(self.get() == this);
switch (status_) {
case Status::SHUTTING_DOWN:
case Status::STOPPING:
// We are already stopping/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]() { RunShutdownIfNoChildren(self); });
});
}
}
} // namespace component