Implement Watchdog interface

This commit gives concrete implementation of the interface.

Change-Id: I3951c5811c8e6cff87c87842a1e3c538463bfde7
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 888f8cf..cd58d99 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,13 +1,20 @@
 sbin_PROGRAMS = phosphor-watchdog
 
-noinst_HEADERS = timer.hpp
+noinst_HEADERS = timer.hpp \
+                 watchdog.hpp
 
-phosphor_watchdog_SOURCES = \
+phosphor_watchdog_SOURCES =  \
                 argument.cpp \
-                timer.cpp \
+                timer.cpp    \
+                watchdog.cpp \
                 mainapp.cpp
 
 phosphor_watchdog_LDFLAGS = $(SYSTEMD_LIBS) \
-                            ${PHOSPHOR_LOGGING_LIBS}
+                            ${PHOSPHOR_LOGGING_LIBS} \
+                            ${SDBUSPLUS_LIBS} \
+                            ${PHOSPHOR_DBUS_INTERFACES_LIBS}
+
 phosphor_watchdog_CXXFLAGS = $(SYSTEMD_CFLAGS)\
-                             ${PHOSPHOR_LOGGING_CFLAGS}
+                             ${PHOSPHOR_LOGGING_CFLAGS} \
+                             ${SDBUSPLUS_CFLAGS} \
+                             ${PHOSPHOR_DBUS_INTERFACES_CFLAGS}
diff --git a/configure.ac b/configure.ac
index 2f06b7b..878b30d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,8 +18,11 @@
 # For linking
 LT_INIT
 
+# Check for needed modules
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221], [], [AC_MSG_ERROR(["systemd required and not found"])])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],, [AC_MSG_ERROR([Could not find sdbusplus...openbmc/sdbusplus package required])])
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],, [AC_MSG_ERROR([Could not find phosphor-dbus-interfaces...openbmc/phosphor-dbus-interfaces package required])])
 
 # Create configured output
 AC_CONFIG_FILES([Makefile])
diff --git a/mainapp.cpp b/mainapp.cpp
index 4ab811c..b9e6121 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -17,7 +17,7 @@
 #include <iostream>
 #include <phosphor-logging/log.hpp>
 #include "argument.hpp"
