Use Host Command Manager in host interface implementation

Change-Id: Icefce510a3a0022bf0288fa99518459b732a2e04
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index ea44248..2f76317 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -75,18 +75,18 @@
 libsysintfcmds_LTLIBRARIES = libsysintfcmds.la
 libsysintfcmds_la_SOURCES = \
 	systemintfcmds.cpp \
-	host-interface.cpp \
-	utils.cpp \
-	timer.cpp
+	host-interface.cpp
 libsysintfcmds_la_LDFLAGS = $(SYSTEMD_LIBS) \
                             $(libmapper_LIBS) \
                             $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
                             $(PHOSPHOR_LOGGING_LIBS) \
+                            $(SDBUSPLUS_LIBS) \
                             -version-info 0:0:0 -shared
 libsysintfcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
                              $(libmapper_CFLAGS) \
                              $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
-                             $(PHOSPHOR_LOGGING_CFLAGS)
+                             $(PHOSPHOR_LOGGING_CFLAGS) \
+                             $(SDBUSPLUS_CFLAGS)
 
 nobase_include_HEADERS = \
 	host-ipmid/ipmid-api.h
diff --git a/configure.ac b/configure.ac
index 9c909ea..dd6a194 100644
--- a/configure.ac
+++ b/configure.ac
@@ -133,11 +133,17 @@
       [CONTROL_HOST_BUSNAME="xyz.openbmc_project.Control.Host"])
 AC_DEFINE_UNQUOTED([CONTROL_HOST_BUSNAME], ["$CONTROL_HOST_BUSNAME"], [The Control Host Dbus busname to own])
 
-# Service dbus root
-AC_ARG_VAR(CONTROL_HOST_OBJPATH, [The Control Host Dbus root])
-AS_IF([test "x$CONTROL_HOST_OBJPATH" == "x"],
-      [CONTROL_HOST_OBJPATH="/xyz/openbmc_project/control/host"])
-AC_DEFINE_UNQUOTED([CONTROL_HOST_OBJPATH], ["$CONTROL_HOST_OBJPATH"], [The Control Host Dbus root])
+# Host object name in the D-Bus
+AC_ARG_VAR(HOST_NAME, [The Host name in the object path])
+AS_IF([test "x$HOST_NAME" == "x"],
+      [HOST_NAME="host"])
+AC_DEFINE_UNQUOTED([HOST_NAME], ["$HOST_NAME"], [The Host name in the object path])
+
+# Service dbus object manager
+AC_ARG_VAR(CONTROL_HOST_OBJ_MGR, [The Control Host D-Bus Object Manager])
+AS_IF([test "x$CONTROL_HOST_OBJ_MGR" == "x"],
+      [CONTROL_HOST_OBJ_MGR="/xyz/openbmc_project/control"])
+AC_DEFINE_UNQUOTED([CONTROL_HOST_OBJ_MGR], ["$CONTROL_HOST_OBJ_MGR"], [The Control Host D-Bus Object Manager])
 
 # Create configured output
 AC_CONFIG_FILES([Makefile test/Makefile softoff/Makefile softoff/test/Makefile])
diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp
index 5b75b55..329e606 100644
--- a/host-cmd-manager.cpp
+++ b/host-cmd-manager.cpp
@@ -6,7 +6,6 @@
 #include <utils.hpp>
 #include <config.h>
 #include <host-cmd-manager.hpp>
