SetLan: Define the global network timer

As timer is being referred in the provider library
so it should be defined in per process(host-ipmid, net-ipmid)
context.

Defining the timer requires to pull the code of timer in this
repository so adding the timer class.

Change-Id: I4ea5f51fed03dd87d70868b0bc76ff1fb683931c
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 33735fd..1d1a370 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -54,7 +54,8 @@
 	sol_module.hpp \
 	sol_module.cpp \
 	settings.hpp \
-	settings.cpp
+	settings.cpp \
+	timer.cpp
 
 netipmid_CPPFLAGS = -DNET_IPMID_LIB_PATH=\"/usr/lib/net-ipmid/\"
 
diff --git a/main.cpp b/main.cpp
index ef4a349..c74b1c8 100644
--- a/main.cpp
+++ b/main.cpp
@@ -20,6 +20,7 @@
 #include "provider_registration.hpp"
 #include "socket_channel.hpp"
 #include "sol_module.hpp"
+#include "timer.hpp"
 
 // Tuple of Global Singletons
 session::Manager manager;
@@ -32,6 +33,9 @@
 
 sd_bus* bus = nullptr;
 
+// Global timer for network changes
+std::unique_ptr<phosphor::ipmi::Timer> networkTimer = nullptr;
+
 FILE* ipmidbus = nullptr;
 unsigned short g_sel_reserve = 0xFFFF;
 sd_bus_slot* ipmid_slot = nullptr;
diff --git a/timer.cpp b/timer.cpp
new file mode 100644
index 0000000..06965de
--- /dev/null
+++ b/timer.cpp
@@ -0,0 +1,110 @@
+#include <chrono>
+#include <phosphor-logging/log.hpp>
+#include "timer.hpp"
+namespace phosphor
+{
+namespace ipmi
+{
+
+using namespace phosphor::logging;
+
+// Initializes the timer object
+void Timer::initialize()
+{
+    // This can not be called more than once.
+    if (eventSource)
+    {
+        throw std::runtime_error("Timer already initialized");
+    }
+
+    // Add infinite expiration time
+    auto r = sd_event_add_time(timeEvent, &eventSource,
+                               CLOCK_MONOTONIC, // Time base
+                               UINT64_MAX,      // Expire time - way long time
+                               0,               // Use default event accuracy
+                               timeoutHandler,  // Callback handler on timeout
+                               this);           // User data
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to set initial expiration time value",
+                entry("ERROR=%s", strerror(-r)));
+
+        throw std::runtime_error("Timer initialization failed");
+    }
+
+    // Disable the timer for now
+    r = setTimer(SD_EVENT_OFF);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to disable timer",
+                entry("ERROR=%s", strerror(-r)));
+
+        throw std::runtime_error("Disabling the timer failed");
+    }
+    return;
+}
+
+/** @brief callback handler on timeout */
+int Timer::timeoutHandler(sd_event_source* eventSource,
+                          uint64_t usec, void* userData)
+{
+    auto timer = static_cast<Timer*>(userData);
+    timer->expired = true;
+
+    log<level::INFO>("Timer expired");
+    // Call optional user call back function if available
+    if(timer->userCallBack)
+    {
+        timer->userCallBack();
+    }
+
+    sd_event_source_set_enabled(eventSource, SD_EVENT_OFF);
+    return 0;
+}
+
+// Gets the time from steady_clock
+std::chrono::microseconds Timer::getTime()
+{
+    using namespace std::chrono;
+    auto usec = steady_clock::now().time_since_epoch();
+    return duration_cast<microseconds>(usec);
+}
+
+// Enables or disables the timer
+int Timer::setTimer(int action)
+{
+    return sd_event_source_set_enabled(eventSource, action);
+}
+
+// Sets the time and arms the timer
+int Timer::startTimer(std::chrono::microseconds timeValue)
+{
+    // Disable the timer
+    setTimer(SD_EVENT_OFF);
+    expired = false;
+
+    // Get the current MONOTONIC time and add the delta
+    auto expireTime = getTime() + timeValue;
+
+    // Set the time
+    auto r = sd_event_source_set_time(eventSource, expireTime.count());
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to set timer",
+                entry("ERROR=%s", strerror(-r)));
+        return r;
+    }
+
+    // A ONESHOT timer means that when the timer goes off,
+    // its moves to disabled state.
+    r = setTimer(SD_EVENT_ONESHOT);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to start timer",
+                entry("ERROR=%s", strerror(-r)));
+    }
+    return r;
+}
+
+} // namespace ipmi
+} // namespace phosphor
diff --git a/timer.hpp b/timer.hpp
new file mode 100644
index 0000000..e39d85c
--- /dev/null
+++ b/timer.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include <chrono>
+#include <functional>
+#include <systemd/sd-event.h>
+namespace phosphor
+{
+namespace ipmi
+{
+
+/** @class Timer
+ *  @brief Manages starting watchdog timers and handling timeouts
+ */
+class Timer
+{
+    public:
+        /** @brief Only need the default Timer */
+        Timer() = delete;
+        Timer(const Timer&) = delete;
+        Timer& operator=(const Timer&) = delete;
+        Timer(Timer&&) = delete;
+        Timer& operator=(Timer&&) = delete;
+
+        /** @brief Constructs timer object
+         *
+         *  @param[in] events - sd_event pointer
+         *  @param[in] funcCallBack - optional function callback for timer
+         *                            expirations
+         */
+        Timer(sd_event* events,
+              std::function<void()> userCallBack = nullptr)
+            : timeEvent(events), userCallBack(userCallBack)
+        {
+            // Initialize the timer
+            initialize();
+        }
+
+        ~Timer()
+        {
+            if (eventSource)
+            {
+                eventSource = sd_event_source_unref(eventSource);
+            }
+        }
+
+        inline auto isExpired() const
+        {
+            return expired;
+        }
+
+        /** @brief Starts the timer with specified expiration value.
+         *  input is an offset from the current steady_clock
+         */
+        int startTimer(std::chrono::microseconds usec);
+
+        /** @brief Enables / disables the timer */
+        int setTimer(int action);
+
+    private:
+        /** @brief the sd_event structure */
+        sd_event* timeEvent = nullptr;
+
+        /** @brief Source of events */
+        sd_event_source* eventSource = nullptr;
+
+        /** @brief Returns if the associated timer is expired
+         *
+         *  This is set to true when the timeoutHandler is called into
+         */
+        bool expired = true;
+
+        /** @brief Initializes the timer object with infinite
+         *         expiration time and sets up the callback handler
+         *
+         *  @return None.
+         *
+         *  @error std::runtime exception thrown
+         */
+        void initialize();
+
+        /** @brief Callback function when timer goes off
+         *
+         *  On getting the signal, initiate the hard power off request
+         *
+         *  @param[in] eventSource - Source of the event
+         *  @param[in] usec        - time in micro seconds
+         *  @param[in] userData    - User data pointer
+         *
+         */
+        static int timeoutHandler(sd_event_source* eventSource,
+                                  uint64_t usec, void* userData);
+
+        /** @brief Gets the current time from steady clock */
+        static std::chrono::microseconds getTime();
+
+        /** @brief Optional function to call on timer expiration */
+        std::function<void()> userCallBack;
+};
+
+} // namespace ipmi
+} // namespace phosphor