blob: 17a864e2016eb53f0e8a5ffeea54217b0d6be62a [file] [log] [blame] [edit]
// Copyright 2022 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 LIB_DRIVER2_OUTGOING_DIRECTORY_H_
#define LIB_DRIVER2_OUTGOING_DIRECTORY_H_
#include <lib/driver2/handlers.h>
#include <lib/fdf/cpp/channel.h>
#include <lib/fdf/cpp/protocol.h>
#include <lib/fdf/dispatcher.h>
#include <lib/fidl_driver/cpp/transport.h>
#include <lib/sys/component/cpp/outgoing_directory.h>
namespace driver {
// The name of the default FIDL Service instance.
constexpr const std::string_view kDefaultInstance = component::kDefaultInstance;
// Driver specific implementation for an outgoing directory that wraps around
// |component::OutgoingDirectory|.
class OutgoingDirectory final {
public:
static OutgoingDirectory Create(fdf_dispatcher_t* dispatcher) {
ZX_ASSERT_MSG(dispatcher != nullptr,
"OutgoingDirectory::Create received nullptr |dispatcher|.");
auto component_outgoing_dir =
component::OutgoingDirectory::Create(fdf_dispatcher_get_async_dispatcher(dispatcher));
return OutgoingDirectory(std::move(component_outgoing_dir), dispatcher);
}
OutgoingDirectory(component::OutgoingDirectory component_outgoing_dir,
fdf_dispatcher_t* dispatcher)
: component_outgoing_dir_(std::move(component_outgoing_dir)), dispatcher_(dispatcher) {}
// OutgoingDirectory can be moved. Once moved, invoking a method on an
// instance will yield undefined behavior.
OutgoingDirectory(OutgoingDirectory&&) noexcept;
OutgoingDirectory& operator=(OutgoingDirectory&&) noexcept;
// OutgoingDirectory cannot be copied.
OutgoingDirectory(const OutgoingDirectory&) = delete;
OutgoingDirectory& operator=(const OutgoingDirectory&) = delete;
// Adds an instance of a FIDL Service.
//
// A |handler| is added to provide an |instance| of a service.
//
// The template type |Service| must be the generated type representing a FIDL Service.
// The generated class |Service::Handler| helps the caller populate a
// |ServiceInstanceHandler|.
//
// # Errors
//
// ZX_ERR_ALREADY_EXISTS: The instance already exists.
//
// ZX_ERR_INVALID_ARGS: |instance| is an empty string or |handler| is empty.
template <typename Service>
zx::status<> AddService(ServiceInstanceHandler handler,
cpp17::string_view instance = kDefaultInstance) {
bool is_channel_transport = false;
if constexpr (std::is_same_v<typename Service::Transport, fidl::internal::ChannelTransport>) {
is_channel_transport = true;
} else if constexpr (std::is_same_v<typename Service::Transport,
fidl::internal::DriverTransport>) {
} else {
static_assert(always_false<Service>);
}
auto handlers = handler.TakeMemberHandlers();
if (handlers.empty()) {
return zx::make_status(ZX_ERR_INVALID_ARGS);
}
std::string basepath =
std::string("svc") + "/" + std::string(Service::Name) + "/" + std::string(instance);
for (auto& [member_name, member_handler] : handlers) {
auto outgoing_handler = [this, is_channel_transport, handler = std::move(member_handler)](
zx::channel server_end) mutable {
ZX_ASSERT(server_end.is_valid());
if (is_channel_transport) {
handler(fidl::internal::MakeAnyTransport(std::move(server_end)));
return;
}
// The received |server_end| is the token channel handle, which needs to be registered
// with the runtime.
RegisterRuntimeToken(std::move(server_end), handler.share());
};
zx::status<> status =
component_outgoing_dir_.AddProtocolAt(std::move(outgoing_handler), basepath, member_name);
if (status.is_error()) {
return status;
}
}
return zx::ok();
}
//
// Wrappers around |component::OutgoingDirectory|.
//
// Protocols are not supported. Drivers should use |AddService| instead.
//
// Starts serving the outgoing directory on the given channel. This should
// be invoked after the outgoing directory has been populated, e.g. via
// |AddService|.
//
// This object will implement the |fuchsia.io.Directory| interface using this
// channel. Note that this method returns immediately and that the |dispatcher|
// provided to the constructor will be responsible for processing messages
// sent to the server endpoint.
//
// # Errors
//
// ZX_ERR_BAD_HANDLE: |directory_server_end| is not a valid handle.
//
// ZX_ERR_ACCESS_DENIED: |directory_server_end| has insufficient rights.
zx::status<> Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_server_end) {
return component_outgoing_dir_.Serve(std::move(directory_server_end));
}
// Serve a subdirectory at the root of this outgoing directory.
//
// The directory will be installed under the path |directory_name|. When
// a request is received under this path, then it will be forwarded to
// |remote_dir|.
//
// # Errors
//
// ZX_ERR_ALREADY_EXISTS: An entry with the provided name already exists.
//
// ZX_ERR_BAD_HANDLE: |remote_dir| is an invalid handle.
//
// ZX_ERR_INVALID_ARGS: |directory_name| is an empty string.
zx::status<> AddDirectory(fidl::ClientEnd<fuchsia_io::Directory> remote_dir,
cpp17::string_view directory_name) {
return component_outgoing_dir_.AddDirectory(std::move(remote_dir), directory_name);
}
// Same as |AddDirectory| but allows setting the parent directory
// in which the directory will be installed.
zx::status<> AddDirectoryAt(fidl::ClientEnd<fuchsia_io::Directory> remote_dir,
cpp17::string_view path, cpp17::string_view directory_name) {
return component_outgoing_dir_.AddDirectoryAt(std::move(remote_dir), path, directory_name);
}
// Removes an instance of a FIDL Service.
//
// # Errors
//
// ZX_ERR_NOT_FOUND: The instance was not found.
//
// # Example
//
// ```
// outgoing.RemoveService<lib_example::MyService>("my-instance");
// ```
template <typename Service>
zx::status<> RemoveService(cpp17::string_view instance = kDefaultInstance) {
return RemoveService(Service::Name, instance);
}
// Same as above but untyped.
zx::status<> RemoveService(cpp17::string_view service,
cpp17::string_view instance = kDefaultInstance) {
return component_outgoing_dir_.RemoveService(service, instance);
}
// Removes the subdirectory on the provided |directory_name|.
//
// # Errors
//
// ZX_ERR_NOT_FOUND: No entry was found with provided name.
zx::status<> RemoveDirectory(cpp17::string_view directory_name) {
return component_outgoing_dir_.RemoveDirectory(directory_name);
}
// Same as |RemoveDirectory| but allows specifying the parent directory
// that the directory will be removed from. The parent directory, |path|,
// will not be removed.
zx::status<> RemoveDirectoryAt(cpp17::string_view path, cpp17::string_view directory_name) {
return component_outgoing_dir_.RemoveDirectoryAt(path, directory_name);
}
// Get the underlying component outgoing directory. These APIs will only support
// FIDL, they do not support DriverTransport.
component::OutgoingDirectory& component() { return component_outgoing_dir_; }
private:
template <typename T>
static constexpr std::false_type always_false{};
// Registers |token| with the driver runtime. When the client attempts to connect using the
// token channel peer, we will call |handler|.
void RegisterRuntimeToken(zx::channel token, AnyHandler handler);
component::OutgoingDirectory component_outgoing_dir_;
fdf_dispatcher_t* dispatcher_ = nullptr;
};
} // namespace driver
#endif // LIB_DRIVER2_OUTGOING_DIRECTORY_H_