-
 namespace phosphor
 {
 namespace host
diff --git a/host-interface.cpp b/host-interface.cpp
index 0bce888..1de2629 100644
--- a/host-interface.cpp
+++ b/host-interface.cpp
@@ -1,122 +1,81 @@
-#include <chrono>
-#include <phosphor-logging/log.hpp>
+#include <functional>
+#include <systemintfcmds.h>
+#include <ipmid-host-cmd.hpp>
 #include <utils.hpp>
+#include <phosphor-logging/log.hpp>
 #include <config.h>
-#include "host-interface.hpp"
-
+#include <host-interface.hpp>
 namespace phosphor
 {
 namespace host
 {
-
-constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
-constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
-constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
-
-using namespace phosphor::logging;
-
-// When you see base:: you know we're referencing our base class
-namespace base = sdbusplus::xyz::openbmc_project::Control::server;
-
-base::Host::Command Host::getNextCommand()
+namespace command
 {
-    // Stop the timer
-    auto r = timer.setTimer(SD_EVENT_OFF);
-    if (r < 0)
+
+// When you see Base:: you know we're referencing our base class
+namespace Base = sdbusplus::xyz::openbmc_project::Control::server;
+
+// IPMI OEM command.
+// https://github.com/openbmc/openbmc/issues/2082 for handling
+// Non-OEM commands that need to send SMS_ATN
+using OEMCmd = uint8_t;
+
+// Map of IPMI OEM command to its equivalent interface command.
+// This is needed when invoking the callback handler to indicate
+// the status of the executed command.
+static const std::map<OEMCmd, Host::Command> intfCommand = {
     {
-        log<level::ERR>("Failure to STOP the timer",
-                entry("ERROR=%s", strerror(-r)));
-    }
-
-    if(this->workQueue.empty())
+        CMD_HEARTBEAT,
+            Base::Host::Command::Heartbeat
+    },
     {
-        // Just return a heartbeat in this case.  A spurious SMS_ATN was
-        // asserted for the host (probably from a previous boot).
-        log<level::INFO>("Control Host work queue is empty!");
-        return (Command::Heartbeat);
+        CMD_POWER,
+            Base::Host::Command::SoftOff
     }
+};
 
-    // Pop the processed entry off the queue
-    Command command = this->workQueue.front();
-    this->workQueue.pop();
+// Map of Interface command to its corresponding IPMI OEM command.
+// This is needed when pushing IPMI commands to command manager's
+// queue. The same pair will be returned when IPMI asks us
+// why a SMS_ATN was sent
+static const std::map<Host::Command, IpmiCmdData> ipmiCommand = {
+    {
+        Base::Host::Command::Heartbeat,
+            std::make_pair(CMD_HEARTBEAT, 0x00)
+    },
+    {
+        Base::Host::Command::SoftOff,
+            std::make_pair(CMD_POWER, SOFT_OFF)
+    }
+};
 
-    // 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()
+// Called at user request
+void Host::execute(Base::Host::Command command)
 {
-    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);
-    }
-}
+    using namespace phosphor::logging;
 
-void Host::checkQueue()
-{
-    if (this->workQueue.size() >= 1)
-    {
-        log<level::INFO>("Asserting SMS Attention");
-
-        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);
-
-        // 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(),
-                                                IPMI_INTERFACE.c_str(),
-                                                "setAttention");
-        auto reply = this->bus.call(method);
-
-        if (reply.is_method_error())
-        {
-            log<level::ERR>("Error in setting SMS attention");
-            throw std::runtime_error("ERROR in call to setAttention");
-        }
-        log<level::INFO>("SMS Attention asserted");
-    }
-}
-
-void Host::execute(base::Host::Command command)
-{
     log<level::INFO>("Pushing cmd on to queue",
             entry("CONTROL_HOST_CMD=%s",
                   convertForMessage(command)));
 
-    this->workQueue.push(command);
+    auto cmd = std::make_tuple(ipmiCommand.at(command),
+                        std::bind(&Host::commandStatusHandler,
+                            this, std::placeholders::_1,
+                                std::placeholders::_2));
 
-    // Alert host if this is only command in queue otherwise host will
-    // be notified of next message after processing the current one
-    if (this->workQueue.size() == 1)
-    {
-        this->checkQueue();
-    }
-    else
-    {
-        log<level::INFO>("Command in process, no attention");
-    }
-
-    return;
+    return ipmid_send_cmd_to_host(std::move(cmd));
 }
 
+// Called into by Command Manager
+void Host::commandStatusHandler(IpmiCmdData cmd, bool status)
+{
+    // Need to convert <cmd> to the equivalent one mentioned in spec
+    auto value = status ? Result::Success : Result::Failure;
+
+    // Fire a signal
+    this->commandComplete(intfCommand.at(std::get<0>(cmd)), value);
+}
+
+} // namespace command
 } // namespace host
 } // namepsace phosphor
diff --git a/host-interface.hpp b/host-interface.hpp
index 8901a2b..5b2ac68 100644
--- a/host-interface.hpp
+++ b/host-interface.hpp
@@ -1,17 +1,14 @@
 #pragma once
 
-#include <queue>
 #include <sdbusplus/bus.hpp>
-#include <phosphor-logging/elog.hpp>
 #include <xyz/openbmc_project/Control/Host/server.hpp>
