Softoff: Add config support for PDR entities

This commit introduces a config file for the soft power off
app. The config file specifies the order of remote PDRs to
be checked during the soft power off process.

Previously, the entity type was hardcoded in the codebase,
limiting scalability. With this change, the softoff app gains
flexibility for future expansions.

TESTED: By running 'obmcutil poweroff' with various remote
entities during boot-up.

Signed-off-by: Sagar Srinivas <sagar.srinivas@ibm.com>
Change-Id: Ib8d2aba6a05fc4e9da4b042fd67890e6282ed784
diff --git a/configurations/meson.build b/configurations/meson.build
index caf5aab..920d3c0 100644
--- a/configurations/meson.build
+++ b/configurations/meson.build
@@ -4,6 +4,8 @@
 
 install_subdir('events', install_dir: package_datadir)
 
+install_subdir('softoff', install_dir: package_datadir)
+
 if get_option('oem-ibm').disabled()
 install_data('fru_master.json', install_dir: package_datadir)
 endif
diff --git a/configurations/softoff/softoff_config.json b/configurations/softoff/softoff_config.json
new file mode 100644
index 0000000..73d4f9c
--- /dev/null
+++ b/configurations/softoff/softoff_config.json
@@ -0,0 +1,14 @@
+{
+    "entries": [
+        {
+            "tid": 208,
+            "entityType": 32801,
+            "stateSetId": 129
+        },
+        {
+            "tid": 2,
+            "entityType": 45,
+            "stateSetId": 129
+        }
+    ]
+}
diff --git a/meson.build b/meson.build
index 6f96291..f64e718 100644
--- a/meson.build
+++ b/meson.build
@@ -47,6 +47,7 @@
 endif
 if get_option('softoff').allowed()
   conf_data.set('SOFTOFF_TIMEOUT_SECONDS', get_option('softoff-timeout-seconds'))
+  conf_data.set_quoted('SOFTOFF_CONFIG_JSON', join_paths(package_datadir, 'softoff'))
 endif
 if get_option('oem-ibm').allowed()
   conf_data.set_quoted('FILE_TABLE_JSON', join_paths(package_datadir, 'fileTable.json'))
diff --git a/softoff/softoff.cpp b/softoff/softoff.cpp
index 6f24991..83d46f0 100644
--- a/softoff/softoff.cpp
+++ b/softoff/softoff.cpp
@@ -16,6 +16,8 @@
 #include <sdeventplus/source/time.hpp>
 
 #include <array>
+#include <filesystem>
+#include <fstream>
 
 PHOSPHOR_LOG2_USING;
 
