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 <filesystem>
12 #include <memory>
13 #include <span>
14 #include <string>
15 #include <system_error>
16 #include <utility>
17
18 namespace notify_watch
19 {
20
21 namespace fs = std::filesystem;
22
NotifyWatch(sdbusplus::async::context & ctx,const std::string & dir,Callback_t callback)23 NotifyWatch::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
~NotifyWatch()61 NotifyWatch::~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
readNotifyAsync()73 auto 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
118