-#include <timer.hpp>
-
+#include <host-cmd-manager.hpp>
 namespace phosphor
 {
 namespace host
 {
-
-using namespace phosphor::logging;
+namespace command
+{
 
 /** @class Host
  *  @brief OpenBMC control host interface implementation.
@@ -24,56 +21,43 @@
     public:
         /** @brief Constructs Host Control Interface
          *
-         * @param[in] bus       - The Dbus bus object
-         * @param[in] objPath   - The Dbus object path
-         * @param[in] events    - The sd_event pointer
+         *  @param[in] bus     - The Dbus bus object
+         *  @param[in] objPath - The Dbus object path
          */
         Host(sdbusplus::bus::bus& bus,
-             const char* objPath,
-             sd_event* events) :
+             const char* objPath) :
              sdbusplus::server::object::object<
                 sdbusplus::xyz::openbmc_project::Control::server::Host>(
                         bus, objPath),
-             bus(bus),
-             timer(events,
-                   std::bind(&Host::hostTimeout, this))
-        {}
+             bus(bus)
+        {
+            // Nothing to do
+        }
 
         /** @brief Send input command to host
+         *         Note that the command will be queued in a FIFO if
+         *         other commands to the host have yet to be run
          *
-         * Note that the command will be queued in a FIFO if other commands
-         * to the host have yet to be run
-         *
-         * @param[in] command       - Input command to execute
+         *  @param[in] command - Input command to execute
          */
         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();
-
     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. */
+        /** @brief 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;
+        /** @brief  Callback function to be invoked by command manager
+         *
+         *  @detail Conveys the status of the last Host bound command.
+         *          Depending on the status,  a CommandComplete or
+         *          CommandFailure signal would be sent
+         *
+         *  @param[in] cmd    - IPMI command and data sent to Host
+         *  @param[in] status - Success or Failure
+         */
+        void commandStatusHandler(IpmiCmdData cmd, bool status);
 };
 
+} // namespace command
 } // namespace host
 } // namespace phosphor
diff --git a/ipmid-host-cmd.hpp b/ipmid-host-cmd.hpp
new file mode 100644
index 0000000..7ef8b39
--- /dev/null
+++ b/ipmid-host-cmd.hpp
@@ -0,0 +1,10 @@
+#include <memory>
+#include <sdbusplus/bus.hpp>
+#include <host-cmd-utils.hpp>
+
+// Need this to use new sdbusplus compatible interfaces
+using sdbusPtr =  std::unique_ptr<sdbusplus::bus::bus>;
+extern sdbusPtr& ipmid_get_sdbus_plus_handler();
+
+// Global Host Bound Command manager
+extern void ipmid_send_cmd_to_host(phosphor::host::command::CommandHandler&&);
diff --git a/ipmid.cpp b/ipmid.cpp
index 7583bb8..f41e27f 100644
--- a/ipmid.cpp
+++ b/ipmid.cpp
@@ -25,6 +25,8 @@
 #include "sensorhandler.h"
 #include "ipmid.hpp"
 #include "settings.hpp"
+#include <host-cmd-manager.hpp>
+#include <ipmid-host-cmd.hpp>
 
 using namespace phosphor::logging;
 namespace sdbusRule = sdbusplus::bus::match::rules;
@@ -33,6 +35,17 @@
 sd_bus_slot *ipmid_slot = NULL;
 sd_event *events = nullptr;
 
+// Need this to use new sdbusplus compatible interfaces
+sdbusPtr sdbusp;
+
+// Global Host Bound Command manager
+using cmdManagerPtr = std::unique_ptr<phosphor::host::command::Manager>;
+cmdManagerPtr cmdManager;
+
+// Command and handler tuple. Used when clients ask the command to be put
+// into host message queue
+using CommandHandler = phosphor::host::command::CommandHandler;
+
 // Initialise restricted mode to true
 bool restricted_mode = true;
 
@@ -238,7 +251,9 @@
     dest = sd_bus_message_get_sender(req);
     path = sd_bus_message_get_path(req);
 
-    r = sd_bus_message_new_method_call(bus,&m,dest,path,DBUS_INTF,"sendMessage");
+    r = sd_bus_message_new_method_call(bus,&m,dest,path,
+                                       DBUS_INTF,
+                                       "sendMessage");
     if (r < 0) {
         fprintf(stderr, "Failed to add the method object: %s\n", strerror(-r));
         return -1;
@@ -486,6 +501,19 @@
     return ipmid_slot;
 }
 
+// Calls host command manager to do the right thing for the command
+void ipmid_send_cmd_to_host(CommandHandler&& cmd) {
+     return cmdManager->execute(std::move(cmd));
+}
+
+cmdManagerPtr& ipmid_get_host_cmd_manager() {
+     return cmdManager;
+}
+
+sdbusPtr& ipmid_get_sdbus_plus_handler() {
+     return sdbusp;
+}
+
 int main(int argc, char *argv[])
 {
     int r;
@@ -536,6 +564,11 @@
         goto finish;
     }
 
+    // Now create the Host Bound Command manager. Need sdbusplus
+    // to use the generated bindings
+    sdbusp = std::make_unique<sdbusplus::bus::bus>(bus);
+    cmdManager = std::make_unique<phosphor::host::command::Manager>(
+                            *sdbusp, events);
 
     // Register all the handlers that provider implementation to IPMI commands.
     ipmi_register_callback_handlers(HOST_IPMI_LIB_PATH);
