BMC RR: check whether the Host is running

This commit checks whether Host is up by sending a request to
Host when pldmd starts up. This does not differentiate the case
when there is a bmc RR or pldmd restarted.

Updates the Host condition accordingly.

Initiates a PDR exchange if Host is running.

Change-Id: I630b342f5f9cc62530e0274f46357d238e92d0c9
Signed-off-by: sampmisr <sampmisr@gmail.com>
diff --git a/host-bmc/host_pdr_handler.cpp b/host-bmc/host_pdr_handler.cpp
index a2d4d17..a786ed4 100644
--- a/host-bmc/host_pdr_handler.cpp
+++ b/host-bmc/host_pdr_handler.cpp
@@ -7,6 +7,10 @@
 #include <assert.h>
 
 #include <nlohmann/json.hpp>
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/exception.hpp>
+#include <sdeventplus/source/io.hpp>
+#include <sdeventplus/source/time.hpp>
 
 #include <fstream>
 
@@ -482,4 +486,114 @@
     this->getHostPDR(nextRecordHandle);
 }
 
+void HostPDRHandler::setHostState()
+{
+    using namespace sdeventplus;
+    using namespace sdeventplus::source;
+    constexpr auto clockId = sdeventplus::ClockId::RealTime;
+    using Clock = Clock<clockId>;
+    using Timer = Time<clockId>;
+
+    auto event1 = sdeventplus::Event::get_default();
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    bus.attach_event(event1.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    responseReceived = false;
+    timeOut = false;
+
+    int fd = pldm_open();
+    if (-1 == fd)
+    {
+        std::cerr << "Failed to connect to mctp demux daemon \n";
+        return;
+    }
+
+    auto timerCallback = [=, this](Timer& /*source*/,
+                                   Timer::TimePoint /*time*/) {
+        timeOut = true;
+        if (!responseReceived)
+        {
+            std::cout << "PLDM did not get a response from Host"
+                         " Host seems to be off \n";
+        }
+        return;
+    };
+
+    Timer time(event1, (Clock(event1).now() + std::chrono::seconds{3}),
+               std::chrono::seconds{1}, std::move(timerCallback));
+
+    auto callback = [=, this](IO& /*io*/, int fd, uint32_t revents) {
+        if (!(revents & EPOLLIN))
+        {
+            return;
+        }
+        uint8_t* responseMsg = nullptr;
+        size_t responseMsgSize{};
+        auto rc =
+            pldm_recv(mctp_eid, fd, insId, &responseMsg, &responseMsgSize);
+        if (rc != PLDM_REQUESTER_SUCCESS)
+        {
+            return;
+        }
+        std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
+            responseMsg, std::free};
+        auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
+        std::cout << "Getting the response. PLDM RC = " << std::hex
+                  << std::showbase
+                  << static_cast<uint16_t>(response->payload[0]) << "\n";
+        responseReceived = true;
+        return;
+    };
+    IO io(event1, fd, EPOLLIN, std::move(callback));
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                    PLDM_GET_PDR_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    uint32_t recordHandle{};
+    insId = requester.getInstanceId(mctp_eid);
+    auto rc =
+        encode_get_pdr_req(insId, recordHandle, 0, PLDM_GET_FIRSTPART,
+                           UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
+    if (rc != PLDM_SUCCESS)
+    {
+        requester.markFree(mctp_eid, insId);
+        std::cerr << "Failed to encode_get_pdr_req, rc = " << rc << std::endl;
+        return;
+    }
+    rc = pldm_send(mctp_eid, fd, requestMsg.data(), requestMsg.size());
+    if (0 > rc)
+    {
+        std::cerr << "Failed to send message RC = " << rc
+                  << ", errno = " << errno << "\n";
+        return;
+    }
+    while (1)
+    {
+        if (responseReceived)
+        {
+            requester.markFree(mctp_eid, insId);
+            break;
+        }
+        if (timeOut)
+        {
+            requester.markFree(mctp_eid, insId);
+            break;
+        }
+        try
+        {
+            event1.run(std::nullopt);
+        }
+        catch (const sdeventplus::SdEventError& e)
+        {
+            std::cerr << "Failure in processing request.ERROR= " << e.what()
+                      << "\n";
+            return;
+        }
+    }
+}
+
+bool HostPDRHandler::isHostUp()
+{
+    return responseReceived;
+}
+
 } // namespace pldm
diff --git a/host-bmc/host_pdr_handler.hpp b/host-bmc/host_pdr_handler.hpp
index 2e0d331..96c0226 100644
--- a/host-bmc/host_pdr_handler.hpp
+++ b/host-bmc/host_pdr_handler.hpp
@@ -144,13 +144,6 @@
     void parseStateSensorPDRs(const PDRList& stateSensorPDRs,
                               const TLPDRMap& tlpdrInfo);
 
-  private:
-    /** @brief deferred function to fetch PDR from Host, scheduled to work on
-     *  the event loop. The PDR exchg with the host is async.
-     *  @param[in] source - sdeventplus event source
-     */
-    void _fetchPDR(sdeventplus::source::EventBase& source);
-
     /** @brief this function sends a GetPDR request to Host firmware.
      *  And processes the PDRs based on type
      *
@@ -158,6 +151,21 @@
      */
     void getHostPDR(uint32_t nextRecordHandle = 0);
 
+    /** @brief set the Host state when pldmd starts
+     */
+    void setHostState();
+
+    /** @brief check whether Host is running when pldmd starts
+     */
+    bool isHostUp();
+
+  private:
+    /** @brief deferred function to fetch PDR from Host, scheduled to work on
+     *  the event loop. The PDR exchg with the host is async.
+     *  @param[in] source - sdeventplus event source
+     */
+    void _fetchPDR(sdeventplus::source::EventBase& source);
+
     /** @brief Merge host firmware's entity association PDRs into BMC's
      *  @details A merge operation involves adding a pldm_entity under the
      *  appropriate parent, and updating container ids.
@@ -239,6 +247,13 @@
      */
     HostStateSensorMap sensorMap;
     bool verbose;
+
+    /** @brief whether response received from Host */
+    bool responseReceived;
+    /** @brief whether timed out waiting for a response from Host */
+    bool timeOut;
+    /** @brief request message instance id */
+    uint8_t insId;
 };
 
 } // namespace pldm
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index 58d71ce..00019f1 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -364,6 +364,19 @@
     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
     bus.request_name("xyz.openbmc_project.PLDM");
     IO io(event, socketFd(), EPOLLIN, std::move(callback));
+    hostPDRHandler->setHostState();
+    if (hostPDRHandler->isHostUp())
+    {
+        hostPDRHandler->getHostPDR();
+    }
+    else
+    {
+        if (verbose)
+        {
+            std::cout << "Host is not running\n";
+        }
+    }
+
     event.loop();
 
     result = shutdown(sockfd, SHUT_RDWR);