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