diff --git a/softoff/softoff.cpp b/softoff/softoff.cpp
index 767eb90..4a3166c 100644
--- a/softoff/softoff.cpp
+++ b/softoff/softoff.cpp
@@ -29,8 +29,8 @@
 
 void SoftPowerOff::sendHostShutDownCmd()
 {
-    std::string ctrlHostPath{CONTROL_HOST_OBJPATH};
-    ctrlHostPath += "0";
+    auto ctrlHostPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' +
+                        HOST_NAME + '0';
     auto host = ::ipmi::getService(this->bus,
                                    CONTROL_HOST_BUSNAME,
                                    ctrlHostPath.c_str());
diff --git a/systemintfcmds.cpp b/systemintfcmds.cpp
index 60961d3..2258630 100644
--- a/systemintfcmds.cpp
+++ b/systemintfcmds.cpp
@@ -1,6 +1,8 @@
 #include "systemintfcmds.h"
 #include "host-ipmid/ipmid-api.h"
+#include "ipmid-host-cmd.hpp"
 #include "config.h"
+#include "host-cmd-manager.hpp"
 #include "host-interface.hpp"
 
 #include <stdio.h>
@@ -10,8 +12,9 @@
 
 using namespace sdbusplus::xyz::openbmc_project::Control::server;
 
-// Internal function to get next host command
-Host::Command getNextHostCmd();
+// For accessing Host command manager
+using cmdManagerPtr = std::unique_ptr<phosphor::host::command::Manager>;
+extern cmdManagerPtr& ipmid_get_host_cmd_manager();
 
 //-------------------------------------------------------------------
 // Called by Host post response from Get_Message_Flags
@@ -40,19 +43,11 @@
     // per IPMI spec NetFuntion for OEM
     oem_sel.netfun  = 0x3A;
 
-    // Read from the queue to see what our response is here
-    Host::Command hCmd = getNextHostCmd();
-    switch (hCmd)
-    {
-    case Host::Command::SoftOff:
-        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;
-    }
+    // Read from the Command Manager queue. What gets returned is a
+    // pair of <command, data> that can be directly used here
+    auto hostCmd    = ipmid_get_host_cmd_manager()->getNextCommand();
+    oem_sel.cmd     = hostCmd.first;
+    oem_sel.data[0] = hostCmd.second;
 
     // All '0xFF' since unused.
     memset(&oem_sel.data[1], 0xFF, 3);
@@ -105,8 +100,10 @@
 
 namespace {
 // Static storage to keep the object alive during process life
-std::unique_ptr<sdbusplus::bus::bus> sdbus __attribute__((init_priority(101)));
-std::unique_ptr<phosphor::host::Host> host __attribute__((init_priority(101)));
+std::unique_ptr<phosphor::host::command::Host> host
+                __attribute__((init_priority(101)));
+std::unique_ptr<sdbusplus::server::manager::manager> objManager
+                __attribute__((init_priority(101)));
 }
 
 #include <unistd.h>
@@ -129,31 +126,18 @@
     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';
+    auto objPath = std::string{CONTROL_HOST_OBJ_MGR} + '/' + HOST_NAME + '0';
 
     // Add sdbusplus ObjectManager.
-    sdbusplus::server::manager::manager objManager(*sdbus,
-                                                   objPathInst.c_str());
+    auto& sdbusPlusHandler = ipmid_get_sdbus_plus_handler();
+    objManager = std::make_unique<sdbusplus::server::manager::manager>(
+                        *sdbusPlusHandler,
+                         CONTROL_HOST_OBJ_MGR);
 
-    // Get the sd_events pointer
-    auto events = ipmid_get_sd_event_connection();
-
-    host = std::make_unique<phosphor::host::Host>(*sdbus,
-                                                  objPathInst.c_str(),
-                                                  events);
-
-    sdbus->request_name(CONTROL_HOST_BUSNAME);
+    host = std::make_unique<phosphor::host::command::Host>(
+                            *sdbusPlusHandler, objPath.c_str());
+    sdbusPlusHandler->request_name(CONTROL_HOST_BUSNAME);
 
     return;
 }
-
-Host::Command getNextHostCmd()
-{
-    return(host->getNextCommand());
-}
diff --git a/timer.hpp b/timer.hpp
index cbd3444..6a59c0d 100644
--- a/timer.hpp
+++ b/timer.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <chrono>
 #include <functional>
 #include <systemd/sd-event.h>
 namespace phosphor