xref: /openbmc/dbus-sensors/src/NotifyWatch.cpp (revision 0d2f96f1e95a6a37f00a3c458c8562d6af92e4a4)
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