Add timer skeleton

This patchset creates the timer infrastructure that is then
used by soft power off object on user requests.

Change-Id: I6f7a5c161999fda89471f453c24725efddac65b9
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/softoff/Makefile.am b/softoff/Makefile.am
index 66501a5..aafce74 100644
--- a/softoff/Makefile.am
+++ b/softoff/Makefile.am
@@ -4,6 +4,7 @@
 
 phosphor_softpoweroff_SOURCES = \
                     softoff.cpp \
+                    timer.cpp   \
                     mainapp.cpp \
                     xyz/openbmc_project/Ipmi/Internal/SoftPowerOff/server.cpp
 
diff --git a/softoff/mainapp.cpp b/softoff/mainapp.cpp
index 38b6984..f5fbf18 100644
--- a/softoff/mainapp.cpp
+++ b/softoff/mainapp.cpp
@@ -13,32 +13,60 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <systemd/sd-event.h>
 #include <phosphor-logging/log.hpp>
 #include "softoff.hpp"
 #include "config.h"
+#include "timer.hpp"
 
 using namespace phosphor::logging;
 
 int main(int argc, char** argv)
 {
+    // systemd event handler
+    sd_event* events = nullptr;
+
     // Get a handle to system dbus.
     auto bus = sdbusplus::bus::new_default();
 
     // Add systemd object manager.
     sdbusplus::server::manager::manager(bus, SOFTOFF_OBJPATH);
 
+    // sd_event object
+    auto r = sd_event_default(&events);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to create sd_event handler",
+                entry("ERROR=%s", strerror(-r)));
+        return -1;
+    }
+
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(events, SD_EVENT_PRIORITY_NORMAL);
+
     // Create the SoftPowerOff object.
-    phosphor::ipmi::SoftPowerOff object(bus, SOFTOFF_OBJPATH);
+    phosphor::ipmi::SoftPowerOff powerObj(bus, events, SOFTOFF_OBJPATH);
 
     /** @brief Claim the bus */
     bus.request_name(SOFTOFF_BUSNAME);
 
-    /** @brief Wait for client requests */
-    while(true)
+    /** @brief Wait for client requests until this application has processed
+     *         at least one successful SoftPowerOff
+     */
+    while(!powerObj.isCompleted() && !powerObj.isTimerExpired())
     {
-        // Handle dbus message / signals discarding unhandled
-        bus.process_discard();
-        bus.wait();
+        // -1 denotes wait for ever
+        r = sd_event_run(events, (uint64_t)-1);
+        if (r < 0)
+        {
+            log<level::ERR>("Failure in processing request",
+                    entry("ERROR=%s", strerror(-r)));
+            break;
+        }
     }
+
+    // Cleanup the event handler
+    events = sd_event_unref(events);
+
     return 0;
 }
diff --git a/softoff/softoff.cpp b/softoff/softoff.cpp
index bd6a72d..11298ab 100644
--- a/softoff/softoff.cpp
+++ b/softoff/softoff.cpp
@@ -19,11 +19,6 @@
 namespace ipmi
 {
 
-// Need this to send SMS_ATTN
-constexpr auto HOST_IPMI_BUS  = "org.openbmc.HostIpmi";
-constexpr auto HOST_IPMI_OBJ  = "/org/openbmc/HostIpmi/1";
-constexpr auto HOST_IPMI_INTF = "org.openbmc.HostIpmi";
-
 /** @brief Send the SMS_ATN to host if value is set */
 void SoftPowerOff::sendSMSAttn()
 {
@@ -38,5 +33,6 @@
 
     return;
 }
+
 } // namespace ipmi
 } // namespace phosphor
diff --git a/softoff/softoff.hpp b/softoff/softoff.hpp
index dcb4b18..06f3861 100644
--- a/softoff/softoff.hpp
+++ b/softoff/softoff.hpp
@@ -3,6 +3,7 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/object.hpp>
 #include <xyz/openbmc_project/Ipmi/Internal/SoftPowerOff/server.hpp>
