blob: 48619480665b57fa107bda2ca9358daf003c329e [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/storage/fshost/watcher.h"
#include <fcntl.h>
#include <lib/syslog/cpp/macros.h>
#include "src/storage/fshost/block-device-manager.h"
#include "src/storage/fshost/filesystem-mounter.h"
#include "src/storage/fshost/nand-device.h"
namespace fshost {
namespace {
namespace fio = fuchsia_io;
template <typename T>
zx_status_t AddDeviceImpl(BlockDeviceManager& manager, FilesystemMounter* mounter,
fbl::unique_fd fd) {
T device(mounter, std::move(fd), manager.config());
return manager.AddDevice(device);
}
} // namespace
std::vector<Watcher> Watcher::CreateWatchers() {
std::pair<const char*, AddDeviceCallback> types[] = {
{"/dev/class/block", AddDeviceImpl<BlockDevice>},
{"/dev/class/nand", AddDeviceImpl<NandDevice>},
};
std::vector<Watcher> ret;
for (auto& [path, callback] : types) {
fbl::unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
if (!dirfd) {
FX_LOGS(ERROR) << "failed to open " << path << ": " << strerror(errno);
continue;
}
fdio_cpp::FdioCaller caller(std::move(dirfd));
ret.emplace_back(Watcher(path, std::move(caller), std::move(callback)));
}
return ret;
}
zx_status_t Watcher::ReinitWatcher() {
watcher_.reset();
zx::status server_end = fidl::CreateEndpoints<fio::DirectoryWatcher>(&watcher_);
if (server_end.is_error()) {
FX_PLOGS(ERROR, server_end.status_value()) << "failed to create watcher channel";
return server_end.status_value();
}
auto mask = fio::wire::WatchMask::kMask;
if (ignore_existing_) {
mask &= ~fio::wire::WatchMask::kExisting;
}
auto result = fidl::WireCall(caller_.borrow_as<fio::Directory>())
->Watch(mask, 0, std::move(server_end.value()));
if (!result.ok()) {
FX_LOGS(ERROR) << "failed to send watch: " << result.error();
return result.status();
}
if (result.value().s != ZX_OK) {
FX_LOGS(ERROR) << "watch failed: " << zx_status_get_string(result.value().s);
return result.value().s;
}
return ZX_OK;
}
void Watcher::ProcessWatchMessages(cpp20::span<uint8_t> buf, WatcherCallback callback) {
uint8_t* iter = buf.begin();
while (iter + 2 <= buf.end()) {
fio::wire::WatchEvent event = static_cast<fio::wire::WatchEvent>(*iter++);
uint8_t name_len = *iter++;
if (iter + name_len > buf.end()) {
break;
}
// Save the first byte of the next message,
// and null-terminate the name.
uint8_t tmp = iter[name_len];
iter[name_len] = 0;
if (callback(*this, caller_.fd().get(), event, reinterpret_cast<const char*>(iter))) {
// We received a WATCH_EVENT_IDLE, and the watcher is paused.
// Bail out early.
ignore_existing_ = true;
return;
}
if (event == fio::wire::WatchEvent::kIdle) {
// WATCH_EVENT_IDLE - but the watcher is not paused. Finish processing this set of messages,
// but set ignore_existing_ to indicate that we should start checking for the pause signal.
ignore_existing_ = true;
break;
}
iter[name_len] = tmp;
iter += name_len;
}
}
zx_status_t Watcher::AddDevice(BlockDeviceManager& manager, FilesystemMounter* mounter,
fbl::unique_fd fd) {
return add_device_(manager, mounter, std::move(fd));
}
} // namespace fshost