pldmd: implement a new handler registration scheme

Implement a PLDM request handler registration scheme that requires
handlers to be C++ objects rather than plain functions. This was needed
for a couple of reasons:

- Perform specific actions at the PLDM daemon startup (that's when the
  handlers are loaded).
- Share data across PLDM request messages (for eg FRU/BIOS tables),
  without having to resort to globals and statics.

Tested:
- existing unit tests still pass
- added tests for the new registration scheme

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I1cf1c0a6fccd15da54f08120e61a5f256df6bc36
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index ec168f4..be48950 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -2,6 +2,7 @@
 
 #include "config.h"
 
+#include "handler.hpp"
 #include "libpldmresponder/pdr.hpp"
 #include "libpldmresponder/utils.hpp"
 
@@ -14,254 +15,269 @@
 
 namespace pldm
 {
-
-using Response = std::vector<uint8_t>;
-
 namespace responder
 {
-
 namespace platform
 {
 
-/** @brief Register handlers for commands from the platform spec
- */
-void registerHandlers();
-
-} // namespace platform
-
-/** @brief Handler for GetPDR
- *
- *  @param[in] request - Request message payload
- *  @param[in] payloadLength - Request payload length
- *  @param[out] Response - Response message written here
- */
-Response getPDR(const pldm_msg* request, size_t payloadLength);
-
-/** @brief Handler for setStateEffecterStates
- *
- *  @param[in] request - Request message
- *  @param[in] payloadLength - Request payload length
- *  @return Response - PLDM Response message
- */
-Response setStateEffecterStates(const pldm_msg* request, size_t payloadLength);
-
-/** @brief Function to set the effecter requested by pldm requester
- *  @param[in] dBusIntf - The interface object
- *  @param[in] effecterId - Effecter ID sent by the requester to act on
- *  @param[in] stateField - The state field data for each of the states, equal
- *        to composite effecter count in number
- *  @return - Success or failure in setting the states. Returns failure in terms
- *        of PLDM completion codes if atleast one state fails to be set
- */
-template <class DBusInterface>
-int setStateEffecterStatesHandler(
-    const DBusInterface& dBusIntf, effecter::Id effecterId,
-    const std::vector<set_effecter_state_field>& stateField)
+class Handler : public CmdHandler
 {
-    using namespace std::string_literals;
-    using DBusProperty = std::variant<std::string, bool>;
-    using StateSetId = uint16_t;
-    using StateSetNum = uint8_t;
-    using PropertyMap =
-        std::map<StateSetId, std::map<StateSetNum, DBusProperty>>;
-    static const PropertyMap stateNumToDbusProp = {
-        {PLDM_BOOT_PROGRESS_STATE,
-         {{PLDM_BOOT_NOT_ACTIVE,
-           "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
-           "Standby"s},
-          {PLDM_BOOT_COMPLETED,
-           "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
-           "BootComplete"s}}},
-        {PLDM_SYSTEM_POWER_STATE,
-         {{PLDM_OFF_SOFT_GRACEFUL,
-           "xyz.openbmc_project.State.Chassis.Transition.Off"s}}}};
-    using namespace phosphor::logging;
-    using namespace pldm::responder::pdr;
-    using namespace pldm::responder::effecter::dbus_mapping;
-
-    state_effecter_possible_states* states = nullptr;
-    pldm_state_effecter_pdr* pdr = nullptr;
-    uint8_t compEffecterCnt = stateField.size();
-    uint32_t recordHndl{};
-    Repo& pdrRepo = get(PDR_JSONS_DIR);
-    pdr::Entry pdrEntry{};
-
-    while (!pdr)
+  public:
+    Handler()
     {
-        pdrEntry = pdrRepo.at(recordHndl);
-        pldm_pdr_hdr* header = reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
-        if (header->type != PLDM_STATE_EFFECTER_PDR)
-        {
-            recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
-            if (recordHndl)
-            {
-                continue;
-            }
-            return PLDM_PLATFORM_INVALID_EFFECTER_ID;
-        }
-        pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
-        recordHndl = pdr->hdr.record_handle;
-        if (pdr->effecter_id == effecterId)
-        {
-            states = reinterpret_cast<state_effecter_possible_states*>(
-                pdr->possible_states);
-            if (compEffecterCnt > pdr->composite_effecter_count)
-            {
-                log<level::ERR>("The requester sent wrong composite effecter "
-                                "count for the effecter",
-                                entry("EFFECTER_ID=%d", effecterId),
-                                entry("COMP_EFF_CNT=%d", compEffecterCnt));
-                return PLDM_ERROR_INVALID_DATA;
-            }
-            break;
-        }
-        recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
-        if (!recordHndl)
-        {
-            return PLDM_PLATFORM_INVALID_EFFECTER_ID;
-        }
-        pdr = nullptr;
+        handlers.emplace(PLDM_GET_PDR,
+                         [this](const pldm_msg* request, size_t payloadLength) {
+                             return this->getPDR(request, payloadLength);
+                         });
+        handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
+                         [this](const pldm_msg* request, size_t payloadLength) {
+                             return this->setStateEffecterStates(request,
+                                                                 payloadLength);
+                         });
     }
 
-    std::map<StateSetId, std::function<int(const std::string& objPath,
-                                           const uint8_t currState)>>
-        effecterToDbusEntries = {
-            {PLDM_BOOT_PROGRESS_STATE,
-             [&](const std::string& objPath, const uint8_t currState) {
-                 auto stateSet =
-                     stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
-                 if (stateSet == stateNumToDbusProp.end())
-                 {
-                     log<level::ERR>("Couldn't find D-Bus mapping for "
-                                     "PLDM_BOOT_PROGRESS_STATE",
-                                     entry("EFFECTER_ID=%d", effecterId));
-                     return PLDM_ERROR;
-                 }
-                 auto iter = stateSet->second.find(
-                     stateField[currState].effecter_state);
-                 if (iter == stateSet->second.end())
-                 {
-                     log<level::ERR>(
-                         "Invalid state field passed or field not "
-                         "found for PLDM_BOOT_PROGRESS_STATE",
-                         entry("EFFECTER_ID=%d", effecterId),
-                         entry("FIELD=%d",
-                               stateField[currState].effecter_state),
-                         entry("OBJECT_PATH=%s", objPath.c_str()));
-                     return PLDM_ERROR_INVALID_DATA;
-                 }
-                 auto dbusProp = "OperatingSystemState";
-                 std::variant<std::string> value{
-                     std::get<std::string>(iter->second)};
-                 auto dbusInterface =
-                     "xyz.openbmc_project.State.OperatingSystem.Status";
-                 try
-                 {
-                     dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
-                                              dbusInterface, value);
-                 }
-                 catch (const std::exception& e)
-                 {
-                     log<level::ERR>("Error setting property",
-                                     entry("ERROR=%s", e.what()),
-                                     entry("PROPERTY=%s", dbusProp),
-                                     entry("INTERFACE=%s", dbusInterface),
-                                     entry("PATH=%s", objPath.c_str()));
-                     return PLDM_ERROR;
-                 }
-                 return PLDM_SUCCESS;
-             }},
-            {PLDM_SYSTEM_POWER_STATE,
-             [&](const std::string& objPath, const uint8_t currState) {
-                 auto stateSet =
-                     stateNumToDbusProp.find(PLDM_SYSTEM_POWER_STATE);
-                 if (stateSet == stateNumToDbusProp.end())
-                 {
-                     log<level::ERR>("Couldn't find D-Bus mapping for "
-                                     "PLDM_SYSTEM_POWER_STATE",
-                                     entry("EFFECTER_ID=%d", effecterId));
-                     return PLDM_ERROR;
-                 }
-                 auto iter = stateSet->second.find(
-                     stateField[currState].effecter_state);
-                 if (iter == stateSet->second.end())
-                 {
-                     log<level::ERR>(
-                         "Invalid state field passed or field not "
-                         "found for PLDM_SYSTEM_POWER_STATE",
-                         entry("EFFECTER_ID=%d", effecterId),
-                         entry("FIELD=%d",
-                               stateField[currState].effecter_state),
-                         entry("OBJECT_PATH=%s", objPath.c_str()));
-                     return PLDM_ERROR_INVALID_DATA;
-                 }
-                 auto dbusProp = "RequestedPowerTransition";
-                 std::variant<std::string> value{
-                     std::get<std::string>(iter->second)};
-                 auto dbusInterface = "xyz.openbmc_project.State.Chassis";
-                 try
-                 {
-                     dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
-                                              dbusInterface, value);
-                 }
-                 catch (const std::exception& e)
-                 {
-                     log<level::ERR>("Error setting property",
-                                     entry("ERROR=%s", e.what()),
-                                     entry("PROPERTY=%s", dbusProp),
-                                     entry("INTERFACE=%s", dbusInterface),
-                                     entry("PATH=%s", objPath.c_str()));
-                     return PLDM_ERROR;
-                 }
-                 return PLDM_SUCCESS;
-             }}};
+    /** @brief Handler for GetPDR
+     *
+     *  @param[in] request - Request message payload
+     *  @param[in] payloadLength - Request payload length
+     *  @param[out] Response - Response message written here
+     */
+    Response getPDR(const pldm_msg* request, size_t payloadLength);
 
