blob: f795464a92ef38ae1d56fc4d355abed5f1b6445a [file] [log] [blame]
Jagpal Singh Gillca8c7e92024-11-02 16:51:48 -07001#include "NotifyWatch.hpp"
2
3#include <sys/inotify.h>
4#include <unistd.h>
5
6#include <sdbusplus/async.hpp>
7
8#include <array>
9#include <cerrno>
10#include <cstdint>
Jagpal Singh Gillca8c7e92024-11-02 16:51:48 -070011#include <filesystem>
12#include <memory>
13#include <span>
14#include <string>
15#include <system_error>
16#include <utility>
17
18namespace notify_watch
19{
20
21namespace fs = std::filesystem;
22
23NotifyWatch::NotifyWatch(sdbusplus::async::context& ctx, const std::string& dir,
24 Callback_t callback) :
25 ctx(ctx), callback(std::move(callback))
26{
27 std::error_code ec = {};
28
29 fs::path dirPath(dir);
30 if (!fs::create_directories(dirPath, ec))
31 {
32 if (ec)
33 {
34 throw std::system_error(ec, "Failed to create directory " + dir);
35 }
36 }
37
38 fd = inotify_init1(IN_NONBLOCK);
39 if (-1 == fd)
40 {
41 throw std::system_error(errno, std::system_category(),
42 "inotify_init1 failed");
43 }
44
45 wd = inotify_add_watch(fd, dir.c_str(), IN_CLOSE_WRITE);
46 if (-1 == wd)
47 {
48 close(fd);
49 throw std::system_error(errno, std::system_category(),
50 "inotify_add_watch failed");
51 }
52
53 fdioInstance = std::make_unique<sdbusplus::async::fdio>(ctx, fd);
54 if (!fdioInstance)
55 {
56 throw std::system_error(errno, std::system_category(),
57 "Failed to create fdio");
58 }
59}
60
61NotifyWatch::~NotifyWatch()
62{
63 if (-1 != fd)
64 {
65 if (-1 != wd)
66 {
67 inotify_rm_watch(fd, wd);
68 }
69 close(fd);
70 }
71}
72
73auto NotifyWatch::readNotifyAsync() -> sdbusplus::async::task<>
74{
75 if (!fdioInstance)
76 {
77 co_return;
78 }
79 co_await fdioInstance->next();
80
81 alignas(inotify_event) std::array<uint8_t, 4096> buffer{};
82
83 auto bytes = read(fd, buffer.data(), buffer.size());
84 if (bytes < 0)
85 {
86 throw std::system_error(errno, std::system_category(),
87 "Failed to read notify event");
88 }
89
90 for (auto* iter = buffer.data(); iter < buffer.data() + bytes;)
91 {
92 // Bypassed clang tidy warning about reinterpret_cast as cast is being
93 // performed to avoid copying of buffer data.
94 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
95 std::span<inotify_event> event{reinterpret_cast<inotify_event*>(iter),
96 1};
97 if (((event[0].mask & IN_CLOSE_WRITE) != 0U) &&
98 ((event[0].mask & IN_ISDIR) == 0U))
99 {
100 if (callback)
101 {
102 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
103 std::span<char> name{reinterpret_cast<char*>(event[0].name),
104 event[0].len};
105 co_await callback(std::string(name.begin(), name.end()));
106 }
107 }
108 iter += sizeof(inotify_event) + event[0].len;
109 }
110
111 if (!ctx.stop_requested())
112 {
113 ctx.spawn(readNotifyAsync());
114 }
115}
116
117} // namespace notify_watch