blob: d64ba03209243d45f9c21b83b3e42024460d9a23 [file] [log] [blame]
// Copyright 2021 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/devices/bin/driver_manager/devfs_exporter.h"
#include "src/devices/lib/log/log.h"
#include "src/lib/storage/vfs/cpp/service.h"
namespace fdfs = fuchsia_device_fs;
namespace driver_manager {
zx::status<std::unique_ptr<ExportWatcher>> ExportWatcher::Create(
async_dispatcher_t* dispatcher, Devnode* root,
fidl::ClientEnd<fuchsia_io::Directory> service_dir, std::string_view service_path,
std::string_view devfs_path, uint32_t protocol_id) {
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
if (endpoints.is_error()) {
return zx::error(endpoints.error_value());
}
auto response =
fidl::WireCall(service_dir)
->Open(fuchsia_io::wire::OpenFlags::kRightReadable |
fuchsia_io::wire::OpenFlags::kRightWritable,
0, fidl::StringView::FromExternal(service_path), std::move(endpoints->server));
if (!response.ok()) {
return zx::error(response.error().status());
}
auto watcher = std::make_unique<ExportWatcher>();
watcher->client_ = fidl::WireClient(std::move(endpoints->client), dispatcher, watcher.get());
zx_status_t status = devfs_export(root, std::move(service_dir), service_path, devfs_path,
protocol_id, watcher->devnodes_);
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(watcher));
}
DevfsExporter::DevfsExporter(Devnode* root, async_dispatcher_t* dispatcher)
: root_(root), dispatcher_(dispatcher) {}
zx::status<> DevfsExporter::PublishExporter(const fbl::RefPtr<fs::PseudoDir>& svc_dir) {
const auto service = [this](fidl::ServerEnd<fdfs::Exporter> request) {
fidl::BindServer(dispatcher_, std::move(request), this);
return ZX_OK;
};
zx_status_t status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdfs::Exporter>,
fbl::MakeRefCounted<fs::Service>(service));
if (status != ZX_OK) {
LOGF(ERROR, "Failed to add directory entry '%s': %s",
fidl::DiscoverableProtocolName<fdfs::Exporter>, zx_status_get_string(status));
}
return zx::make_status(status);
}
void DevfsExporter::Export(ExportRequestView request, ExportCompleter::Sync& completer) {
auto result = ExportWatcher::Create(dispatcher_, root_, std::move(request->service_dir),
request->service_path.get(), request->devfs_path.get(),
request->protocol_id);
if (result.is_error()) {
LOGF(ERROR, "Failed to export service to devfs path \"%.*s\": %s",
static_cast<int>(request->devfs_path.size()), request->devfs_path.data(),
result.status_string());
completer.ReplyError(result.error_value());
return;
}
ExportWatcher* export_ptr = result.value().get();
exports_.push_back(std::move(result.value()));
// Set a callback so when the ExportWatcher sees its connection closed, it
// will delete itself and its devnodes.
export_ptr->set_on_close_callback([this, export_ptr]() {
exports_.erase(std::remove_if(exports_.begin(), exports_.end(),
[&](const auto& it) { return it.get() == export_ptr; }),
exports_.end());
});
completer.ReplySuccess();
}
} // namespace driver_manager