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