Build FRU table lazily

The FRU table is created when the PLDM daemon starts and depends on
BMC inventory collection to populate it completely. There is no D-Bus
signal or target that PLDM daemon can rely on to figure completion of
inventory. This can cause some of the inventory to not show up in the
FRU table if the inventory collection is not complete when the PLDM
daemon starts.

An easy solution to this problem is to do the FRU table creation
lazily. The FRU table will be created when the FRU commands or Get PDR
command is handled the first time. The entity association PDR's are
created when the FRU table is built. So Get PDR commands expects the
building of the FRU table as a pre condition.

Tested:

Ensured FRU table is created when the GetFRURecordTableMetadata,
GetFRURecordTable and GetPDR command is handled by the PLDM daemon the
first time. Next time already built table is returned for the FRU
commands.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Change-Id: I0deb723f30a30a667d0e80c9f9f6aced5ab23a67
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index a9c8787..ce470ab 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -17,12 +17,13 @@
 namespace responder
 {
 
-FruImpl::FruImpl(const std::string& configPath, pldm_pdr* pdrRepo,
-                 pldm_entity_association_tree* entityTree) :
-    pdrRepo(pdrRepo),
-    entityTree(entityTree)
+void FruImpl::buildFRUTable()
 {
-    fru_parser::FruParser handle(configPath);
+
+    if (isBuilt)
+    {
+        return;
+    }
 
     fru_parser::DBusLookupInfo dbusInfo;
     // Read the all the inventory D-Bus objects
@@ -31,7 +32,7 @@
 
     try
     {
-        dbusInfo = handle.inventoryLookup();
+        dbusInfo = parser.inventoryLookup();
         auto method = bus.new_method_call(
             std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(),
             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
@@ -61,7 +62,7 @@
                 try
                 {
                     pldm_entity entity{};
-                    entity.entity_type = handle.getEntityType(interface.first);
+                    entity.entity_type = parser.getEntityType(interface.first);
                     pldm_entity_node* parent = nullptr;
                     auto parentObj = pldm::utils::findParent(object.first.str);
                     // To add a FRU to the entity association tree, we need to
@@ -90,7 +91,7 @@
                         PLDM_ENTITY_ASSOCIAION_PHYSICAL);
                     objToEntityNode[object.first.str] = node;
 
-                    auto recordInfos = handle.getRecordInfo(interface.first);
+                    auto recordInfos = parser.getRecordInfo(interface.first);
                     populateRecords(interfaces, recordInfos, entity);
                     break;
                 }
@@ -115,6 +116,7 @@
         // Calculate the checksum
         checksum = crc32(table.data(), table.size());
     }
+    isBuilt = true;
 }
 
 void FruImpl::populateRecords(
@@ -208,6 +210,9 @@
 Response Handler::getFRURecordTableMetadata(const pldm_msg* request,
                                             size_t /*payloadLength*/)
 {
+    // FRU table is built lazily, build if not done.
+    buildFRUTable();
+
     constexpr uint8_t major = 0x01;
     constexpr uint8_t minor = 0x00;
     constexpr uint32_t maxSize = 0xFFFFFFFF;
@@ -232,6 +237,9 @@
 Response Handler::getFRURecordTable(const pldm_msg* request,
                                     size_t payloadLength)
 {
+    // FRU table is built lazily, build if not done.
+    buildFRUTable();
+
     if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES)
     {
         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
diff --git a/libpldmresponder/fru.hpp b/libpldmresponder/fru.hpp
index e23cd48..2597dc4 100644
--- a/libpldmresponder/fru.hpp
+++ b/libpldmresponder/fru.hpp
@@ -49,15 +49,19 @@
         sizeof(struct pldm_fru_record_data_format) -
         sizeof(struct pldm_fru_record_tlv);
 
-    /** @brief The FRU table is populated by processing the D-Bus inventory
-     *         namespace, based on the config files for FRU. The configPath is
-     *         consumed to build the FruParser object.
+    /** @brief Constructor for FruImpl, the configPath is consumed to build the
+     *         FruParser object.
      *
      *  @param[in] configPath - path to the directory containing config files
-     * for PLDM FRU
+     *                          for PLDM FRU
+     *  @param[in] pdrRepo - opaque pointer to PDR repository
+     *  @param[in] entityTree - opaque pointer to the entity association tree
      */
     FruImpl(const std::string& configPath, pldm_pdr* pdrRepo,
-            pldm_entity_association_tree* entityTree);
+            pldm_entity_association_tree* entityTree) :
+        parser(configPath),
+        pdrRepo(pdrRepo), entityTree(entityTree)
+    {}
 
     /** @brief Total length of the FRU table in bytes, this excludes the pad
      *         bytes and the checksum.
@@ -102,6 +106,12 @@
      */
     void getFRUTable(Response& response);
 
+    /** @brief FRU table is built by processing the D-Bus inventory namespace
+     *         based on the config files for FRU. The table is populated based
+     *         on the isBuilt flag.
+     */
+    void buildFRUTable();
+
   private:
     uint16_t nextRSI()
     {
@@ -113,7 +123,9 @@
     uint8_t padBytes = 0;
     std::vector<uint8_t> table;
     uint32_t checksum = 0;
+    bool isBuilt = false;
 
+    fru_parser::FruParser parser;
     pldm_pdr* pdrRepo;
     pldm_entity_association_tree* entityTree;
 
@@ -175,6 +187,14 @@
      */
     Response getFRURecordTable(const pldm_msg* request, size_t payloadLength);
 
+    /** @brief Build FRU table is bnot already built
+     *
+     */
+    void buildFRUTable()
+    {
+        impl.buildFRUTable();
+    }
+
   private:
     FruImpl impl;
 };
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index 1e13754..17b5095 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -125,6 +125,13 @@
 
 Response Handler::getPDR(const pldm_msg* request, size_t payloadLength)
 {
+    // Build FRU table if not built, since entity association PDR's are built
+    // when the FRU table is constructed.
+    if (fruHandler)
+    {
+        fruHandler->buildFRUTable();
+    }
+
     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES, 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 40f9214..ad635bd 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -7,6 +7,7 @@
 
 #include "common/utils.hpp"
 #include "event_parser.hpp"
+#include "fru.hpp"
 #include "host-bmc/host_pdr_handler.hpp"
 #include "libpldmresponder/pdr.hpp"
 #include "libpldmresponder/pdr_utils.hpp"
@@ -56,9 +57,11 @@
   public:
     Handler(const std::string& pdrJsonsDir, const std::string& eventsJsonsDir,
             pldm_pdr* repo, HostPDRHandler* hostPDRHandler,
+            fru::Handler* fruHandler,
             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
         pdrRepo(repo),
-        hostPDRHandler(hostPDRHandler), stateSensorHandler(eventsJsonsDir)
+        hostPDRHandler(hostPDRHandler), stateSensorHandler(eventsJsonsDir),
+        fruHandler(fruHandler)
     {
         generate(pdrJsonsDir, pdrRepo);
 
@@ -396,6 +399,7 @@
     DbusObjMaps dbusObjMaps{};
     HostPDRHandler* hostPDRHandler;
     events::StateSensorHandler stateSensorHandler;
+    fru::Handler* fruHandler;
 };
 
 } // namespace platform
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index b2912bc..fa1c89c 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -186,13 +186,16 @@
     Invoker invoker{};
     invoker.registerHandler(PLDM_BASE, std::make_unique<base::Handler>());
     invoker.registerHandler(PLDM_BIOS, std::make_unique<bios::Handler>());
+    auto fruHandler = std::make_unique<fru::Handler>(
+        FRU_JSONS_DIR, pdrRepo.get(), entityTree.get());
+    // FRU table is built lazily when a FRU command or Get PDR command is
+    // handled. To enable building FRU table, the FRU handler is passed to the
+    // Platform handler.
     invoker.registerHandler(PLDM_PLATFORM,
                             std::make_unique<platform::Handler>(
                                 PDR_JSONS_DIR, EVENTS_JSONS_DIR, pdrRepo.get(),
-                                hostPDRHandler.get()));
-    invoker.registerHandler(
-        PLDM_FRU, std::make_unique<fru::Handler>(FRU_JSONS_DIR, pdrRepo.get(),
-                                                 entityTree.get()));
+                                hostPDRHandler.get(), fruHandler.get()));
+    invoker.registerHandler(PLDM_FRU, std::move(fruHandler));
 
 #ifdef OEM_IBM
     invoker.registerHandler(PLDM_OEM, std::make_unique<oem_ibm::Handler>());
diff --git a/test/libpldmresponder_pdr_effecter_test.cpp b/test/libpldmresponder_pdr_effecter_test.cpp
index 9d98c1f..6e36b3b 100644
--- a/test/libpldmresponder_pdr_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_effecter_test.cpp
@@ -16,7 +16,7 @@
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    inPDRRepo, nullptr);
+                    inPDRRepo, nullptr, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
 
@@ -109,7 +109,8 @@
     auto inPDRRepo = pldm_pdr_init();
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr);
+    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr,
+                    nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_NUMERIC_EFFECTER_PDR);
 
