blob: d4d8fafc3f40a601c5bde59473f1ee2ed4a22841 [file] [log] [blame]
#include "NotifyWatch.hpp"
#include <sys/inotify.h>
#include <unistd.h>
#include <sdbusplus/async.hpp>
#include <array>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <utility>
namespace notify_watch
{
namespace fs = std::filesystem;
NotifyWatch::NotifyWatch(sdbusplus::async::context& ctx, const std::string& dir,
Callback_t callback) :
ctx(ctx), callback(std::move(callback))
{
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);
if (!fdioInstance)
{
throw std::system_error(errno, std::system_category(),
"Failed to create fdio");
}
}
NotifyWatch::~NotifyWatch()
{
if (-1 != fd)
{
if (-1 != wd)
{
inotify_rm_watch(fd, wd);
}
close(fd);
}
}
auto NotifyWatch::readNotifyAsync() -> sdbusplus::async::task<>
{
if (!fdioInstance)
{
co_return;
}
co_await fdioInstance->next();
alignas(inotify_event) std::array<uint8_t, 4096> buffer{};
auto bytes = read(fd, buffer.data(), buffer.size());
if (bytes < 0)
{
throw std::system_error(errno, std::system_category(),
"Failed to read notify event");
}
for (auto* iter = buffer.data(); iter < buffer.data() + bytes;)
{
// Bypassed clang tidy warning about reinterpret_cast as cast is being
// performed to avoid copying of buffer data.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
std::span<inotify_event> event{reinterpret_cast<inotify_event*>(iter),
1};
if (((event[0].mask & IN_CLOSE_WRITE) != 0U) &&
((event[0].mask & IN_ISDIR) == 0U))
{
if (callback)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
std::span<char> name{reinterpret_cast<char*>(event[0].name),
event[0].len};
co_await callback(std::string(name.begin(), name.end()));
}
}
iter += sizeof(inotify_event) + event[0].len;
}
if (!ctx.stop_requested())
{
ctx.spawn(readNotifyAsync());
}
}
} // namespace notify_watch