Add sd_event loop and callback handler

Change-Id: I032d1156be2b8082fdf347a60ec883a9bc0038ae
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 3fe7742..89fa2e4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,5 +7,5 @@
 				monitor.cpp \
 				mainapp.cpp
 
-phosphor_gpio_monitor_LDFLAGS = $(PHOSPHOR_LOGGING_LIBS)
-phosphor_gpio_monitor_CFLAGS = $(PHOSPHOR_LOGGING_CFLAGS)
+phosphor_gpio_monitor_LDFLAGS = $(SYSTEMD_LIBS) $(PHOSPHOR_LOGGING_LIBS)
+phosphor_gpio_monitor_CFLAGS = $(SYSTEMD_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
diff --git a/configure.ac b/configure.ac
index 40100c7..709ed05 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,6 +19,7 @@
 LT_INIT
 
 # Checks for modules
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221], ,[AC_MSG_ERROR([Could not find systemd...systemd developement package required])])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
 
 # Create configured output
diff --git a/mainapp.cpp b/mainapp.cpp
index 18bc4ca..6290366 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -16,9 +16,12 @@
 
 #include <iostream>
 #include <string>
+#include <systemd/sd-event.h>
+#include <phosphor-logging/log.hpp>
 #include "argument.hpp"
 #include "monitor.hpp"
 
+using namespace phosphor::logging;
 static void exitWithError(const char* err, char** argv)
 {
     phosphor::gpio::ArgumentParser::usage(argv);
@@ -59,9 +62,21 @@
     // on meeting a condition.
     auto target = (options)["target"];
 
-    // Create a GPIO monitor object and let it do all the rest
+    sd_event* event = nullptr;
+    auto r = sd_event_default(&event);
+    if (r < 0)
+    {
+        log<level::ERR>("Error creating a default sd_event handler");
+        return r;
+    }
+    phosphor::gpio::EventPtr eventP{event};
+    event = nullptr;
+
+    // Create a monitor object and let it do all the rest
     phosphor::gpio::Monitor monitor(path, std::stoi(key),
-                                    std::stoi(polarity),target);
+                                    std::stoi(polarity), target, eventP);
+    // Wait for events
+    sd_event_loop(eventP.get());
 
     return 0;
 }
diff --git a/monitor.cpp b/monitor.cpp
index 29d9d6d..5428c09 100644
--- a/monitor.cpp
+++ b/monitor.cpp
@@ -22,6 +22,8 @@
 namespace gpio
 {
 
+using namespace phosphor::logging;
+
 // Populate the file descriptor for passed in device
 int Monitor::openDevice()
 {
@@ -37,5 +39,30 @@
     return fd;
 }
 
+// Attaches the FD to event loop and registers the callback handler
+void Monitor::registerCallback()
+{
+    decltype(eventSource.get()) sourcePtr = nullptr;
+    auto r = sd_event_add_io(event.get(), &sourcePtr, (fd)(),
+                             EPOLLIN, callbackHandler, this);
+    eventSource.reset(sourcePtr);
+
+    if (r < 0)
+    {
+        log<level::ERR>("Failed to register callback handler",
+                entry("ERROR=%s", strerror(-r)));
+        throw std::runtime_error("Failed to register callback handler");
+    }
+}
+
+// Callback handler when there is an activity on the FD
+int Monitor::processEvents(sd_event_source* es, int fd,
+                           uint32_t revents, void* userData)
+{
+    // TODO. This calls into starting configured target
+    log<level::INFO>("Callback handler called");
+    return 0;
+}
+
 } // namespace gpio
 } // namespace phosphor
diff --git a/monitor.hpp b/monitor.hpp
index 9e3992d..1027714 100644
--- a/monitor.hpp
+++ b/monitor.hpp
@@ -3,11 +3,33 @@
 #include <unistd.h>
 #include <string>
 #include <linux/input.h>
+#include <systemd/sd-event.h>
 #include "file.hpp"
 namespace phosphor
 {
 namespace gpio
 {
+
+/* Need a custom deleter for freeing up sd_event */
+struct EventDeleter
+{
+    void operator()(sd_event* event) const
+    {
+        event = sd_event_unref(event);
+    }
+};
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+
+/* Need a custom deleter for freeing up sd_event_source */
+struct EventSourceDeleter
+{
+    void operator()(sd_event_source* eventSource) const
+    {
+        eventSource = sd_event_source_unref(eventSource);
+    }
+};
+using EventSourcePtr = std::unique_ptr<sd_event_source, EventSourceDeleter>;
+
 /** @class Monitor
  *  @brief Responsible for catching GPIO state change
  *  condition and taking actions
@@ -16,6 +38,7 @@
 {
     public:
         Monitor() = delete;
+        ~Monitor() = default;
         Monitor(const Monitor&) = delete;
         Monitor& operator=(const Monitor&) = delete;
         Monitor(Monitor&&) = delete;
@@ -28,20 +51,41 @@
          *  @param[in] polarity - GPIO assertion polarity to look for
          *  @param[in] target   - systemd unit to be started on GPIO
          *                        value change
+         *  @param[in] event    - sd_event handler
+         *  @param[in] handler  - IO callback handler. Defaults to one in this
+         *                        class
          */
         Monitor(const std::string& path,
                 decltype(input_event::code) key,
                 decltype(input_event::value) polarity,
-                const std::string& target)
+                const std::string& target,
+                EventPtr& event,
+                sd_event_io_handler_t handler = Monitor::processEvents)
             : path(path),
               key(key),
               polarity(polarity),
               target(target),
+              event(event),
+              callbackHandler(handler),
               fd(openDevice())
         {
-            // Nothing
+            // And register callback handler when FD has some data
+            registerCallback();
         }
 
+        /** @brief Callback handler when the FD has some activity on it
+         *
+         *  @param[in] es       - Populated event source
+         *  @param[in] fd       - Associated File descriptor
+         *  @param[in] revents  - Type of event
+         *  @param[in] userData - User data that was passed during registration
+         *
+         *  @return             - 0 or positive number on success and negative
+         *                        errno otherwise
+         */
+        static int processEvents(sd_event_source* es, int fd,
+                                 uint32_t revents, void* userData);
+
     private:
         /** @brief Absolute path of GPIO input device */
         const std::string& path;
@@ -55,11 +99,23 @@
         /** @brief Systemd unit to be started when the condition is met */
         const std::string& target;
 
-        /** @brief Manages File descriptor */
+        /** @brief Monitor to sd_event */
+        EventPtr& event;
+
+        /** @brief event source */
+        EventSourcePtr eventSource;
+
+        /** @brief Callback handler when the FD has some data */
+        sd_event_io_handler_t callbackHandler;
+
+        /** @brief File descriptor manager */
         FileDescriptor fd;
 
         /** @brief Opens the device and populates the descriptor */
         int openDevice();
+
+        /** @brief attaches FD to events and sets up callback handler */
+        void registerCallback();
 };
 
 } // namespace gpio