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