Add timeout support to host control

On timeout, send error signal for all commands within the queue

Change-Id: Ic995fd4b057bd83f121a3deec405a26e0991e9a2
Signed-off-by: Andrew Geissler <andrewg@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 4daadcd..2a51d2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,7 +45,8 @@
 libsysintfcmds_la_SOURCES = \
 	systemintfcmds.cpp \
 	host-interface.cpp \
-	utils.cpp
+	utils.cpp \
+	timer.cpp
 libsysintfcmds_la_LDFLAGS = $(SYSTEMD_LIBS) \
                             $(libmapper_LIBS) \
                             $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
diff --git a/host-interface.cpp b/host-interface.cpp
index 160c127..be2bd99 100644
--- a/host-interface.cpp
+++ b/host-interface.cpp
@@ -1,6 +1,9 @@
+#include <chrono>
 #include <phosphor-logging/log.hpp>
 #include <utils.hpp>
+#include <config.h>
 #include "host-interface.hpp"
+#include "elog-errors.hpp"
 
 namespace phosphor
 {
@@ -16,10 +19,45 @@
 // When you see base:: you know we're referencing our base class
 namespace base = sdbusplus::xyz::openbmc_project::Control::server;
 
-// TODO - Add timeout function?
-//          - If host does not respond to SMS, need to signal a failure
-//      - Flush queue on power off?  - Timeout would do this for us for free
-//      - Ignore requests when host state not running? - Timeout handles too
+base::Host::Command Host::getNextCommand()
+{
+    // Stop the timer
+    auto r = timer.setTimer(SD_EVENT_OFF);
+    if (r < 0)
+    {
+        log<level::ERR>("Failure to STOP the timer",
+                entry("ERROR=%s", strerror(-r)));
+    }
+
+    if(this->workQueue.empty())
+    {
+        log<level::ERR>("Control Host work queue is empty!");
+        elog<xyz::openbmc_project::Control::Internal::Host::QueueEmpty>();
+    }
+
+    // Pop the processed entry off the queue
+    Command command = this->workQueue.front();
+    this->workQueue.pop();
+
+    // Issue command complete signal
+    this->commandComplete(command, Result::Success);
+
+    // Check for another entry in the queue and kick it off
+    this->checkQueue();
+    return command;
+}
+
+void Host::hostTimeout()
+{
+    log<level::ERR>("Host control timeout hit!");
+    // Dequeue all entries and send fail signal
+    while(!this->workQueue.empty())
+    {
+        auto command = this->workQueue.front();
+        this->workQueue.pop();
+        this->commandComplete(command,Result::Failure);
+    }
+}
 
 void Host::checkQueue()
 {
@@ -30,7 +68,17 @@
         std::string IPMI_PATH("/org/openbmc/HostIpmi/1");
         std::string IPMI_INTERFACE("org.openbmc.HostIpmi");
 
-        auto host = ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH);
+        auto host = ::ipmi::getService(this->bus,IPMI_INTERFACE,IPMI_PATH);
+
+        // Start the timer for this transaction
+        auto time = std::chrono::duration_cast<std::chrono::microseconds>(
+                        std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS));
+        auto r = timer.startTimer(time);
+        if (r < 0)
+        {
+            log<level::ERR>("Error starting timer for control host");
+            return;
+        }
 
         auto method = this->bus.new_method_call(host.c_str(),
                                                 IPMI_PATH.c_str(),
diff --git a/host-interface.hpp b/host-interface.hpp
index 0ad3bfb..8901a2b 100644
--- a/host-interface.hpp
+++ b/host-interface.hpp
@@ -4,7 +4,7 @@
 #include <sdbusplus/bus.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <xyz/openbmc_project/Control/Host/server.hpp>
-#include "elog-errors.hpp"
+#include <timer.hpp>
 
 namespace phosphor
 {
@@ -26,13 +26,17 @@
          *
          * @param[in] bus       - The Dbus bus object
          * @param[in] objPath   - The Dbus object path
+         * @param[in] events    - The sd_event pointer
          */
         Host(sdbusplus::bus::bus& bus,
-             const char* objPath) :
+             const char* objPath,
+             sd_event* events) :
              sdbusplus::server::object::object<
                 sdbusplus::xyz::openbmc_project::Control::server::Host>(
                         bus, objPath),
-             bus(bus)
+             bus(bus),
+             timer(events,
+                   std::bind(&Host::hostTimeout, this))
         {}
 
         /** @brief Send input command to host
@@ -51,30 +55,24 @@
          *  passed to the host (which is required when calling this interface)
          *
          */
-        Command getNextCommand()
-        {
-            if(this->workQueue.empty())
-            {
-                log<level::ERR>("Control Host work queue is empty!");
-                elog<xyz::openbmc_project::Control::Internal::Host::QueueEmpty>();
-            }
-            Command command = this->workQueue.front();
-            this->workQueue.pop();
-            this->commandComplete(command, Result::Success);
-            this->checkQueue();
-            return command;
-        }
+        Command getNextCommand();
 
     private:
 
         /** @brief Check if anything in queue and alert host if so */
         void checkQueue();
 
+        /** @brief Call back interface on message timeouts to host */
+        void hostTimeout();
+
         /** @brief Persistent sdbusplus DBus bus connection. */
         sdbusplus::bus::bus& bus;
 
         /** @brief Queue to store the requested commands */
         std::queue<Command> workQueue{};
+
+        /** @brief Timer for commands to host */
+        phosphor::ipmi::Timer timer;
 };
 
 } // namespace host