-    int rc = PLDM_SUCCESS;
-    auto paths = get(effecterId);
-    for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
+    /** @brief Handler for setStateEffecterStates
+     *
+     *  @param[in] request - Request message
+     *  @param[in] payloadLength - Request payload length
+     *  @return Response - PLDM Response message
+     */
+    Response setStateEffecterStates(const pldm_msg* request,
+                                    size_t payloadLength);
+
+    /** @brief Function to set the effecter requested by pldm requester
+     *  @param[in] dBusIntf - The interface object
+     *  @param[in] effecterId - Effecter ID sent by the requester to act on
+     *  @param[in] stateField - The state field data for each of the states,
+     * equal to composite effecter count in number
+     *  @return - Success or failure in setting the states. Returns failure in
+     * terms of PLDM completion codes if atleast one state fails to be set
+     */
+    template <class DBusInterface>
+    int setStateEffecterStatesHandler(
+        const DBusInterface& dBusIntf, effecter::Id effecterId,
+        const std::vector<set_effecter_state_field>& stateField)
     {
-        std::vector<StateSetNum> allowed{};
-        // computation is based on table 79 from DSP0248 v1.1.1
-        uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
-        uint8_t bit =
-            stateField[currState].effecter_state - (8 * bitfieldIndex);
-        if (states->possible_states_size < bitfieldIndex ||
-            !(states->states[bitfieldIndex].byte & (1 << bit)))
+        using namespace std::string_literals;
+        using DBusProperty = std::variant<std::string, bool>;
+        using StateSetId = uint16_t;
+        using StateSetNum = uint8_t;
+        using PropertyMap =
+            std::map<StateSetId, std::map<StateSetNum, DBusProperty>>;
+        static const PropertyMap stateNumToDbusProp = {
+            {PLDM_BOOT_PROGRESS_STATE,
+             {{PLDM_BOOT_NOT_ACTIVE,
+               "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
+               "Standby"s},
+              {PLDM_BOOT_COMPLETED,
+               "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
+               "BootComplete"s}}},
+            {PLDM_SYSTEM_POWER_STATE,
+             {{PLDM_OFF_SOFT_GRACEFUL,
+               "xyz.openbmc_project.State.Chassis.Transition.Off"s}}}};
+        using namespace phosphor::logging;
+        using namespace pldm::responder::pdr;
+        using namespace pldm::responder::effecter::dbus_mapping;
+
+        state_effecter_possible_states* states = nullptr;
+        pldm_state_effecter_pdr* pdr = nullptr;
+        uint8_t compEffecterCnt = stateField.size();
+        uint32_t recordHndl{};
+        Repo& pdrRepo = get(PDR_JSONS_DIR);
+        pdr::Entry pdrEntry{};
+
+        while (!pdr)
         {
-            log<level::ERR>(
-                "Invalid state set value", entry("EFFECTER_ID=%d", effecterId),
-                entry("VALUE=%d", stateField[currState].effecter_state),
-                entry("COMPOSITE_EFFECTER_ID=%d", currState),
-                entry("DBUS_PATH=%c", paths[currState].c_str()));
-            rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
-            break;
-        }
-        auto iter = effecterToDbusEntries.find(states->state_set_id);
-        if (iter == effecterToDbusEntries.end())
-        {
-            uint16_t setId = states->state_set_id;
-            log<level::ERR>(
-                "Did not find the state set for the state effecter pdr  ",
-                entry("STATE=%d", setId), entry("EFFECTER_ID=%d", effecterId));
-            rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
-            break;
-        }
-        if (stateField[currState].set_request == PLDM_REQUEST_SET)
-        {
-            rc = iter->second(paths[currState], currState);
-            if (rc != PLDM_SUCCESS)
+            pdrEntry = pdrRepo.at(recordHndl);
+            pldm_pdr_hdr* header =
+                reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
+            if (header->type != PLDM_STATE_EFFECTER_PDR)
             {
+                recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
+                if (recordHndl)
+                {
+                    continue;
+                }
+                return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+            }
+            pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
+            recordHndl = pdr->hdr.record_handle;
+            if (pdr->effecter_id == effecterId)
+            {
+                states = reinterpret_cast<state_effecter_possible_states*>(
+                    pdr->possible_states);
+                if (compEffecterCnt > pdr->composite_effecter_count)
+                {
+                    log<level::ERR>(
+                        "The requester sent wrong composite effecter "
+                        "count for the effecter",
+                        entry("EFFECTER_ID=%d", effecterId),
+                        entry("COMP_EFF_CNT=%d", compEffecterCnt));
+                    return PLDM_ERROR_INVALID_DATA;
+                }
                 break;
             }
+            recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
+            if (!recordHndl)
+            {
+                return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+            }
+            pdr = nullptr;
         }
-        uint8_t* nextState =
-            reinterpret_cast<uint8_t*>(states) +
-            sizeof(state_effecter_possible_states) - sizeof(states->states) +
-            (states->possible_states_size * sizeof(states->states));
-        states = reinterpret_cast<state_effecter_possible_states*>(nextState);
-    }
-    return rc;
-}
 
