blob: 382f51736b2e47a5ec36acd1166216cb2de929bf [file] [log] [blame]
// Copyright 2017 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 "lib/svc/cpp/service_provider_bridge.h"
#include <fcntl.h>
#include <lib/async/default.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <zircon/device/vfs.h>
#include <utility>
#include <fs/service.h>
#include <fs/vfs_types.h>
namespace component {
ServiceProviderBridge::ServiceProviderBridge()
: vfs_(async_get_default_dispatcher()), weak_factory_(this) {
directory_ = fbl::AdoptRef(new ServiceProviderDir(weak_factory_.GetWeakPtr()));
}
ServiceProviderBridge::~ServiceProviderBridge() = default;
void ServiceProviderBridge::AddBinding(fidl::InterfaceRequest<ServiceProvider> request) {
bindings_.AddBinding(this, std::move(request));
}
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> ServiceProviderBridge::AddBinding() {
return bindings_.AddBinding(this);
}
void ServiceProviderBridge::AddServiceForName(ServiceConnector connector,
const std::string& service_name) {
name_to_service_connector_[service_name] = std::move(connector);
}
bool ServiceProviderBridge::ServeDirectory(zx::channel channel) {
return vfs_.ServeDirectory(directory_, std::move(channel)) == ZX_OK;
}
zx::channel ServiceProviderBridge::OpenAsDirectory() {
zx::channel h1, h2;
if (zx::channel::create(0, &h1, &h2) < 0)
return zx::channel();
if (!ServeDirectory(std::move(h1)))
return zx::channel();
return h2;
}
int ServiceProviderBridge::OpenAsFileDescriptor() {
zx::channel h1, h2;
if (zx::channel::create(0, &h1, &h2) < 0)
return -1;
if (!ServeDirectory(std::move(h1)))
return -1;
int fd = -1;
zx_status_t status = fdio_fd_create(h2.release(), &fd);
if (status != ZX_OK)
return -1;
return fd;
}
void ServiceProviderBridge::ConnectToService(std::string service_name, zx::channel channel) {
auto it = name_to_service_connector_.find(service_name);
if (it != name_to_service_connector_.end())
it->second(std::move(channel));
else if (backend_)
backend_->ConnectToService(service_name, std::move(channel));
else if (backing_dir_)
fdio_service_connect_at(backing_dir_.get(), service_name.c_str(), channel.release());
}
ServiceProviderBridge::ServiceProviderDir::ServiceProviderDir(
fxl::WeakPtr<ServiceProviderBridge> bridge)
: bridge_(std::move(bridge)) {}
ServiceProviderBridge::ServiceProviderDir::~ServiceProviderDir() = default;
fs::VnodeProtocolSet ServiceProviderBridge::ServiceProviderDir::GetProtocols() const {
return fs::VnodeProtocol::kDirectory;
}
zx_status_t ServiceProviderBridge::ServiceProviderDir::Lookup(fbl::RefPtr<fs::Vnode>* out,
fbl::StringPiece name) {
*out = fbl::AdoptRef(new fs::Service(
[bridge = bridge_, name = std::string(name.data(), name.length())](zx::channel channel) {
if (bridge) {
bridge->ConnectToService(name, std::move(channel));
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}));
return ZX_OK;
}
zx_status_t ServiceProviderBridge::ServiceProviderDir::GetAttributes(fs::VnodeAttributes* attr) {
*attr = fs::VnodeAttributes();
attr->mode = V_TYPE_DIR | V_IRUSR;
attr->link_count = 1;
return ZX_OK;
}
zx_status_t ServiceProviderBridge::ServiceProviderDir::GetNodeInfoForProtocol(
[[maybe_unused]] fs::VnodeProtocol protocol, [[maybe_unused]] fs::Rights rights,
fs::VnodeRepresentation* representation) {
*representation = fs::VnodeRepresentation::Directory();
return ZX_OK;
}
} // namespace component