Implement basic queue for sending multiple commands

Sends dbus signal upon completion of command

Change-Id: Ic507f35af0b38305eecd5558c55738f2d283aac5
Signed-off-by: Andrew Geissler <andrewg@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index c9b28c2..1df5216 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -43,23 +43,18 @@
 libsysintfcmdsdir = ${libdir}/ipmid-providers
 libsysintfcmds_LTLIBRARIES = libsysintfcmds.la
 libsysintfcmds_la_SOURCES = \
-	systemintfcmds.cpp
-libsysintfcmds_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) -version-info 0:0:0 -shared
-libsysintfcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS)
-
-libhostservicedir = ${libdir}/ipmid-providers
-libhostservice_LTLIBRARIES = libhostservice.la
-libhostservice_la_SOURCES = \
-	host-services.cpp \
+	systemintfcmds.cpp \
 	host-interface.cpp \
 	utils.cpp
-libhostservice_la_LDFLAGS = $(SYSTEMD_LIBS) \
+libsysintfcmds_la_LDFLAGS = $(SYSTEMD_LIBS) \
                             $(libmapper_LIBS) \
                             $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+                            $(PHOSPHOR_LOGGING_LIBS) \
                             -version-info 0:0:0 -shared
-libhostservice_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
+libsysintfcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
+                             $(libmapper_CFLAGS) \
                              $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
-                             $(libmapper_CFLAGS)
+                             $(PHOSPHOR_LOGGING_CFLAGS)
 
 nobase_include_HEADERS = \
 	host-ipmid/ipmid-api.h
diff --git a/elog-errors.hpp b/elog-errors.hpp
new file mode 100644
index 0000000..de74160
--- /dev/null
+++ b/elog-errors.hpp
@@ -0,0 +1,67 @@
+// This file was autogenerated.  Do not edit!
+// See elog-gen.py for more details
+#pragma once
+
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <sdbusplus/exception.hpp>
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+
+
+namespace phosphor
+{
+
+namespace logging
+{
+
+namespace xyz
+{
+namespace openbmc_project
+{
+namespace Control
+{
+namespace Internal
+{
+namespace Host
+{
+namespace _QueueEmpty
+{
+
+
+}  // namespace _QueueEmpty
+
+struct QueueEmpty : public sdbusplus::exception_t
+{
+    static constexpr auto errName = "xyz.openbmc_project.Control.Internal.Host.QueueEmpty";
+    static constexpr auto errDesc = "The host response queue is empty and it should not be!";
+    static constexpr auto L = level::ERR;
+    using metadata_types = std::tuple<>;
+
+    const char* name() const noexcept
+    {
+        return errName;
+    }
+
+    const char* description() const noexcept
+    {
+        return errDesc;
+    }
+
+    const char* what() const noexcept
+    {
+        return errName;
+    }
+};
+
+} // namespace Host
+} // namespace Internal
+} // namespace Control
+} // namespace openbmc_project
+} // namespace xyz
+
+
+} // namespace logging
+
+} // namespace phosphor
diff --git a/host-interface.cpp b/host-interface.cpp
index f485516..aa32ff0 100644
--- a/host-interface.cpp
+++ b/host-interface.cpp
@@ -1,4 +1,3 @@
-#include <queue>
 #include <phosphor-logging/log.hpp>
 #include <utils.hpp>
 #include "host-interface.hpp"
@@ -17,17 +16,15 @@
 // When you see base:: you know we're referencing our base class
 namespace base = sdbusplus::xyz::openbmc_project::Control::server;
 
