Add inotify watch support
Added code to register for inotify events on the needed
path and the user callback on events
Change-Id: I90529f8e96fcbecfe0a1b943c3c435dab79222c3
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/watch.cpp b/watch.cpp
new file mode 100644
index 0000000..3cacefd
--- /dev/null
+++ b/watch.cpp
@@ -0,0 +1,139 @@
+#include "watch.hpp"
+
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+
+#include <sys/inotify.h>
+#include <errno.h>
+
+namespace phosphor
+{
+namespace network
+{
+namespace inotify
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Watch::Watch(phosphor::network::EventPtr& eventPtr,
+ fs::path path,
+ UserCallBack userFunc,
+ int flags,
+ uint32_t mask,
+ uint32_t events) :
+ path(path),
+ userFunc(userFunc),
+ flags(flags),
+ mask(mask),
+ events(events),
+ fd(inotifyInit())
+{
+ // Check if watch file exists
+ // This is supposed to be there always
+ if (!fs::is_regular_file(path))
+ {
+ log<level::ERR>("Watch file doesn't exist",
+ entry("FILE=%s", path.c_str()));
+ elog<InternalFailure>();
+ }
+
+ auto dirPath = path.parent_path();
+ wd = inotify_add_watch(fd(), dirPath.c_str(), mask);
+ if (wd == -1)
+ {
+ log<level::ERR>("Error from inotify_add_watch",
+ entry("ERRNO=%d", errno));
+ elog<InternalFailure>();
+ }
+
+ // Register the fd with sd_event infrastructure and setup a
+ // callback handler to be invoked on events
+ auto rc = sd_event_add_io(eventPtr.get(),
+ nullptr,
+ fd(),
+ events,
+ Watch::processEvents,
+ this);
+ if (rc < 0)
+ {
+ // Failed to add to event loop
+ log<level::ERR>("Error registering with sd_event_add_io",
+ entry("RC=%d", rc));
+ elog<InternalFailure>();
+ }
+}
+
+int Watch::inotifyInit()
+{
+ auto fd = inotify_init1(flags);
+ if (fd < 0)
+ {
+ log<level::ERR>("Error from inotify_init1",
+ entry("ERRNO=%d", errno));
+ elog<InternalFailure>();
+ }
+ return fd;
+}
+
+int Watch::processEvents(sd_event_source* eventSource,
+ int fd,
+ uint32_t retEvents,
+ void* userData)
+{
+ auto watch = static_cast<Watch*>(userData);
+
+ // Not the ones we are interested in
+ if (!(retEvents & watch->events))
+ {
+ return 0;
+ }
+
+ // Buffer size to be used while reading events.
+ // per inotify(7), below number should be fine for reading
+ // at-least one event
+ constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
+ uint8_t eventData[maxBytes]{};
+
+ auto bytes = read(fd, eventData, maxBytes);
+ if (bytes <= 0)
+ {
+ // Failed to read inotify event data
+ // Report error and return
+ log<level::ERR>("Error reading inotify event",
+ entry("ERRNO=%d", errno));
+ report<InternalFailure>();
+ return 0;
+ }
+
+ auto offset = 0;
+ auto stateFile = watch->path.filename();
+ while (offset < bytes)
+ {
+ auto event = reinterpret_cast<inotify_event*>(&eventData[offset]);
+
+ // Filter the interesting ones
+ auto mask = event->mask & watch->mask;
+ if (mask)
+ {
+ if((event->len > 0) &&
+ (strstr(event->name, stateFile.string().c_str())))
+ {
+ if (watch->userFunc)
+ {
+ watch->userFunc(watch->path);
+ }
+ // Found the event of interest
+ break;
+ }
+ }
+ // Move past this entry
+ offset += offsetof(inotify_event, name) + event->len;
+ }
+ return 0;
+}
+
+} // namespace inotify
+} // namespace network
+} // namespace phosphor