-#include "timer.hpp"
+#include "watchdog.hpp"
 
 static void exitWithError(const char* err, char** argv)
 {
@@ -62,11 +62,23 @@
     phosphor::watchdog::EventPtr eventP{event};
     event = nullptr;
 
-    // TODO: Creating the timer object would be inside watchdog implementation.
-    //       Putting this here for completion of this piece
-    phosphor::watchdog::Timer timer(eventP);
+    // Get a handle to system dbus.
+    auto bus = sdbusplus::bus::new_default();
 
-    while(!timer.expired())
+    // Add systemd object manager.
+    sdbusplus::server::manager::manager(bus, path.c_str());
+
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(eventP.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    // Create a watchdog object
+    phosphor::watchdog::Watchdog watchdog(bus, path.c_str(), eventP);
+
+    // Claim the bus
+    bus.request_name(service.c_str());
+
+    // Wait until the timer has expired
+    while(!watchdog.timerExpired())
     {
         // -1 denotes wait for ever
         r = sd_event_run(eventP.get(), (uint64_t)-1);
diff --git a/watchdog.cpp b/watchdog.cpp
new file mode 100644
index 0000000..c9e9919
--- /dev/null
+++ b/watchdog.cpp
@@ -0,0 +1,96 @@
+#include <chrono>
+#include <phosphor-logging/log.hpp>
+#include "watchdog.hpp"
+
+namespace phosphor
+{
+namespace watchdog
+{
+using namespace std::chrono;
+using namespace std::chrono_literals;
+using namespace phosphor::logging;
+
+// Enable or disable watchdog
+bool Watchdog::enabled(bool value)
+{
+    if (WatchdogInherits::enabled() != value)
+    {
+        if (value)
+        {
+            // Start ONESHOT timer. Timer handles all in usec
+            auto usec = duration_cast<microseconds>(
+                                      milliseconds(this->interval()));
+            // Update new expiration
+            timer.start(usec);
+
+            // Enable timer
+            timer.setEnabled<std::true_type>();
+
+            log<level::INFO>("watchdog: enabled and started",
+                             entry("INTERVAL=%llu", this->interval()));
+        }
+        else
+        {
+            timer.setEnabled<std::false_type>();
+            log<level::INFO>("watchdog: disabled");
+        }
+    }
+    return WatchdogInherits::enabled(value);
+}
+
+// Get the remaining time before timer expires.
+// If the timer is disabled, returns 0
+uint64_t Watchdog::timeRemaining() const
+{
+    uint64_t timeRemain = 0;
+
+    if (WatchdogInherits::enabled())
+    {
+        // timer may have already expired and disabled
+        if (timer.getEnabled() != SD_EVENT_OFF)
+        {
+            // the one-shot timer does not expire yet
+            auto expiry = duration_cast<milliseconds>(
+                                   timer.getRemaining());
+
+            // convert to msec per interface expectation.
+            auto timeNow = duration_cast<milliseconds>(
+                                    Timer::getCurrentTime());
+
+            // Its possible that timer may have expired by now.
+            // So need to cross verify.
+            timeRemain = (expiry > timeNow) ?
+                            (expiry - timeNow).count() : 0;
+        }
+    }
+    return timeRemain;
+}
+
+// Reset the timer to a new expiration value
+uint64_t Watchdog::timeRemaining(uint64_t value)
+{
+    if (WatchdogInherits::enabled())
+    {
+        // Disable the timer
+        timer.setEnabled<std::false_type>();
+
+        // Timer handles all in microseconds and hence converting
+        auto usec = duration_cast<microseconds>(
+                                  milliseconds(value));
+        // Update new expiration
+        timer.start(usec);
+
+        // Enable the timer.
+        timer.setEnabled<std::true_type>();
+
+        log<level::INFO>("watchdog: reset timer",
+                         entry("VALUE=%llu", value));
+
+        // Update Base class data.
+        return WatchdogInherits::timeRemaining(value);
+    }
+    return 0;
+}
+
+} // namespace watchdog
+} // namepsace phosphor
diff --git a/watchdog.hpp b/watchdog.hpp
new file mode 100644
index 0000000..47484af
--- /dev/null
+++ b/watchdog.hpp
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <systemd/sd-event.h>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/State/Watchdog/server.hpp>
+#include "timer.hpp"
+
+namespace phosphor
+{
+namespace watchdog
+{
+using WatchdogInherits = sdbusplus::server::object::object<
+        sdbusplus::xyz::openbmc_project::State::server::Watchdog>;
+
+/** @class Watchdog
+ *  @brief OpenBMC watchdog implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.State.Watchdog DBus API.
+ */
+class Watchdog : public WatchdogInherits
+{
+    public:
+        Watchdog() = delete;
+        ~Watchdog() = default;
+        Watchdog(const Watchdog&) = delete;
+        Watchdog& operator=(const Watchdog&) = delete;
+        Watchdog(Watchdog&&) = delete;
+        Watchdog& operator=(Watchdog &&) = delete;
+
+        /** @brief Constructs the Watchdog object
+         *
+         *  @param[in] bus     - DBus bus to attach to
+         *  @param[in] objPath - Object path to attach to
+         *  @param[in] event   - reference to sd_event unique pointer
+         */
+        Watchdog(sdbusplus::bus::bus& bus,
+                const char* objPath,
+                EventPtr& event) :
+            WatchdogInherits(bus, objPath),
+            bus(bus),
+            timer(event)
+        {
+            // Nothing
+        }
+
+        /** @brief Enable or disable watchdog
+         *         If a watchdog state is changed from disable to enable,
+         *         the watchdog timer is set with the default expiration
+         *         interval and it starts counting down.
+         *         If a watchdog is already enabled, setting @value to true
+         *         has no effect.
+         *
+         *  @param[in] value - 'true' to enable. 'false' to disable
+         *
+         *  @return : applied value if success, previous value otherwise
+         */
+        bool enabled(bool value) override;
+
+        /** @brief Gets the remaining time before watchdog expires.
+         *
+         *  @return 0 if watchdog is disabled or expired.
+         *          Remaining time in milliseconds otherwise.
+         */
+        uint64_t timeRemaining() const override;
+
+        /** @brief Reset timer to expire after new timeout in milliseconds.
+         *
+         *  @param[in] value - the time in miliseconds after which
+         *                     the watchdog will expire
+         *
+         *  @return: updated timeout value if watchdog is enabled.
+         *           0 otherwise.
+         */
+        uint64_t timeRemaining(uint64_t value) override;
+
+        /** @brief Tells if the referenced timer is expired or not */
+        inline auto timerExpired() const
+        {
+            return timer.expired();
+        }
+
+    private:
+        /** @brief sdbusplus handle */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief Contained timer object */
+        Timer timer;
+};
+
+} // namespace watchdog
+} // namespace phosphor