-std::queue<base::Host::Command> workQueue{};
-
 void Host::execute(base::Host::Command command)
 {
     log<level::INFO>("Pushing cmd on to queue",
             entry("CONTROL_HOST_CMD=%s",
                   convertForMessage(command)));
-    workQueue.push(command);
+    this->workQueue.push(command);
 
     // If this was the only entry then send the SMS attention
-    if(workQueue.size() == 1)
+    if(this->workQueue.size() == 1)
     {
         log<level::INFO>("Asserting SMS Attention");
 
diff --git a/host-interface.hpp b/host-interface.hpp
index a3b7efa..36b41d9 100644
--- a/host-interface.hpp
+++ b/host-interface.hpp
@@ -1,13 +1,18 @@
 #pragma once
 
+#include <queue>
 #include <sdbusplus/bus.hpp>
+#include <phosphor-logging/elog.hpp>
 #include <xyz/openbmc_project/Control/Host/server.hpp>
+#include "elog-errors.hpp"
 
 namespace phosphor
 {
 namespace host
 {
 
+using namespace phosphor::logging;
+
 /** @class Host
  *  @brief OpenBMC control host interface implementation.
  *  @details A concrete implementation for xyz.openbmc_project.Control.Host
@@ -39,10 +44,33 @@
          */
         void execute(Command command) override;
 
+        /** @brief Return the next entry in the queue
+         *
+         *  Also signal that the command is complete since the interface
+         *  contract is that we emit this signal once the message has been
+         *  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);
+            return command;
+        }
+
     private:
 
         /** @brief Persistent sdbusplus DBus bus connection. */
         sdbusplus::bus::bus& bus;
+
+        /** @brief Queue to store the requested commands */
+        std::queue<Command> workQueue{};
 };
 
 } // namespace host
diff --git a/host-services.cpp b/host-services.cpp
deleted file mode 100644
index c78d90a..0000000
--- a/host-services.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <systemd/sd-bus.h>
-#include <mapper.h>
-#include "host-ipmid/ipmid-api.h"
-#include "host-interface.hpp"
-#include <config.h>
-
-void register_host_services() __attribute__((constructor));
-
-//------------------------------------------------------
-// Callback register function
-// -----------------------------------------------------
-
-// Globals to keep the object alive during process life
-std::unique_ptr<sdbusplus::bus::bus> sdbus = nullptr;
-// TODO openbmc/openbmc#1581 - unique_ptr causes seg fault
-phosphor::host::Host* host = nullptr;
-
-void register_host_services()
-{
-    // Gets a hook onto SYSTEM bus used by host-ipmid
-    sd_bus *bus = ipmid_get_sd_bus_connection();
-
-    sdbus = std::make_unique<sdbusplus::bus::bus>(bus);
-
-    // Create new xyz.openbmc_project.host object on the bus
-    auto objPathInst = std::string{CONTROL_HOST_OBJPATH} + '0';
-
-    // Add sdbusplus ObjectManager.
-    sdbusplus::server::manager::manager objManager(*sdbus,
-                                                   objPathInst.c_str());
-
-    host = new phosphor::host::Host(*sdbus,
-                                    objPathInst.c_str());
-
-    sdbus->request_name(CONTROL_HOST_BUSNAME);
-}
diff --git a/host-services.hpp b/host-services.hpp
deleted file mode 100644
index 4c2ddad..0000000
--- a/host-services.hpp
+++ /dev/null
@@ -1 +0,0 @@
-#include <systemd/sd-bus.h>
diff --git a/ipmid.hpp b/ipmid.hpp
index 9ee75e7..aab44c0 100644
--- a/ipmid.hpp
+++ b/ipmid.hpp
@@ -2,7 +2,6 @@
 #define __HOST_IPMID_IPMI_H__
 #include "host-ipmid/ipmid-api.h"
 #include <stdio.h>
-#include "host-services.hpp"
 
 // When the requester sends in a netfn and a command along with data, this
 // function will look for registered handlers that will handle that [netfn,cmd]
diff --git a/systemintfcmds.cpp b/systemintfcmds.cpp
index 62d9524..999e710 100644
--- a/systemintfcmds.cpp
+++ b/systemintfcmds.cpp
@@ -1,26 +1,21 @@
 #include "systemintfcmds.h"
 #include "host-ipmid/ipmid-api.h"
 #include "config.h"
+#include "host-interface.hpp"
 
 #include <stdio.h>
 #include <mapper.h>
 
 void register_netfn_app_functions() __attribute__((constructor));
 
-//-------------------------------------------------------------------
-// Called by Host post response from Get_Message_Flags
-//-------------------------------------------------------------------
-ipmi_ret_t ipmi_app_read_event(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                             ipmi_request_t request, ipmi_response_t response,
-                             ipmi_data_len_t data_len, ipmi_context_t context)
+using namespace sdbusplus::xyz::openbmc_project::Control::server;
+
+// Internal function to get next host command
+Host::Command getNextHostCmd();
+
+// Notify SofPowerOff application that host is responding to command
+void notifySoftOff()
 {
-    ipmi_ret_t rc = IPMI_CC_OK;
-
-    printf("IPMI APP READ EVENT command received\n");
-
-    // TODO : For now, this is catering only to the Soft Power Off via OEM SEL
-    //        mechanism. If we need to make this generically used for some
-    //        other conditions, then we can take advantage of context pointer.
 
     constexpr auto iface          = "org.freedesktop.DBus.Properties";
     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
@@ -31,9 +26,6 @@
                                     "SoftPowerOff.HostResponse.SoftOffReceived";
     char *busname = nullptr;
 
-    struct oem_sel_timestamped soft_off = {0};
-    *data_len = sizeof(struct oem_sel_timestamped);
-
     // Get the system bus where most system services are provided.
     auto bus = ipmid_get_sd_bus_connection();
 
@@ -56,31 +48,58 @@
     }
     else
     {
-            printf("Soft Power Off object is not available. Ignoring watchdog refresh");
+        printf("Soft Power Off object is not available. Ignoring watchdog \
+                refresh");
     }
+}
+
+//-------------------------------------------------------------------
+// Called by Host post response from Get_Message_Flags
+//-------------------------------------------------------------------
+ipmi_ret_t ipmi_app_read_event(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+        ipmi_request_t request, ipmi_response_t response,
+        ipmi_data_len_t data_len, ipmi_context_t context)
+{
+    ipmi_ret_t rc = IPMI_CC_OK;
+
+    printf("IPMI APP READ EVENT command received\n");
+
+    struct oem_sel_timestamped oem_sel = {0};
+    *data_len = sizeof(struct oem_sel_timestamped);
 
     // either id[0] -or- id[1] can be filled in. We will use id[0]
-    soft_off.id[0]   = SEL_OEM_ID_0;
-    soft_off.id[1]   = SEL_OEM_ID_0;
-    soft_off.type    = SEL_RECORD_TYPE_OEM;
+    oem_sel.id[0]   = SEL_OEM_ID_0;
+    oem_sel.id[1]   = SEL_OEM_ID_0;
+    oem_sel.type    = SEL_RECORD_TYPE_OEM;
 
     // Following 3 bytes are from IANA Manufactre_Id field. See below
-    soft_off.manuf_id[0]= 0x41;
-    soft_off.manuf_id[1]= 0xA7;
-    soft_off.manuf_id[2]= 0x00;
+    oem_sel.manuf_id[0]= 0x41;
+    oem_sel.manuf_id[1]= 0xA7;
+    oem_sel.manuf_id[2]= 0x00;
 
     // per IPMI spec NetFuntion for OEM
-    soft_off.netfun  = 0x3A;
+    oem_sel.netfun  = 0x3A;
 
-    // Mechanism to kick start soft shutdown.
-    soft_off.cmd     = CMD_POWER;
-    soft_off.data[0] = SOFT_OFF;
+    // Read from the queue to see what our response is here
+    Host::Command hCmd = getNextHostCmd();
+    switch (hCmd)
+    {
+    case Host::Command::SoftOff:
+        notifySoftOff();
+        oem_sel.cmd     = CMD_POWER;
+        oem_sel.data[0] = SOFT_OFF;
+        break;
+    case Host::Command::Heartbeat:
+        oem_sel.cmd     = CMD_HEARTBEAT;
+        oem_sel.data[0] = 0x00;
+        break;
+    }
 
     // All '0xFF' since unused.
-    memset(&soft_off.data[1], 0xFF, 3);
+    memset(&oem_sel.data[1], 0xFF, 3);
 
     // Pack the actual response
-    memcpy(response, &soft_off, *data_len);
+    memcpy(response, &oem_sel, *data_len);
     return rc;
 }
 
@@ -125,6 +144,13 @@
     return rc;
 }
 