+#include "timer.hpp"
 namespace phosphor
 {
 namespace ipmi
@@ -19,15 +20,22 @@
     public:
         /** @brief Constructs SoftPowerOff object.
          *
-         *  @param[in] bus      - system dbus handler
-         *  @param[in] objPath  - The Dbus path that hosts SoftPowerOff function
+         *  @param[in] bus       - system dbus handler
+         *  @param[in] event     - sd_event handler
+         *  @param[in] objPath   - The Dbus path hosting SoftPowerOff function
          */
         SoftPowerOff(sdbusplus::bus::bus& bus,
+                     sd_event* event,
                      const char* objPath) :
             sdbusplus::server::object::object<
-                Base::SoftPowerOff>(bus, objPath),
-                bus(bus)
+                Base::SoftPowerOff>(bus, objPath, false),
+                bus(bus),
+                timer(event)
         {
+            // Need to announce since we may get the response
+            // very quickly on SMS_ATN
+            emit_object_added();
+
             // The whole purpose of this application is to send SMS_ATTN
             // and watch for the soft power off to go through. We need the
             // interface added signal emitted before we send SMS_ATN just to
@@ -35,12 +43,43 @@
             sendSMSAttn();
         }
 
+        /** @brief Tells if the objective of this application is completed */
+        inline auto isCompleted()
+        {
+            return completed;
+        }
+
+        /** @brief Tells if the referenced timer is expired or not */
+        inline auto isTimerExpired()
+        {
+            return timer.isExpired();
+        }
+
     private:
+        // Need this to send SMS_ATTN
+        // TODO : Switch over to using mapper service in a different patch
+        static constexpr auto HOST_IPMI_BUS  = "org.openbmc.HostIpmi";
+        static constexpr auto HOST_IPMI_OBJ  = "/org/openbmc/HostIpmi/1";
+        static constexpr auto HOST_IPMI_INTF = "org.openbmc.HostIpmi";
+
+        /* @brief sdbusplus handle */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief Reference to Timer object */
+        Timer timer;
+
+        /** @brief Marks the end of life of this application.
+         *
+         *  This is set to true if host gives appropriate responses
+         *  for the sequence of commands.
+         */
+        bool completed = false;
+
         /** @brief Sends SMS_ATN to host to initiate soft power off process.
          *
-         *  After sending the SMS_ATN, starts a watchdog timer for 30
+         *  After sending the SMS_ATN, starts a timer for 30
          *  seconds and expects a initial response from the host.
-         *  After receiving the initial response, starts another watchdog
+         *  After receiving the initial response, starts another
          *  timer for 30 minutes to let host do a clean shutdown of
          *  partitions. When the second response is received from the
          *  host, it indicates that BMC can do a power off.
@@ -51,9 +90,6 @@
          *            being thrown
          */
         void sendSMSAttn();
-
-        /* @brief sdbusplus handle */
-        sdbusplus::bus::bus& bus;
 };
 } // namespace ipmi
 } // namespace phosphor
diff --git a/softoff/timer.cpp b/softoff/timer.cpp
new file mode 100644
index 0000000..9e722f1
--- /dev/null
+++ b/softoff/timer.cpp
@@ -0,0 +1,58 @@
+#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 enough 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 = sd_event_source_set_enabled(eventSource, SD_EVENT_OFF);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to disable timer",
+                entry("ERROR=%s", strerror(-r)));
+
+        throw std::runtime_error("Setting initial timer value 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");
+    return 0;
+}
+
+} // namespace ipmi
+} // namespace phosphor
diff --git a/softoff/timer.hpp b/softoff/timer.hpp
new file mode 100644
index 0000000..7c3bac2
--- /dev/null
+++ b/softoff/timer.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#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
+         */
+        Timer(sd_event* events)
+            : timeEvent(events)
+        {
+            // Initialize the timer
+            initialize();
+        }
+
+        ~Timer()
+        {
+            if (eventSource)
+            {
+                eventSource = sd_event_source_unref(eventSource);
+            }
+        }
+
+        inline auto isExpired()
+        {
+            return expired;
+        }
+
+    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 = false;
+
+        /** @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);
+};
+
+} // namespace ipmi
+} // namespace phosphor