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