+// Globals to keep the object alive during process life
+std::unique_ptr<sdbusplus::bus::bus> sdbus = nullptr;
+// TODO openbmc/openbmc#1581 - unique_ptr causes seg fault
+phosphor::host::Host* host = nullptr;
+
+
+#include <unistd.h>
 void register_netfn_app_functions()
 {
 
@@ -144,5 +170,27 @@
     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS, NULL, ipmi_app_get_msg_flags,
                            SYSTEM_INTERFACE);
 
+    // Gets a hook onto SYSTEM bus used by host-ipmid
+    sd_bus *bus = ipmid_get_sd_bus_connection();
+
+    sdbus = std::make_unique<sdbusplus::bus::bus>(bus);
+
+    // Create new xyz.openbmc_project.host object on the bus
+    auto objPathInst = std::string{CONTROL_HOST_OBJPATH} + '0';
+
+    // Add sdbusplus ObjectManager.
+    sdbusplus::server::manager::manager objManager(*sdbus,
+                                                   objPathInst.c_str());
+
+    host = new phosphor::host::Host(*sdbus,
+                                    objPathInst.c_str());
+
+    sdbus->request_name(CONTROL_HOST_BUSNAME);
+
     return;
 }
+
+Host::Command getNextHostCmd()
+{
+    return(host->getNextCommand());
+}
diff --git a/systemintfcmds.h b/systemintfcmds.h
index 19526f6..d603aaa 100644
--- a/systemintfcmds.h
+++ b/systemintfcmds.h
@@ -13,6 +13,8 @@
 #define SOFT_OFF            0x00
 // Major command for Any kind of power ops
 #define CMD_POWER           0x04
+// Major command for the heartbeat operation (verify host is alive)
+#define CMD_HEARTBEAT       0xFF
 
 // IPMI commands used via System Interface functions.
 enum ipmi_netfn_system_intf_cmds
diff --git a/xyz/openbmc_project/Control/Internal/Host.errors.yaml b/xyz/openbmc_project/Control/Internal/Host.errors.yaml
new file mode 100644
index 0000000..3c14863
--- /dev/null
+++ b/xyz/openbmc_project/Control/Internal/Host.errors.yaml
@@ -0,0 +1,3 @@
+- name: QueueEmpty
+  description: The host response queue is empty and it should not be!
+
diff --git a/xyz/openbmc_project/Control/Internal/Host.metadata.yaml b/xyz/openbmc_project/Control/Internal/Host.metadata.yaml
new file mode 100644
index 0000000..3f0b852
--- /dev/null
+++ b/xyz/openbmc_project/Control/Internal/Host.metadata.yaml
@@ -0,0 +1,3 @@
+- name: QueueEmpty
+  level: ERR
+