Move get bmc device state api to yield calls

Get BMC current state in getDeviceID IPMI command
is using blocking D-Bus calls. Also every GetDeviceID
is making 2 D-Bus calls which is overhead considering
number of GetDeviceID calls from client.

 - Moved the blocking D-Bus calls to yield method
   calls.
 - Added signal for catching CurrentBMCState change
   during run time and update value instead of multiple
   D-Bus calls on every IPMI command request.

Tested:
GetDeviceID works fine in both cases(BMC busy and ready).

Change-Id: Ie81192e6b2d7d66d5fe2a3eb705323c6bec79749
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/src/appcommands.cpp b/src/appcommands.cpp
index 485422d..870886e 100644
--- a/src/appcommands.cpp
+++ b/src/appcommands.cpp
@@ -22,7 +22,6 @@
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/log.hpp>
 #include <regex>
-#include <xyz/openbmc_project/State/BMC/server.hpp>
 
 namespace ipmi
 {
@@ -39,38 +38,79 @@
 static constexpr const char* softwareFunctionalPath =
     "/xyz/openbmc_project/software/functional";
 
-using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC;
-constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC";
-constexpr auto bmc_state_property = "CurrentBMCState";
+static constexpr const char* currentBmcStateProp = "CurrentBMCState";
+static constexpr const char* bmcStateReadyStr =
+    "xyz.openbmc_project.State.BMC.BMCState.Ready";
 
-bool getCurrentBmcState()
+static std::unique_ptr<sdbusplus::bus::match::match> bmcStateChangedSignal;
+static uint8_t bmcDeviceBusy = true;
+
+int initBMCDeviceState(ipmi::Context::ptr ctx)
 {
-    sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
-
-    // Get the Inventory object implementing the BMC interface
-    ipmi::DbusObjectInfo bmcObject =
-        ipmi::getDbusObject(bus, bmc_state_interface);
-    auto variant =
-        ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
-                              bmc_state_interface, bmc_state_property);
-
-    return std::holds_alternative<std::string>(variant) &&
-           BMC::convertBMCStateFromString(std::get<std::string>(variant)) ==
-               BMC::BMCState::Ready;
-}
-
-bool getCurrentBmcStateWithFallback(const bool fallbackAvailability)
-{
-    try
+    DbusObjectInfo objInfo;
+    boost::system::error_code ec =
+        ipmi::getDbusObject(ctx, bmcStateIntf, "/", "bmc0", objInfo);
+    if (ec)
     {
-        return getCurrentBmcState();
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "initBMCDeviceState: Failed to perform GetSubTree action",
+            phosphor::logging::entry("ERROR=%s", ec.message().c_str()),
+            phosphor::logging::entry("INTERFACE=%s", bmcStateIntf));
+        return -1;
     }
-    catch (...)
+
+    std::string bmcState;
+    ec = ipmi::getDbusProperty(ctx, objInfo.second, objInfo.first, bmcStateIntf,
+                               currentBmcStateProp, bmcState);
+    if (ec)
     {
-        // Nothing provided the BMC interface, therefore return whatever was
-        // configured as the default.
-        return fallbackAvailability;
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "initBMCDeviceState: Failed to get CurrentBMCState property",
+            phosphor::logging::entry("ERROR=%s", ec.message().c_str()));
+        return -1;
     }
+
+    bmcDeviceBusy = (bmcState != bmcStateReadyStr);
+
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "BMC device state updated");
+
+    // BMC state may change runtime while doing firmware udpate.
+    // Register for property change signal to update state.
+    bmcStateChangedSignal = std::make_unique<sdbusplus::bus::match::match>(
+        *(ctx->bus),
+        sdbusplus::bus::match::rules::propertiesChanged(objInfo.first,
+                                                        bmcStateIntf),
+        [](sdbusplus::message::message& msg) {
+            std::map<std::string, sdbusplus::message::variant<std::string>>
+                props;
+            std::vector<std::string> inVal;
+            std::string iface;
+            try
+            {
+                msg.read(iface, props, inVal);
+            }
+            catch (const std::exception& e)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "Exception caught in Get CurrentBMCState");
+                return;
+            }
+
+            auto it = props.find(currentBmcStateProp);
+            if (it != props.end())
+            {
+                std::string* state = std::get_if<std::string>(&it->second);
+                if (state)
+                {
+                    bmcDeviceBusy = (*state != bmcStateReadyStr);
+                    phosphor::logging::log<phosphor::logging::level::INFO>(
+                        "BMC device state updated");
+                }
+            }
+        });
+
+    return 0;
 }
 
 /**
@@ -244,7 +284,7 @@
     } devId;
     static bool fwVerInitialized = false;
     static bool devIdInitialized = false;
-    static bool defaultActivationSetting = false;
+    static bool bmcStateInitialized = false;
     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
     const char* prodIdFilename = "/var/cache/private/prodID";
     if (!fwVerInitialized)
@@ -329,12 +369,16 @@
         }
     }
 
-    // Set availability to the actual current BMC state.
-    // The availability may change in run time so don't cache.
-    bool bmcDevBusy = getCurrentBmcStateWithFallback(defaultActivationSetting);
+    if (!bmcStateInitialized)
+    {
+        if (!initBMCDeviceState(ctx))
+        {
+            bmcStateInitialized = true;
+        }
+    }
 
     return ipmi::responseSuccess(devId.id, devId.revision, devId.fwMajor,
-                                 bmcDevBusy, devId.fwMinor, devId.ipmiVer,
+                                 bmcDeviceBusy, devId.fwMinor, devId.ipmiVer,
                                  devId.addnDevSupport, devId.manufId,
                                  devId.prodId, devId.aux);
 }