Clear host command queue on a power on

When the RequestedHostTransition property changes to On, clear
any pending commands in the command queue.  This is done to avoid
race conditions around state transitions as well as other
scenarios like the following:

1) Host is already off
2) RequestedHostTransition is set to Off
3) RequestedHostTransition is set to On
4) Host powers on
5) Host immediately powers off because of the pending command
   sent in 2).

Resolves openbmc/openbmc#3207

Tested:  Verified the scenario above no longer occurs.

Change-Id: I26c8195c305c75b01333d1b10ff4bf16d76b91a6
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/host-cmd-manager.cpp b/host-cmd-manager.cpp
index 799f9bf..4316451 100644
--- a/host-cmd-manager.cpp
+++ b/host-cmd-manager.cpp
@@ -2,6 +2,7 @@
 #include <phosphor-logging/log.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
 #include <systemintfcmds.h>
 #include <utils.hpp>
 #include <config.h>
@@ -18,15 +19,26 @@
 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";
+constexpr auto HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
+constexpr auto HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host";
+constexpr auto HOST_TRANS_PROP = "RequestedHostTransition";
 
 // For throwing exceptions
 using namespace phosphor::logging;
 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
                             Error::InternalFailure;
 
+namespace sdbusRule = sdbusplus::bus::match::rules;
+
 Manager::Manager(sdbusplus::bus::bus& bus, sd_event* event) :
             bus(bus),
-            timer(event, std::bind(&Manager::hostTimeout, this))
+            timer(event, std::bind(&Manager::hostTimeout, this)),
+            hostTransitionMatch(bus,
+                    sdbusRule::propertiesChanged(
+                        HOST_STATE_PATH,
+                        HOST_STATE_INTERFACE),
+                    std::bind(&Manager::clearQueueOnPowerOn, this,
+                        std::placeholders::_1))
 {
     // Nothing to do here.
 }
@@ -75,6 +87,11 @@
 {
     log<level::ERR>("Host control timeout hit!");
 
+    clearQueue();
+}
+
+void Manager::clearQueue()
+{
     // Dequeue all entries and send fail signal
     while(!this->workQueue.empty())
     {
@@ -150,6 +167,29 @@
     return;
 }
 
+void Manager::clearQueueOnPowerOn(sdbusplus::message::message& msg)
+{
+    namespace server = sdbusplus::xyz::openbmc_project::State::server;
+
+    ::ipmi::DbusInterface interface;
+    ::ipmi::PropertyMap properties;
+
+    msg.read(interface, properties);
+
+    if (properties.find(HOST_TRANS_PROP) == properties.end())
+    {
+        return;
+    }
+
+    auto& requestedState = properties.at(HOST_TRANS_PROP).get<std::string>();
+
+    if (server::Host::convertTransitionFromString(requestedState) ==
+            server::Host::Transition::On)
+    {
+        clearQueue();
+    }
+}
+
 } // namespace command
 } // namespace host
 } // namepsace phosphor