+        std::map<StateSetId, std::function<int(const std::string& objPath,
+                                               const uint8_t currState)>>
+            effecterToDbusEntries = {
+                {PLDM_BOOT_PROGRESS_STATE,
+                 [&](const std::string& objPath, const uint8_t currState) {
+                     auto stateSet =
+                         stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
+                     if (stateSet == stateNumToDbusProp.end())
+                     {
+                         log<level::ERR>("Couldn't find D-Bus mapping for "
+                                         "PLDM_BOOT_PROGRESS_STATE",
+                                         entry("EFFECTER_ID=%d", effecterId));
+                         return PLDM_ERROR;
+                     }
+                     auto iter = stateSet->second.find(
+                         stateField[currState].effecter_state);
+                     if (iter == stateSet->second.end())
+                     {
+                         log<level::ERR>(
+                             "Invalid state field passed or field not "
+                             "found for PLDM_BOOT_PROGRESS_STATE",
+                             entry("EFFECTER_ID=%d", effecterId),
+                             entry("FIELD=%d",
+                                   stateField[currState].effecter_state),
+                             entry("OBJECT_PATH=%s", objPath.c_str()));
+                         return PLDM_ERROR_INVALID_DATA;
+                     }
+                     auto dbusProp = "OperatingSystemState";
+                     std::variant<std::string> value{
+                         std::get<std::string>(iter->second)};
+                     auto dbusInterface =
+                         "xyz.openbmc_project.State.OperatingSystem.Status";
+                     try
+                     {
+                         dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
+                                                  dbusInterface, value);
+                     }
+                     catch (const std::exception& e)
+                     {
+                         log<level::ERR>("Error setting property",
+                                         entry("ERROR=%s", e.what()),
+                                         entry("PROPERTY=%s", dbusProp),
+                                         entry("INTERFACE=%s", dbusInterface),
+                                         entry("PATH=%s", objPath.c_str()));
+                         return PLDM_ERROR;
+                     }
+                     return PLDM_SUCCESS;
+                 }},
+                {PLDM_SYSTEM_POWER_STATE,
+                 [&](const std::string& objPath, const uint8_t currState) {
+                     auto stateSet =
+                         stateNumToDbusProp.find(PLDM_SYSTEM_POWER_STATE);
+                     if (stateSet == stateNumToDbusProp.end())
+                     {
+                         log<level::ERR>("Couldn't find D-Bus mapping for "
+                                         "PLDM_SYSTEM_POWER_STATE",
+                                         entry("EFFECTER_ID=%d", effecterId));
+                         return PLDM_ERROR;
+                     }
+                     auto iter = stateSet->second.find(
+                         stateField[currState].effecter_state);
+                     if (iter == stateSet->second.end())
+                     {
+                         log<level::ERR>(
+                             "Invalid state field passed or field not "
+                             "found for PLDM_SYSTEM_POWER_STATE",
+                             entry("EFFECTER_ID=%d", effecterId),
+                             entry("FIELD=%d",
+                                   stateField[currState].effecter_state),
+                             entry("OBJECT_PATH=%s", objPath.c_str()));
+                         return PLDM_ERROR_INVALID_DATA;
+                     }
+                     auto dbusProp = "RequestedPowerTransition";
+                     std::variant<std::string> value{
+                         std::get<std::string>(iter->second)};
+                     auto dbusInterface = "xyz.openbmc_project.State.Chassis";
+                     try
+                     {
+                         dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
+                                                  dbusInterface, value);
+                     }
+                     catch (const std::exception& e)
+                     {
+                         log<level::ERR>("Error setting property",
+                                         entry("ERROR=%s", e.what()),
+                                         entry("PROPERTY=%s", dbusProp),
+                                         entry("INTERFACE=%s", dbusInterface),
+                                         entry("PATH=%s", objPath.c_str()));
+                         return PLDM_ERROR;
+                     }
+                     return PLDM_SUCCESS;
+                 }}};
+
+        int rc = PLDM_SUCCESS;
+        auto paths = get(effecterId);
+        for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
+        {
+            std::vector<StateSetNum> allowed{};
+            // computation is based on table 79 from DSP0248 v1.1.1
+            uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
+            uint8_t bit =
+                stateField[currState].effecter_state - (8 * bitfieldIndex);
+            if (states->possible_states_size < bitfieldIndex ||
+                !(states->states[bitfieldIndex].byte & (1 << bit)))
+            {
+                log<level::ERR>(
+                    "Invalid state set value",
+                    entry("EFFECTER_ID=%d", effecterId),
+                    entry("VALUE=%d", stateField[currState].effecter_state),
+                    entry("COMPOSITE_EFFECTER_ID=%d", currState),
+                    entry("DBUS_PATH=%c", paths[currState].c_str()));
+                rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
+                break;
+            }
+            auto iter = effecterToDbusEntries.find(states->state_set_id);
+            if (iter == effecterToDbusEntries.end())
+            {
+                uint16_t setId = states->state_set_id;
+                log<level::ERR>(
+                    "Did not find the state set for the state effecter pdr  ",
+                    entry("STATE=%d", setId),
+                    entry("EFFECTER_ID=%d", effecterId));
+                rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
+                break;
+            }
+            if (stateField[currState].set_request == PLDM_REQUEST_SET)
+            {
+                rc = iter->second(paths[currState], currState);
+                if (rc != PLDM_SUCCESS)
+                {
+                    break;
+                }
+            }
+            uint8_t* nextState =
+                reinterpret_cast<uint8_t*>(states) +
+                sizeof(state_effecter_possible_states) -
+                sizeof(states->states) +
+                (states->possible_states_size * sizeof(states->states));
+            states =
+                reinterpret_cast<state_effecter_possible_states*>(nextState);
+        }
+        return rc;
+    }
+};
+
+} // namespace platform
 } // namespace responder
 } // namespace pldm