blob: 8b5c79570cbd14b37d6ef0a2191702c7720eb010 [file] [log] [blame] [edit]
#pragma once
#include <sys/inotify.h>
#include <unistd.h>
#include <sdbusplus/async/context.hpp>
#include <sdbusplus/async/fdio.hpp>
#include <sdbusplus/async/task.hpp>
#include <array>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <memory>
#include <span>
#include <string>
#include <system_error>
namespace phosphor::notify::watch
{
namespace fs = std::filesystem;
template <typename Instance>
class NotifyWatch
{
public:
NotifyWatch() = delete;
NotifyWatch(const NotifyWatch&) = delete;
NotifyWatch& operator=(const NotifyWatch&) = delete;
NotifyWatch(NotifyWatch&&) = delete;
NotifyWatch& operator=(NotifyWatch&&) = delete;
explicit NotifyWatch(sdbusplus::async::context& ctx,
const std::string& dir) : notifyCtx(ctx)
{
std::error_code ec = {};
fs::path dirPath(dir);
if (!fs::create_directories(dirPath, ec))
{
if (ec)
{
throw std::system_error(ec,
"Failed to create directory " + dir);
}
}
fd = inotify_init1(IN_NONBLOCK);
if (-1 == fd)
{
throw std::system_error(errno, std::system_category(),
"inotify_init1 failed");
}
wd = inotify_add_watch(fd, dir.c_str(), IN_CLOSE_WRITE);
if (-1 == wd)
{
close(fd);
throw std::system_error(errno, std::system_category(),
"inotify_add_watch failed");
}
fdioInstance = std::make_unique<sdbusplus::async::fdio>(ctx, fd);
}
~NotifyWatch()
{
if (-1 != fd)
{
if (-1 != wd)
{
inotify_rm_watch(fd, wd);
}
close(fd);
}
}
sdbusplus::async::task<> readNotifyAsync()
{
co_await fdioInstance->next();
constexpr size_t maxBytes = 1024;
std::array<uint8_t, maxBytes> buffer{};
auto bytes = read(fd, buffer.data(), maxBytes);
if (0 > bytes)
{
throw std::system_error(errno, std::system_category(),
"Failed to read notify event");
}
auto offset = 0;
while (offset < bytes)
{
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
std::span<uint32_t> mask{
reinterpret_cast<uint32_t*>(
buffer.data() + offset + offsetof(inotify_event, mask)),
1};
std::span<uint32_t> len{
reinterpret_cast<uint32_t*>(
buffer.data() + offset + offsetof(inotify_event, len)),
1};
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
if (((mask[0] & IN_CLOSE_WRITE) != 0U) &&
((mask[0] & IN_ISDIR) == 0U))
{
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
std::span<char> name{
reinterpret_cast<char*>(
buffer.data() + offset + offsetof(inotify_event, name)),
len[0]};
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
co_await static_cast<Instance*>(this)->processUpdate(
std::string(name.begin(), name.end()));
}
offset += offsetof(inotify_event, name) + len[0];
}
if (!notifyCtx.stop_requested())
{
notifyCtx.spawn(readNotifyAsync());
}
}
private:
sdbusplus::async::context& notifyCtx;
int wd = -1;
int fd = -1;
std::unique_ptr<sdbusplus::async::fdio> fdioInstance;
};
} // namespace phosphor::notify::watch