@@ -149,7 +150,7 @@
     auto pdrRepo = pldm_pdr_init();
 
     ASSERT_THROW(Handler("./pdr_jsons/not_there", "./event_jsons/not_there",
-                         pdrRepo, nullptr),
+                         pdrRepo, nullptr, nullptr),
                  std::exception);
 
     pldm_pdr_destroy(pdrRepo);
@@ -161,7 +162,7 @@
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    inPDRRepo, nullptr);
+                    inPDRRepo, nullptr, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
 
@@ -176,7 +177,8 @@
 TEST(findStateEffecterId, goodJson)
 {
     auto inPDRRepo = pldm_pdr_init();
-    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr);
+    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr,
+                    nullptr);
     uint16_t entityType = 33;
     uint16_t entityInstance = 0;
     uint16_t containerId = 0;
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index fdb5544..60df7bf 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -28,7 +28,7 @@
 
     auto pdrRepo = pldm_pdr_init();
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    pdrRepo, nullptr);
+                    pdrRepo, nullptr, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -60,7 +60,7 @@
 
     auto pdrRepo = pldm_pdr_init();
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    pdrRepo, nullptr);
+                    pdrRepo, nullptr, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -86,7 +86,7 @@
 
     auto pdrRepo = pldm_pdr_init();
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    pdrRepo, nullptr);
+                    pdrRepo, nullptr, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -110,7 +110,7 @@
 
     auto pdrRepo = pldm_pdr_init();
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    pdrRepo, nullptr);
+                    pdrRepo, nullptr, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -136,7 +136,7 @@
 
     auto pdrRepo = pldm_pdr_init();
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    pdrRepo, nullptr);
+                    pdrRepo, nullptr, nullptr);
     Repo repo(pdrRepo);
     ASSERT_EQ(repo.empty(), false);
     auto response = handler.getPDR(req, requestPayloadLength);