diff --git a/softoff/Makefile.am b/softoff/Makefile.am
index 35734d2..b7f4c92 100644
--- a/softoff/Makefile.am
+++ b/softoff/Makefile.am
@@ -2,9 +2,11 @@
 AM_CPPFLAGS = -I$(top_srcdir)
 sbin_PROGRAMS = phosphor-softpoweroff
 
+# Using ../ instead of $(top_srcdir) due to automake bug in version 1.15.
+#  https://debbugs.gnu.org/cgi/bugreport.cgi?bug=13928
 phosphor_softpoweroff_SOURCES = \
                     softoff.cpp \
-                    timer.cpp   \
+                    ../timer.cpp   \
                     mainapp.cpp \
                     xyz/openbmc_project/Ipmi/Internal/SoftPowerOff/server.cpp
 
diff --git a/softoff/test/Makefile.am b/softoff/test/Makefile.am
index 2c65568..afdb620 100644
--- a/softoff/test/Makefile.am
+++ b/softoff/test/Makefile.am
@@ -9,4 +9,4 @@
 utest_CXXFLAGS = $(PTHREAD_CFLAGS)
 utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) $(SYSTEMD_LIBS) ${SDBUSPLUS_LIBS}
 utest_SOURCES = utest.cpp
-utest_LDADD = $(top_builddir)/softoff/timer.o
+utest_LDADD = $(top_builddir)/timer.o
diff --git a/systemintfcmds.cpp b/systemintfcmds.cpp
index 999e710..23e81ba 100644
--- a/systemintfcmds.cpp
+++ b/systemintfcmds.cpp
@@ -182,8 +182,12 @@
     sdbusplus::server::manager::manager objManager(*sdbus,
                                                    objPathInst.c_str());
 
+    // Get the sd_events pointer
+    auto events = ipmid_get_sd_event_connection();
+
     host = new phosphor::host::Host(*sdbus,
-                                    objPathInst.c_str());
+                                    objPathInst.c_str(),
+                                    events);
 
     sdbus->request_name(CONTROL_HOST_BUSNAME);
 
diff --git a/softoff/timer.cpp b/timer.cpp
similarity index 100%
rename from softoff/timer.cpp
rename to timer.cpp
diff --git a/softoff/timer.hpp b/timer.hpp
similarity index 100%
rename from softoff/timer.hpp
rename to timer.hpp