xref: /openbmc/phosphor-debug-collector/watch.cpp (revision 7c7ba55d66b4e5c3529715b97f4455f46272a2be)
1 #include "watch.hpp"
2 
3 #include "xyz/openbmc_project/Common/error.hpp"
4 
5 #include <phosphor-logging/elog-errors.hpp>
6 #include <phosphor-logging/lg2.hpp>
7 
8 #include <span>
9 
10 namespace phosphor
11 {
12 namespace dump
13 {
14 namespace inotify
15 {
16 
17 using namespace std::string_literals;
18 using namespace phosphor::logging;
19 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
20 
21 Watch::~Watch()
22 {
23     if ((fd() >= 0) && (wd >= 0))
24     {
25         inotify_rm_watch(fd(), wd);
26     }
27 }
28 
29 Watch::Watch(const EventPtr& eventObj, const int flags, const uint32_t mask,
30              const uint32_t events, const std::filesystem::path& path,
31              UserType userFunc) :
32     flags(flags), mask(mask), events(events), path(path), fd(inotifyInit()),
33     userFunc(userFunc)
34 {
35     // Check if watch DIR exists.
36     if (!std::filesystem::is_directory(path))
37     {
38         lg2::error("Watch directory doesn't exist, DIR: {DIRECTORY}",
39                    "DIRECTORY", path);
40         elog<InternalFailure>();
41     }
42 
43     wd = inotify_add_watch(fd(), path.c_str(), mask);
44     if (-1 == wd)
45     {
46         auto error = errno;
47         lg2::error(
48             "Error occurred during the inotify_add_watch call, errno: {ERRNO}",
49             "ERRNO", error);
50         elog<InternalFailure>();
51     }
52 
53     auto rc =
54         sd_event_add_io(eventObj.get(), nullptr, fd(), events, callback, this);
55     if (0 > rc)
56     {
57         // Failed to add to event loop
58         lg2::error("Error occurred during the sd_event_add_io call, rc: {RC}",
59                    "RC", rc);
60         elog<InternalFailure>();
61     }
62 }
63 
64 int Watch::inotifyInit()
65 {
66     auto fd = inotify_init1(flags);
67 
68     if (-1 == fd)
69     {
70         auto error = errno;
71         lg2::error("Error occurred during the inotify_init1, errno: {ERRNO}",
72                    "ERRNO", error);
73         elog<InternalFailure>();
74     }
75 
76     return fd;
77 }
78 
79 int Watch::callback(sd_event_source*, int fd, uint32_t revents, void* userdata)
80 {
81     auto userData = static_cast<Watch*>(userdata);
82 
83     if ((revents & userData->events) == 0U)
84     {
85         return 0;
86     }
87 
88     // Maximum inotify events supported in the buffer
89     constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
90     uint8_t buffer[maxBytes];
91 
92     std::span<char> bufferSpan(reinterpret_cast<char*>(buffer), maxBytes);
93     auto bytes = read(fd, bufferSpan.data(), bufferSpan.size());
94     if (0 > bytes)
95     {
96         // Failed to read inotify event
97         // Report error and return
98         auto error = errno;
99         lg2::error("Error occurred during the read, errno: {ERRNO}", "ERRNO",
100                    error);
101         report<InternalFailure>();
102         return 0;
103     }
104 
105     auto offset = 0;
106 
107     UserMap userMap;
108 
109     while (offset < bytes)
110     {
111         auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
112         auto mask = event->mask & userData->mask;
113 
114         if (mask != 0U)
115         {
116             userMap.emplace((userData->path / event->name), mask);
117         }
118 
119         offset += offsetof(inotify_event, name) + event->len;
120     }
121 
122     // Call user call back function in case valid data in the map
123     if (!userMap.empty())
124     {
125         userData->userFunc(userMap);
126     }
127 
128     return 0;
129 }
130 
131 } // namespace inotify
132 } // namespace dump
133 } // namespace phosphor
134