Add support for generic inotify based directory watch.
Watch a directory for the changes based on user configuration
and then report changes to the user.
Change-Id: I9f53d3135dd4bff6187840c0c53d2a64509808cd
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/watch.cpp b/watch.cpp
new file mode 100644
index 0000000..bc7fb1f
--- /dev/null
+++ b/watch.cpp
@@ -0,0 +1,140 @@
+#include <phosphor-logging/elog-errors.hpp>
+
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "watch.hpp"
+
+namespace phosphor
+{
+namespace dump
+{
+namespace inotify
+{
+
+using namespace std::string_literals;
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Watch::~Watch()
+{
+ if ((fd() >= 0) && (wd >= 0))
+ {
+ inotify_rm_watch(fd(), wd);
+ }
+}
+
+Watch::Watch(const EventPtr& eventObj,
+ const int flags,
+ const uint32_t mask,
+ const uint32_t events,
+ const fs::path& path,
+ UserType userFunc):
+ flags(flags),
+ mask(mask),
+ events(events),
+ path(path),
+ fd(inotifyInit()),
+ userFunc(userFunc)
+{
+ // Check if watch DIR exists.
+ if (!fs::is_directory(path))
+ {
+ log<level::ERR>("Watch directory doesn't exist",
+ entry("dir=%s", path.c_str()));
+ elog<InternalFailure>();
+ }
+
+ wd = inotify_add_watch(fd(), path.c_str(), mask);
+ if (-1 == wd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_add_watch call",
+ entry("ERRNO=%d", error));
+ elog<InternalFailure>();
+ }
+
+ auto rc = sd_event_add_io(eventObj.get(),
+ nullptr,
+ fd(),
+ events,
+ callback,
+ this);
+ if (0 > rc)
+ {
+ // Failed to add to event loop
+ log<level::ERR>("Error occurred during the sd_event_add_io call",
+ entry("rc=%d", rc));
+ elog<InternalFailure>();
+ }
+}
+
+int Watch::inotifyInit()
+{
+ auto fd = inotify_init1(flags);
+
+ if (-1 == fd)
+ {
+ auto error = errno;
+ log<level::ERR>("Error occurred during the inotify_init1",
+ entry("ERRNO=%d", error));
+ elog<InternalFailure>();
+ }
+
+ return fd;
+}
+
+int Watch::callback(sd_event_source* s,
+ int fd,
+ uint32_t revents,
+ void* userdata)
+{
+ if (!(revents & static_cast<Watch*>(userdata)->events))
+ {
+ return 0;
+ }
+
+ //Maximum inotify events supported in the buffer
+ constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
+ uint8_t buffer[maxBytes];
+
+ auto bytes = read(fd, buffer, maxBytes);
+ if (0 > bytes)
+ {
+ //Failed to read inotify event
+ //Report error and return
+ auto error = errno;
+ log<level::ERR>("Error occurred during the read",
+ entry("ERRNO=%d", error));
+ report<InternalFailure>();
+ return 0;
+ }
+
+ auto offset = 0;
+
+ UserMap userMap;
+
+ while (offset < bytes)
+ {
+ auto event = reinterpret_cast<inotify_event*>(&buffer[offset]);
+ auto mask = event->mask & static_cast<Watch*>(userdata)->mask;
+
+ if (mask && !(event->mask & IN_ISDIR))
+ {
+ userMap.emplace(
+ (static_cast<Watch*>(userdata)->path / event->name), mask);
+ }
+
+ offset += offsetof(inotify_event, name) + event->len;
+ }
+
+ //Call user call back function incase valid data in the map
+ if (!userMap.empty())
+ {
+ static_cast<Watch*>(userdata)->userFunc(userMap);
+ }
+
+ return 0;
+}
+
+} // namespace inotify
+} // namespace dump
+} // namespace phosphor