host-condition: implement property read

This is the implementation of the new PDI interface for checking if the
host is running.

Tested:
- Verified when host was not running that expected response was
  provided:
  busctl get-property xyz.openbmc_project.Control.Host /xyz/openbmc_project/control/host0 xyz.openbmc_project.Condition.HostFirmware CurrentFirmwareCondition
  s "xyz.openbmc_project.Condition.HostFirmware.FirmwareCondition.Off"

- Verified when host was running, that expected response was provided:
  busctl get-property xyz.openbmc_project.Control.Host /xyz/openbmc_project/control/host0 xyz.openbmc_project.Condition.HostFirmware CurrentFirmwareCondition
  s "xyz.openbmc_project.Condition.HostFirmware.FirmwareCondition.Running"

- Verified that if hostAckCallback is not called within allotted time
  that hostAckTimer expires and successfully sets the hostCondition

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I07bc3b3eac4fdfde2a8a1798c2a29f5f2a34a832
diff --git a/host-interface.cpp b/host-interface.cpp
index 7836b40..ffd1aaf 100644
--- a/host-interface.cpp
+++ b/host-interface.cpp
@@ -1,3 +1,4 @@
+
 #include "config.h"
 
 #include "host-interface.hpp"
@@ -7,7 +8,10 @@
 #include <functional>
 #include <ipmid-host/cmd-utils.hpp>
 #include <ipmid-host/cmd.hpp>
+#include <ipmid/api.hpp>
 #include <ipmid/utils.hpp>
+#include <memory>
+#include <optional>
 #include <phosphor-logging/log.hpp>
 
 namespace phosphor
@@ -17,6 +21,8 @@
 namespace command
 {
 
+using namespace phosphor::logging;
+
 // When you see Base:: you know we're referencing our base class
 namespace Base = sdbusplus::xyz::openbmc_project::Control::server;
 
@@ -43,8 +49,6 @@
 // Called at user request
 void Host::execute(Base::Host::Command command)
 {
-    using namespace phosphor::logging;
-
     log<level::DEBUG>(
         "Pushing cmd on to queue",
         entry("CONTROL_HOST_CMD=%s", convertForMessage(command).c_str()));
@@ -69,8 +73,50 @@
 
 Host::FirmwareCondition Host::currentFirmwareCondition() const
 {
-    // TODO: Implement function
-    return FirmwareCondition::Unknown;
+    // shared object used to wait for host response
+    auto hostCondition =
+        std::make_shared<std::optional<Host::FirmwareCondition>>();
+
+    // callback for command to host
+    auto hostAckCallback = [hostCondition](IpmiCmdData cmd, bool status) {
+        auto value = status ? Host::FirmwareCondition::Running
+                            : Host::FirmwareCondition::Off;
+
+        log<level::DEBUG>("currentFirmwareCondition:hostAckCallback fired",
+                          entry("CONTROL_HOST_CMD=%i", value));
+
+        *(hostCondition.get()) = value;
+        return;
+    };
+
+    auto cmd = phosphor::host::command::CommandHandler(
+        ipmiCommand.at(Base::Host::Command::Heartbeat),
+        std::move(hostAckCallback));
+
+    ipmid_send_cmd_to_host(std::move(cmd));
+
+    // Timer to ensure this function returns something within a reasonable time
+    phosphor::Timer hostAckTimer([hostCondition]() {
+        log<level::DEBUG>("currentFirmwareCondition: timer expired!");
+        *(hostCondition.get()) = Host::FirmwareCondition::Off;
+    });
+
+    // Wait 1 second past the ATN_ACK timeout to ensure we wait for as
+    // long as the timeout
+    hostAckTimer.start(std::chrono::seconds(IPMI_SMS_ATN_ACK_TIMEOUT_SECS + 1));
+
+    auto io = getIoContext();
+
+    while (!hostCondition.get()->has_value())
+    {
+        log<level::DEBUG>(
+            "currentFirmwareCondition: waiting for host response");
+        io->run_for(std::chrono::milliseconds(100));
+    }
+    hostAckTimer.stop();
+
+    log<level::DEBUG>("currentFirmwareCondition: hostCondition is ready!");
+    return hostCondition.get()->value();
 }
 
 } // namespace command