@@ -23,11 +25,12 @@
 {
 using namespace sdeventplus;
 using namespace sdeventplus::source;
+namespace fs = std::filesystem;
 constexpr auto clockId = sdeventplus::ClockId::RealTime;
 using Clock = Clock<clockId>;
 using Timer = Time<clockId>;
 
-constexpr pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
+pldm::pdr::TerminusID TID = 0; // TID will be implemented later.
 namespace sdbusRule = sdbusplus::bus::match::rules;
 
 SoftPowerOff::SoftPowerOff(sdbusplus::bus_t& bus, sd_event* event,
@@ -35,31 +38,44 @@
     bus(bus),
     timer(event), instanceIdDb(instanceIdDb)
 {
+    auto jsonData = parseConfig();
+
+    if (jsonData.is_discarded())
+    {
+        error("Parsing softoff config JSON file failed");
+        return;
+    }
+
     getHostState();
     if (hasError || completed)
     {
         return;
     }
+    const std::vector<Json> emptyJsonList{};
+    auto entries = jsonData.value("entries", emptyJsonList);
+    for (const auto& entry : entries)
+    {
+        TID = entry.value("tid", 0);
+        pldm::pdr::EntityType entityType = entry.value("entityType", 0);
+        pldm::pdr::StateSetId stateSetId = entry.value("stateSetId", 0);
 
-    auto rc = getEffecterID();
-    if (completed)
-    {
-        error("pldm-softpoweroff: effecter to initiate softoff not found");
-        return;
-    }
-    else if (rc != PLDM_SUCCESS)
-    {
-        hasError = true;
-        return;
-    }
-
-    rc = getSensorInfo();
-    if (rc != PLDM_SUCCESS)
-    {
-        error("Message get Sensor PDRs error. PLDM error code = {RC}", "RC",
-              lg2::hex, static_cast<int>(rc));
-        hasError = true;
-        return;
+        bool effecterFound = getEffecterID(entityType, stateSetId);
+        if (effecterFound)
+        {
+            auto rc = getSensorInfo(entityType, stateSetId);
+            if (rc != PLDM_SUCCESS)
+            {
+                error("Message get Sensor PDRs error. PLDM error code = {RC}",
+                      "RC", lg2::hex, static_cast<int>(rc));
+                hasError = true;
+                return;
+            }
+            break;
+        }
+        else
+        {
+            continue;
+        }
     }
 
     // Matches on the pldm StateSensorEvent signal
@@ -130,103 +146,61 @@
     }
 }
 
-int SoftPowerOff::getEffecterID()
+Json SoftPowerOff::parseConfig()
+{
+    fs::path softoffConfigJson(fs::path(SOFTOFF_CONFIG_JSON) /
+                               "softoff_config.json");
+
+    if (!fs::exists(softoffConfigJson) || fs::is_empty(softoffConfigJson))
+    {
+        error("Parsing softoff config JSON file failed, File does not exist");
+        return PLDM_ERROR;
+    }
+
+    std::ifstream jsonFile(softoffConfigJson);
+    return Json::parse(jsonFile);
+}
+
+bool SoftPowerOff::getEffecterID(pldm::pdr::EntityType& entityType,
+                                 pldm::pdr::StateSetId& stateSetId)
 {
     auto& bus = pldm::utils::DBusHandler::getBus();
-
-    // VMM is a logical entity, so the bit 15 in entity type is set.
-    pdr::EntityType entityType = PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER | 0x8000;
-
     try
     {
-        std::vector<std::vector<uint8_t>> VMMResponse{};
-        auto VMMMethod = bus.new_method_call(
+        std::vector<std::vector<uint8_t>> response{};
+        auto method = bus.new_method_call(
             "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
             "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
-        VMMMethod.append(TID, entityType,
-                         (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
+        method.append(TID, entityType, stateSetId);
+        auto responseMsg = bus.call(method, dbusTimeout);
 
-        auto VMMResponseMsg = bus.call(VMMMethod, dbusTimeout);
-
-        VMMResponseMsg.read(VMMResponse);
-        if (VMMResponse.size() != 0)
+        responseMsg.read(response);
+        if (response.size())
         {
-            for (auto& rep : VMMResponse)
+            for (auto& rep : response)
             {
-                auto VMMPdr =
+                auto softoffPdr =
                     reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
-                effecterID = VMMPdr->effecter_id;
+                effecterID = softoffPdr->effecter_id;
             }
         }
         else
         {
-            VMMPdrExist = false;
+            return false;
         }
     }
     catch (const sdbusplus::exception_t& e)
     {
-        error("PLDM soft off: Error get VMM PDR,ERROR={ERR_EXCEP}", "ERR_EXCEP",
-              e.what());
-        VMMPdrExist = false;
-    }
-
-    if (VMMPdrExist)
-    {
-        return PLDM_SUCCESS;
-    }
-
-    // If the Virtual Machine Manager PDRs doesn't exist, go find the System
-    // Firmware PDRs.
-    // System Firmware is a logical entity, so the bit 15 in entity type is set
-    entityType = PLDM_ENTITY_SYS_FIRMWARE | 0x8000;
-    try
-    {
-        std::vector<std::vector<uint8_t>> sysFwResponse{};
-        auto sysFwMethod = bus.new_method_call(
-            "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
-            "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
-        sysFwMethod.append(TID, entityType,
-                           (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
-
-        auto sysFwResponseMsg = bus.call(sysFwMethod, dbusTimeout);
-
-        sysFwResponseMsg.read(sysFwResponse);
-
-        if (sysFwResponse.size() == 0)
-        {
-            error("No effecter ID has been found that matches the criteria");
-            return PLDM_ERROR;
-        }
-
-        for (auto& rep : sysFwResponse)
-        {
-            auto sysFwPdr =
-                reinterpret_cast<pldm_state_effecter_pdr*>(rep.data());
-            effecterID = sysFwPdr->effecter_id;
-        }
-    }
-    catch (const sdbusplus::exception_t& e)
-    {
-        error("PLDM soft off: Error get system firmware PDR,ERROR={ERR_EXCEP}",
+        error("PLDM soft off: Error get softPowerOff PDR,ERROR={ERR_EXCEP}",
               "ERR_EXCEP", e.what());
-        completed = true;
-        return PLDM_ERROR;
+        return false;
     }
-
-    return PLDM_SUCCESS;
+    return true;
 }
 
-int SoftPowerOff::getSensorInfo()
+int SoftPowerOff::getSensorInfo(pldm::pdr::EntityType& entityType,
+                                pldm::pdr::StateSetId& stateSetId)
 {
-    pldm::pdr::EntityType entityType;
-
-    entityType = VMMPdrExist ? PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER
-                             : PLDM_ENTITY_SYS_FIRMWARE;
-
-    // The Virtual machine manager/System firmware is logical entity, so bit 15
-    // need to be set.
-    entityType = entityType | 0x8000;
-
     try
     {
         auto& bus = pldm::utils::DBusHandler::getBus();
@@ -234,8 +208,7 @@
         auto method = bus.new_method_call(
             "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
             "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
-        method.append(TID, entityType,
-                      (uint16_t)PLDM_STATE_SET_SW_TERMINATION_STATUS);
+        method.append(TID, entityType, stateSetId);
 
         auto ResponseMsg = bus.call(method, dbusTimeout);
 
diff --git a/softoff/softoff.hpp b/softoff/softoff.hpp
index edec74e..866784b 100644
--- a/softoff/softoff.hpp
+++ b/softoff/softoff.hpp
@@ -4,6 +4,7 @@
 #include "common/transport.hpp"
 #include "common/types.hpp"
 
+#include <nlohmann/json.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server.hpp>
 #include <sdbusplus/server/object.hpp>
@@ -12,6 +13,7 @@
 
 namespace pldm
 {
+using Json = nlohmann::json;
 
 /** @class SoftPowerOff
  *  @brief Responsible for coordinating Host SoftPowerOff operation
@@ -79,6 +81,12 @@
         return timer.stop();
     }
 
+    /** @brief method to parse the config Json file for softoff
+     *
+     *  @return Json - Json object of
+     */
+    Json parseConfig();
+
     /** @brief When host soft off completed, stop the timer and
      *         set the completed to true.
      *
@@ -96,15 +104,25 @@
 
     /** @brief Get effecterID from PDRs.
      *
-     *  @return PLDM_SUCCESS or PLDM_ERROR
+     *  @param[in] entityType - entity type of the entity hosting
+     *                              hosting softoff PDR
+     *  @param[in] stateSetId - state set ID of the softoff PDR
+     *
+     *  @return true or false
      */
-    int getEffecterID();
+    bool getEffecterID(pldm::pdr::EntityType& entityType,
+                       pldm::pdr::StateSetId& stateSetId);
 
     /** @brief Get VMM/SystemFirmware Sensor info from PDRs.
      *
+     *  @param[in] entityType - entity type of the entity hosting
+     *                              hosting softoff PDR
+     *  @param[in] stateSetId - state set ID of the softoff PDR
+     *
      *  @return PLDM_SUCCESS or PLDM_ERROR
      */
-    int getSensorInfo();
+    int getSensorInfo(pldm::pdr::EntityType& entityType,
+                      pldm::pdr::StateSetId& stateSetId);
 
     /** @brief effecterID
      */
@@ -130,10 +148,6 @@
      */
     bool responseReceived = false;
 
-    /** @brief Is the Virtual Machine Manager/VMM state effecter available.
-     */
-    bool VMMPdrExist = true;
-
     /* @brief sdbusplus handle */
     sdbusplus::bus_t& bus;