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