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