@@ -188,7 +188,7 @@
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    inPDRRepo, nullptr);
+                    inPDRRepo, nullptr, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
@@ -224,7 +224,7 @@
     auto outPDRRepo = pldm_pdr_init();
     Repo outRepo(outPDRRepo);
     Handler handler("./pdr_jsons/state_effecter/good", "./event_jsons/good",
-                    inPDRRepo, nullptr);
+                    inPDRRepo, nullptr, nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, outRepo, PLDM_STATE_EFFECTER_PDR);
     pdr_utils::PdrEntry e;
@@ -263,7 +263,8 @@
     auto inPDRRepo = pldm_pdr_init();
     auto numericEffecterPdrRepo = pldm_pdr_init();
     Repo numericEffecterPDRs(numericEffecterPdrRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr);
+    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr,
+                    nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);
 
@@ -300,7 +301,8 @@
     auto inPDRRepo = pldm_pdr_init();
     auto numericEffecterPdrRepo = pldm_pdr_init();
     Repo numericEffecterPDRs(numericEffecterPdrRepo);
-    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr);
+    Handler handler("./pdr_jsons/state_effecter/good", "", inPDRRepo, nullptr,
+                    nullptr);
     Repo inRepo(inPDRRepo);
     getRepoByType(inRepo, numericEffecterPDRs, PLDM_NUMERIC_EFFECTER_PDR);