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