Revamped code for VPD parser
The commit removes all the pre-existing code from the branch
and pushes the revamped code.
Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.
Note: This code supports all the existing functionalities as it is.
Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/include/utility/common_utility.hpp b/vpd-manager/include/utility/common_utility.hpp
new file mode 100644
index 0000000..15b33a2
--- /dev/null
+++ b/vpd-manager/include/utility/common_utility.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+/**
+ * @brief Namespace to host common utility methods.
+ *
+ * A method qualifies as a common utility function if,
+ * A)It is being used by the utility namespace at the same level as well as
+ * other files directly.
+ * B) The utility should be a leaf node and should not be dependent on any other
+ * utility.
+ * *******************
+ * | Commmon Utility | - - - - - - -
+ * ******************* |
+ * /\ |
+ * / \ |
+ * **************** **************** |
+ * | json utility | | dbus utility | |
+ * **************** **************** |
+ * \ / |
+ * \ / |
+ * ************************ |
+ * | Vpd specific Utility | - - - - - - -
+ * ************************
+ */
+
+namespace vpd
+{
+
+namespace commonUtility
+{
+/** @brief Return the hex representation of the incoming byte.
+ *
+ * @param [in] i_aByte - The input byte.
+ * @returns Hex representation of the byte as a character.
+ */
+constexpr auto toHex(size_t i_aByte)
+{
+ constexpr auto l_map = "0123456789abcdef";
+ return l_map[i_aByte];
+}
+
+/**
+ * @brief API to return null at the end of variadic template args.
+ *
+ * @return empty string.
+ */
+inline std::string getCommand()
+{
+ return "";
+}
+
+/**
+ * @brief API to arrange create command.
+ *
+ * @param[in] arguments to create the command
+ * @return Command string
+ */
+template <typename T, typename... Types>
+inline std::string getCommand(T i_arg1, Types... i_args)
+{
+ std::string l_cmd = " " + i_arg1 + getCommand(i_args...);
+
+ return l_cmd;
+}
+
+/**
+ * @brief API to create shell command and execute.
+ *
+ * @throw std::runtime_error.
+ *
+ * @param[in] arguments for command
+ * @returns output of that command
+ */
+template <typename T, typename... Types>
+inline std::vector<std::string> executeCmd(T&& i_path, Types... i_args)
+{
+ std::vector<std::string> l_cmdOutput;
+ std::array<char, constants::CMD_BUFFER_LENGTH> l_buffer;
+
+ std::string l_cmd = i_path + getCommand(i_args...);
+
+ std::unique_ptr<FILE, decltype(&pclose)> l_cmdPipe(
+ popen(l_cmd.c_str(), "r"), pclose);
+
+ if (!l_cmdPipe)
+ {
+ logging::logMessage(
+ "popen failed with error " + std::string(strerror(errno)));
+ throw std::runtime_error("popen failed!");
+ }
+ while (fgets(l_buffer.data(), l_buffer.size(), l_cmdPipe.get()) != nullptr)
+ {
+ l_cmdOutput.emplace_back(l_buffer.data());
+ }
+
+ return l_cmdOutput;
+}
+
+/** @brief Converts string to lower case.
+ *
+ * @param [in] i_string - Input string.
+ */
+inline void toLower(std::string& i_string)
+{
+ std::transform(i_string.begin(), i_string.end(), i_string.begin(),
+ [](unsigned char l_char) { return std::tolower(l_char); });
+}
+} // namespace commonUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/dbus_utility.hpp b/vpd-manager/include/utility/dbus_utility.hpp
new file mode 100644
index 0000000..4c81815
--- /dev/null
+++ b/vpd-manager/include/utility/dbus_utility.hpp
@@ -0,0 +1,567 @@
+#pragma once
+
+#include "constants.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <chrono>
+
+namespace vpd
+{
+/**
+ * @brief The namespace defines utlity methods for generic D-Bus operations.
+ */
+namespace dbusUtility
+{
+
+/**
+ * @brief An API to get Map of service and interfaces for an object path.
+ *
+ * The API returns a Map of service name and interfaces for a given pair of
+ * object path and interface list. It can be used to determine service name
+ * which implemets a particular object path and interface.
+ *
+ * Note: It will be caller's responsibility to check for empty map returned and
+ * generate appropriate error.
+ *
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interfaces - Array of interface(s).
+ * @return - A Map of service name to object to interface(s), if success.
+ * If failed, empty map.
+ */
+inline types::MapperGetObject getObjectMap(const std::string& objectPath,
+ std::span<const char*> interfaces)
+{
+ types::MapperGetObject getObjectMap;
+
+ // interface list is optional argument, hence no check required.
+ if (objectPath.empty())
+ {
+ logging::logMessage("Path value is empty, invalid call to GetObject");
+ return getObjectMap;
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method = bus.new_method_call(
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject");
+
+ method.append(objectPath, interfaces);
+ auto result = bus.call(method);
+ result.read(getObjectMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ // logging::logMessage(e.what());
+ return getObjectMap;
+ }
+
+ return getObjectMap;
+}
+
+/**
+ * @brief An API to get property map for an interface.
+ *
+ * This API returns a map of property and its value with respect to a particular
+ * interface.
+ *
+ * Note: It will be caller's responsibility to check for empty map returned and
+ * generate appropriate error.
+ *
+ * @param[in] i_service - Service name.
+ * @param[in] i_objectPath - object path.
+ * @param[in] i_interface - Interface, for the properties to be listed.
+ *
+ * @return - A map of property and value of an interface, if success.
+ * if failed, empty map.
+ */
+inline types::PropertyMap getPropertyMap(const std::string& i_service,
+ const std::string& i_objectPath,
+ const std::string& i_interface)
+{
+ types::PropertyMap l_propertyValueMap;
+ if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
+ {
+ logging::logMessage("Invalid parameters to get property map");
+ return l_propertyValueMap;
+ }
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method =
+ l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "GetAll");
+ l_method.append(i_interface);
+ auto l_result = l_bus.call(l_method);
+ l_result.read(l_propertyValueMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+
+ return l_propertyValueMap;
+}
+
+/**
+ * @brief API to get object subtree from D-bus.
+ *
+ * The API returns the map of object, services and interfaces in the
+ * subtree that implement a certain interface. If no interfaces are provided
+ * then all the objects, services and interfaces under the subtree will
+ * be returned.
+ *
+ * Note: Depth can be 0 and interfaces can be null.
+ * It will be caller's responsibility to check for empty vector returned
+ * and generate appropriate error.
+ *
+ * @param[in] i_objectPath - Path to search for an interface.
+ * @param[in] i_depth - Maximum depth of the tree to search.
+ * @param[in] i_interfaces - List of interfaces to search.
+ *
+ * @return - A map of object and its related services and interfaces, if
+ * success. If failed, empty map.
+ */
+
+inline types::MapperGetSubTree
+ getObjectSubTree(const std::string& i_objectPath, const int& i_depth,
+ const std::vector<std::string>& i_interfaces)
+{
+ types::MapperGetSubTree l_subTreeMap;
+
+ if (i_objectPath.empty())
+ {
+ logging::logMessage("Object path is empty.");
+ return l_subTreeMap;
+ }
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::objectMapperService, constants::objectMapperPath,
+ constants::objectMapperInf, "GetSubTree");
+ l_method.append(i_objectPath, i_depth, i_interfaces);
+ auto l_result = l_bus.call(l_method);
+ l_result.read(l_subTreeMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+
+ return l_subTreeMap;
+}
+
+/**
+ * @brief An API to read property from Dbus.
+ *
+ * The caller of the API needs to validate the validatity and correctness of the
+ * type and value of data returned. The API will just fetch and retun the data
+ * without any data validation.
+ *
+ * Note: It will be caller's responsibility to check for empty value returned
+ * and generate appropriate error if required.
+ *
+ * @param [in] serviceName - Name of the Dbus service.
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interface - Interface under which property exist.
+ * @param [in] property - Property whose value is to be read.
+ * @return - Value read from Dbus, if success.
+ * If failed, empty variant.
+ */
+inline types::DbusVariantType readDbusProperty(
+ const std::string& serviceName, const std::string& objectPath,
+ const std::string& interface, const std::string& property)
+{
+ types::DbusVariantType propertyValue;
+
+ // Mandatory fields to make a read dbus call.
+ if (serviceName.empty() || objectPath.empty() || interface.empty() ||
+ property.empty())
+ {
+ logging::logMessage(
+ "One of the parameter to make Dbus read call is empty.");
+ return propertyValue;
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method =
+ bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(interface, property);
+
+ auto result = bus.call(method);
+ result.read(propertyValue);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ return propertyValue;
+ }
+ return propertyValue;
+}
+
+/**
+ * @brief An API to write property on Dbus.
+ *
+ * The caller of this API needs to handle exception thrown by this method to
+ * identify any write failure. The API in no other way indicate write failure
+ * to the caller.
+ *
+ * Note: It will be caller's responsibility ho handle the exception thrown in
+ * case of write failure and generate appropriate error.
+ *
+ * @param [in] serviceName - Name of the Dbus service.
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interface - Interface under which property exist.
+ * @param [in] property - Property whose value is to be written.
+ * @param [in] propertyValue - The value to be written.
+ */
+inline void writeDbusProperty(
+ const std::string& serviceName, const std::string& objectPath,
+ const std::string& interface, const std::string& property,
+ const types::DbusVariantType& propertyValue)
+{
+ // Mandatory fields to make a write dbus call.
+ if (serviceName.empty() || objectPath.empty() || interface.empty() ||
+ property.empty())
+ {
+ logging::logMessage(
+ "One of the parameter to make Dbus read call is empty.");
+
+ // caller need to handle the throw to ensure Dbus write success.
+ throw std::runtime_error("Dbus write failed, Parameter empty");
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method =
+ bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Set");
+ method.append(interface, property, propertyValue);
+ bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ logging::logMessage(e.what());
+
+ // caller needs to handle this throw to handle error in writing Dbus.
+ throw std::runtime_error("Dbus write failed");
+ }
+}
+
+/**
+ * @brief API to publish data on PIM
+ *
+ * The API calls notify on PIM object to publlish VPD.
+ *
+ * @param[in] objectMap - Object, its interface and data.
+ * @return bool - Status of call to PIM notify.
+ */
+inline bool callPIM(types::ObjectMap&& objectMap)
+{
+ try
+ {
+ for (const auto& l_objectKeyValue : objectMap)
+ {
+ auto l_nodeHandle = objectMap.extract(l_objectKeyValue.first);
+
+ if (l_nodeHandle.key().str.find(constants::pimPath, 0) !=
+ std::string::npos)
+ {
+ l_nodeHandle.key() = l_nodeHandle.key().str.replace(
+ 0, std::strlen(constants::pimPath), "");
+ objectMap.insert(std::move(l_nodeHandle));
+ }
+ }
+
+ auto bus = sdbusplus::bus::new_default();
+ auto pimMsg =
+ bus.new_method_call(constants::pimServiceName, constants::pimPath,
+ constants::pimIntf, "Notify");
+ pimMsg.append(std::move(objectMap));
+ bus.call(pimMsg);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief API to check if a D-Bus service is running or not.
+ *
+ * Any failure in calling the method "NameHasOwner" implies that the service is
+ * not in a running state. Hence the API returns false in case of any exception
+ * as well.
+ *
+ * @param[in] i_serviceName - D-Bus service name whose status is to be checked.
+ * @return bool - True if the service is running, false otherwise.
+ */
+inline bool isServiceRunning(const std::string& i_serviceName)
+{
+ bool l_retVal = false;
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ "org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "NameHasOwner");
+ l_method.append(i_serviceName);
+
+ l_bus.call(l_method).read(l_retVal);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(
+ "Call to check service status failed with exception: " +
+ std::string(l_ex.what()));
+ }
+
+ return l_retVal;
+}
+
+/**
+ * @brief API to call "GetAttribute" method uner BIOS manager.
+ *
+ * The API reads the given attribuute from BIOS and returns a tuple of both
+ * current as well as pending value for that attribute.
+ * The API return only the current attribute value if found.
+ * API returns an empty variant of type BiosAttributeCurrentValue in case of any
+ * error.
+ *
+ * @param[in] i_attributeName - Attribute to be read.
+ * @return Tuple of PLDM attribute Type, current attribute value and pending
+ * attribute value.
+ */
+inline types::BiosAttributeCurrentValue
+ biosGetAttributeMethodCall(const std::string& i_attributeName)
+{
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "GetAttribute");
+ l_method.append(i_attributeName);
+
+ types::BiosGetAttrRetType l_attributeVal;
+ try
+ {
+ auto l_result = l_bus.call(l_method);
+ l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
+ std::get<2>(l_attributeVal));
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(
+ "Failed to read BIOS Attribute: " + i_attributeName +
+ " due to error " + std::string(l_ex.what()));
+
+ // TODO: Log an informational PEL here.
+ }
+
+ return std::get<1>(l_attributeVal);
+}
+
+/**
+ * @brief API to check if Chassis is powered on.
+ *
+ * This API queries Phosphor Chassis State Manager to know whether
+ * Chassis is powered on.
+ *
+ * @return true if chassis is powered on, false otherwise
+ */
+inline bool isChassisPowerOn()
+{
+ auto powerState = dbusUtility::readDbusProperty(
+ "xyz.openbmc_project.State.Chassis",
+ "/xyz/openbmc_project/state/chassis0",
+ "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
+
+ if (auto curPowerState = std::get_if<std::string>(&powerState))
+ {
+ if ("xyz.openbmc_project.State.Chassis.PowerState.On" == *curPowerState)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ TODO: Add PEL.
+ Callout: Firmware callout
+ Type: Informational
+ Description: Chassis state can't be determined, defaulting to chassis
+ off. : e.what()
+ */
+ return false;
+}
+
+/**
+ * @brief API to check if host is in running state.
+ *
+ * This API reads the current host state from D-bus and returns true if the host
+ * is running.
+ *
+ * @return true if host is in running state. false otherwise.
+ */
+inline bool isHostRunning()
+{
+ const auto l_hostState = dbusUtility::readDbusProperty(
+ constants::hostService, constants::hostObjectPath,
+ constants::hostInterface, "CurrentHostState");
+
+ if (const auto l_currHostState = std::get_if<std::string>(&l_hostState))
+ {
+ if (*l_currHostState == constants::hostRunningState)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief API to check if BMC is in ready state.
+ *
+ * This API reads the current state of BMC from D-bus and returns true if BMC is
+ * in ready state.
+ *
+ * @return true if BMC is ready, false otherwise.
+ */
+inline bool isBMCReady()
+{
+ const auto l_bmcState = dbusUtility::readDbusProperty(
+ constants::bmcStateService, constants::bmcZeroStateObject,
+ constants::bmcStateInterface, constants::currentBMCStateProperty);
+
+ if (const auto l_currBMCState = std::get_if<std::string>(&l_bmcState))
+ {
+ if (*l_currBMCState == constants::bmcReadyState)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief An API to enable BMC reboot guard
+ *
+ * This API does a D-Bus method call to enable BMC reboot guard.
+ *
+ * @return On success, returns 0, otherwise returns -1.
+ */
+inline int EnableRebootGuard() noexcept
+{
+ int l_rc{constants::FAILURE};
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::systemdService, constants::systemdObjectPath,
+ constants::systemdManagerInterface, "StartUnit");
+ l_method.append("reboot-guard-enable.service", "replace");
+ l_bus.call_noreply(l_method);
+ l_rc = constants::SUCCESS;
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ std::string l_errMsg =
+ "D-Bus call to enable BMC reboot guard failed for reason: ";
+ l_errMsg += l_ex.what();
+
+ logging::logMessage(l_errMsg);
+ }
+ return l_rc;
+}
+
+/**
+ * @brief An API to disable BMC reboot guard
+ *
+ * This API disables BMC reboot guard. This API has an inbuilt re-try mechanism.
+ * If Disable Reboot Guard fails, this API attempts to Disable Reboot Guard for
+ * 3 more times at an interval of 333ms.
+ *
+ * @return On success, returns 0, otherwise returns -1.
+ */
+inline int DisableRebootGuard() noexcept
+{
+ int l_rc{constants::FAILURE};
+
+ // A lambda which executes the DBus call to disable BMC reboot guard.
+ auto l_executeDisableRebootGuard = []() -> int {
+ int l_dBusCallRc{constants::FAILURE};
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::systemdService, constants::systemdObjectPath,
+ constants::systemdManagerInterface, "StartUnit");
+ l_method.append("reboot-guard-disable.service", "replace");
+ l_bus.call_noreply(l_method);
+ l_dBusCallRc = constants::SUCCESS;
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {}
+ return l_dBusCallRc;
+ };
+
+ if (constants::FAILURE == l_executeDisableRebootGuard())
+ {
+ std::function<void()> l_retryDisableRebootGuard;
+
+ // A lambda which tries to disable BMC reboot guard for 3 times at an
+ // interval of 333 ms.
+ l_retryDisableRebootGuard = [&]() {
+ constexpr int MAX_RETRIES{3};
+ static int l_numRetries{0};
+
+ if (l_numRetries < MAX_RETRIES)
+ {
+ l_numRetries++;
+ if (constants::FAILURE == l_executeDisableRebootGuard())
+ {
+ // sleep for 333ms before next retry. This is just a random
+ // value so that 3 re-tries * 333ms takes ~1 second in the
+ // worst case.
+ const std::chrono::milliseconds l_sleepTime{333};
+ std::this_thread::sleep_for(l_sleepTime);
+ l_retryDisableRebootGuard();
+ }
+ else
+ {
+ l_numRetries = 0;
+ l_rc = constants::SUCCESS;
+ }
+ }
+ else
+ {
+ // Failed to Disable Reboot Guard even after 3 retries.
+ logging::logMessage("Failed to Disable Reboot Guard after " +
+ std::to_string(MAX_RETRIES) + " re-tries");
+ l_numRetries = 0;
+ }
+ };
+
+ l_retryDisableRebootGuard();
+ }
+ else
+ {
+ l_rc = constants::SUCCESS;
+ }
+ return l_rc;
+}
+
+} // namespace dbusUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/json_utility.hpp b/vpd-manager/include/utility/json_utility.hpp
new file mode 100644
index 0000000..3dab105
--- /dev/null
+++ b/vpd-manager/include/utility/json_utility.hpp
@@ -0,0 +1,1043 @@
+#pragma once
+
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <gpiod.hpp>
+#include <nlohmann/json.hpp>
+#include <utility/common_utility.hpp>
+
+#include <fstream>
+#include <type_traits>
+#include <unordered_map>
+
+namespace vpd
+{
+namespace jsonUtility
+{
+
+// forward declaration of API for function map.
+bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_baseAction,
+ const std::string& i_flagToProcess);
+
+// forward declaration of API for function map.
+bool processGpioPresenceTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess);
+
+// forward declaration of API for function map.
+bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_baseAction,
+ const std::string& i_flagToProcess);
+
+// Function pointers to process tags from config JSON.
+typedef bool (*functionPtr)(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess);
+
+inline std::unordered_map<std::string, functionPtr> funcionMap{
+ {"gpioPresence", processGpioPresenceTag},
+ {"setGpio", procesSetGpioTag},
+ {"systemCmd", processSystemCmdTag}};
+
+/**
+ * @brief API to read VPD offset from JSON file.
+ *
+ * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
+ * @param[in] i_vpdFilePath - VPD file path.
+ * @return VPD offset if found in JSON, 0 otherwise.
+ */
+inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFilePath)
+{
+ if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
+ (!i_sysCfgJsonObj.contains("frus")))
+ {
+ return 0;
+ }
+
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ // check if given path is redundant FRU path
+ if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", ""))
+ {
+ // Return the offset of redundant EEPROM taken from JSON.
+ return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief API to parse respective JSON.
+ *
+ * Exception is thrown in case of JSON parse error.
+ *
+ * @param[in] pathToJson - Path to JSON.
+ * @return Parsed JSON.
+ */
+inline nlohmann::json getParsedJson(const std::string& pathToJson)
+{
+ if (pathToJson.empty())
+ {
+ throw std::runtime_error("Path to JSON is missing");
+ }
+
+ if (!std::filesystem::exists(pathToJson) ||
+ std::filesystem::is_empty(pathToJson))
+ {
+ throw std::runtime_error("Incorrect File Path or empty file");
+ }
+
+ std::ifstream jsonFile(pathToJson);
+ if (!jsonFile)
+ {
+ throw std::runtime_error("Failed to access Json path = " + pathToJson);
+ }
+
+ try
+ {
+ return nlohmann::json::parse(jsonFile);
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ throw std::runtime_error("Failed to parse JSON file");
+ }
+}
+
+/**
+ * @brief Get inventory object path from system config JSON.
+ *
+ * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
+ * this API returns D-bus inventory path if present in JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ *
+ * @return On success a valid path is returned, on failure an empty string is
+ * returned or an exception is thrown.
+ */
+inline std::string getInventoryObjPathFromJson(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
+ "inventoryPath", "");
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+ const auto l_invObjPath =
+ i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath", "");
+
+ // check if given path is redundant FRU path or inventory path
+ if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", "") ||
+ (i_vpdPath == l_invObjPath))
+ {
+ return l_invObjPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief Process "PostFailAction" defined in config JSON.
+ *
+ * In case there is some error in the processing of "preAction" execution and a
+ * set of procedure needs to be done as a part of post fail action. This base
+ * action can be defined in the config JSON for that FRU and it will be handled
+ * under this API.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
+ * under PostFailAction tag of config JSON.
+ * @return - success or failure
+ */
+inline bool executePostFailAction(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_flagToProcess)
+{
+ if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
+ i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameters. Abort processing for post fail action");
+
+ return false;
+ }
+
+ if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["PostFailAction"]
+ .contains(i_flagToProcess))
+ {
+ logging::logMessage(
+ "Config JSON missing flag " + i_flagToProcess +
+ " to execute post fail action for path = " + i_vpdFilePath);
+
+ return false;
+ }
+
+ for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0))["PostFailAction"][i_flagToProcess]
+ .items())
+ {
+ auto itrToFunction = funcionMap.find(l_tags.key());
+ if (itrToFunction != funcionMap.end())
+ {
+ if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
+ "PostFailAction", i_flagToProcess))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Process "systemCmd" tag for a given FRU.
+ *
+ * The API will process "systemCmd" tag if it is defined in the config
+ * JSON for the given FRU.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool processSystemCmdTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of processSystemCmd.");
+ return false;
+ }
+
+ if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["systemCmd"])
+ .contains("cmd")))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to execute system command for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ try
+ {
+ const std::string& l_systemCommand =
+ i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
+
+ commonUtility::executeCmd(l_systemCommand);
+ return true;
+ }
+ catch (const std::exception& e)
+ {
+ std::string l_errMsg = "Process system tag failed for exception: ";
+ l_errMsg += e.what();
+
+ logging::logMessage(l_errMsg);
+ return false;
+ }
+}
+
+/**
+ * @brief Checks for presence of a given FRU using GPIO line.
+ *
+ * This API returns the presence information of the FRU corresponding to the
+ * given VPD file path by setting the presence pin.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool processGpioPresenceTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of processGpioPresence tag");
+ return false;
+ }
+
+ if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
+ .contains("pin")) &&
+ ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
+ .contains("value"))))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to detect presence for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ // get the pin name
+ const std::string& l_presencePinName =
+ i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
+
+ // get the pin value
+ uint8_t l_presencePinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
+
+ try
+ {
+ gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
+
+ if (!l_presenceLine)
+ {
+ throw GpioException("Couldn't find the GPIO line.");
+ }
+
+ l_presenceLine.request({"Read the presence line",
+ gpiod::line_request::DIRECTION_INPUT, 0});
+
+ return (l_presencePinValue == l_presenceLine.get_value());
+ }
+ catch (const std::exception& ex)
+ {
+ std::string l_errMsg = "Exception on GPIO line: ";
+ l_errMsg += l_presencePinName;
+ l_errMsg += " Reason: ";
+ l_errMsg += ex.what();
+ l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
+
+ // ToDo -- Update Internal Rc code.
+ EventLogger::createAsyncPelWithInventoryCallout(
+ types::ErrorType::GpioError, types::SeverityType::Informational,
+ {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
+ types::CalloutPriority::High}},
+ std::source_location::current().file_name(),
+ std::source_location::current().function_name(), 0, l_errMsg,
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ logging::logMessage(l_errMsg);
+
+ // Except when GPIO pin value is false, we go and try collecting the
+ // FRU VPD as we couldn't able to read GPIO pin value due to some
+ // error/exception. So returning true in error scenario.
+ return true;
+ }
+}
+
+/**
+ * @brief Process "setGpio" tag for a given FRU.
+ *
+ * This API enables the GPIO line.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool procesSetGpioTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of procesSetGpio.");
+ return false;
+ }
+
+ if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"])
+ .contains("pin")) &&
+ ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"])
+ .contains("value"))))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to set gpio line for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ const std::string& l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
+
+ // Get the value to set
+ uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
+
+ logging::logMessage(
+ "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
+ try
+ {
+ gpiod::line l_outputLine = gpiod::find_line(l_pinName);
+
+ if (!l_outputLine)
+ {
+ throw GpioException("Couldn't find GPIO line.");
+ }
+
+ l_outputLine.request(
+ {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
+ l_pinValue);
+ return true;
+ }
+ catch (const std::exception& ex)
+ {
+ std::string l_errMsg = "Exception on GPIO line: ";
+ l_errMsg += l_pinName;
+ l_errMsg += " Reason: ";
+ l_errMsg += ex.what();
+ l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
+
+ // ToDo -- Update Internal RC code
+ EventLogger::createAsyncPelWithInventoryCallout(
+ types::ErrorType::GpioError, types::SeverityType::Informational,
+ {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
+ types::CalloutPriority::High}},
+ std::source_location::current().file_name(),
+ std::source_location::current().function_name(), 0, l_errMsg,
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ logging::logMessage(l_errMsg);
+
+ return false;
+ }
+}
+
+/**
+ * @brief Process any action, if defined in config JSON.
+ *
+ * If any FRU(s) requires any special handling, then this base action can be
+ * defined for that FRU in the config JSON, processing of which will be handled
+ * in this API.
+ * Examples of action - preAction, PostAction etc.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_action - Base action to be performed.
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
+ * under PreAction tag of config JSON.
+ * @return - success or failure
+ */
+inline bool executeBaseAction(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_action,
+ const std::string& i_vpdFilePath, const std::string& i_flagToProcess)
+{
+ if (i_flagToProcess.empty() || i_action.empty() || i_vpdFilePath.empty() ||
+ !i_parsedConfigJson.contains("frus"))
+ {
+ logging::logMessage("Invalid parameter");
+ return false;
+ }
+
+ if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
+ {
+ logging::logMessage(
+ "File path: " + i_vpdFilePath + " not found in JSON");
+ return false;
+ }
+
+ if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(i_action))
+ {
+ logging::logMessage("Action [" + i_action +
+ "] not defined for file path:" + i_vpdFilePath);
+ return false;
+ }
+
+ if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))[i_action].contains(
+ i_flagToProcess))
+ {
+ logging::logMessage("Config JSON missing flag [" + i_flagToProcess +
+ "] to execute action for path = " + i_vpdFilePath);
+
+ return false;
+ }
+
+ const nlohmann::json& l_tagsJson =
+ (i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0))[i_action][i_flagToProcess];
+
+ for (const auto& l_tag : l_tagsJson.items())
+ {
+ auto itrToFunction = funcionMap.find(l_tag.key());
+ if (itrToFunction != funcionMap.end())
+ {
+ if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
+ i_action, i_flagToProcess))
+ {
+ // In case any of the tag fails to execute. Mark action
+ // as failed for that flag.
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Get redundant FRU path from system config JSON
+ *
+ * Given either D-bus inventory path/FRU path/redundant FRU path, this
+ * API returns the redundant FRU path taken from "redundantEeprom" tag from
+ * system config JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ * @return On success return valid path, on failure return empty string.
+ */
+inline std::string getRedundantEepromPathFromJson(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
+ "redundantEeprom", "");
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const std::string& l_fruPath = l_fru.key();
+ const std::string& l_redundantFruPath =
+ i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("redundantEeprom",
+ "");
+
+ // check if given path is inventory path or redundant FRU path
+ if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
+ "") == i_vpdPath) ||
+ (l_redundantFruPath == i_vpdPath))
+ {
+ return l_redundantFruPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief Get FRU EEPROM path from system config JSON
+ *
+ * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
+ * this API returns FRU EEPROM path if present in JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ *
+ * @return On success return valid path, on failure return empty string.
+ */
+inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_vpdPath;
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ // check if given path is redundant FRU path or inventory path
+ if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", "") ||
+ (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "inventoryPath", "")))
+ {
+ return l_fruPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief An API to check backup and restore VPD is required.
+ *
+ * The API checks if there is provision for backup and restore mentioned in the
+ * system config JSON, by looking "backupRestoreConfigPath" tag.
+ * Checks if the path mentioned is a hardware path, by checking if the file path
+ * exists and size of contents in the path.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return true if backup and restore is required, false otherwise.
+ */
+inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj)
+{
+ try
+ {
+ const std::string& l_backupAndRestoreCfgFilePath =
+ i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+ if (!l_backupAndRestoreCfgFilePath.empty() &&
+ std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
+ !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
+ {
+ return true;
+ }
+ }
+ catch (std::exception& ex)
+ {
+ logging::logMessage(ex.what());
+ }
+ return false;
+}
+
+/** @brief API to check if an action is required for given EEPROM path.
+ *
+ * System config JSON can contain pre-action, post-action etc. like actions
+ * defined for an EEPROM path. The API will check if any such action is defined
+ * for the EEPROM.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ * @param[in] i_action - Action to be checked.
+ * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
+ * triggered.
+ * @return - True if action is defined for the flow, false otherwise.
+ */
+inline bool isActionRequired(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdFruPath,
+ const std::string& i_action, const std::string& i_flowFlag)
+{
+ if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
+ {
+ logging::logMessage("Invalid parameters recieved.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Invalid JSON object recieved.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
+ {
+ logging::logMessage(
+ "JSON object does not contain EEPROM path " + i_vpdFruPath);
+ return false;
+ }
+
+ if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
+ {
+ if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
+ i_flowFlag))
+ {
+ return true;
+ }
+
+ logging::logMessage("Flow flag: [" + i_flowFlag +
+ "], not found in JSON for path: " + i_vpdFruPath);
+ return false;
+ }
+ return false;
+}
+
+/**
+ * @brief An API to return list of FRUs that needs GPIO polling.
+ *
+ * An API that checks for the FRUs that requires GPIO polling and returns
+ * a list of FRUs that needs polling. Returns an empty list if there are
+ * no FRUs that requires polling.
+ *
+ * @throw std::runtime_error
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return list of FRUs parameters that needs polling.
+ */
+inline std::vector<std::string>
+ getListOfGpioPollingFrus(const nlohmann::json& i_sysCfgJsonObj)
+{
+ if (i_sysCfgJsonObj.empty())
+ {
+ throw std::runtime_error("Invalid Parameters");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus section in system config JSON");
+ }
+
+ std::vector<std::string> l_gpioPollingRequiredFrusList;
+
+ for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ try
+ {
+ if (isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
+ "hotPlugging"))
+ {
+ if (i_sysCfgJsonObj["frus"][l_fruPath]
+ .at(0)["pollingRequired"]["hotPlugging"]
+ .contains("gpioPresence"))
+ {
+ l_gpioPollingRequiredFrusList.push_back(l_fruPath);
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+ }
+
+ return l_gpioPollingRequiredFrusList;
+}
+
+/**
+ * @brief Get all related path(s) to update keyword value.
+ *
+ * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
+ * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
+ * exists in the system config JSON.
+ *
+ * Note: If the inventory object path or redundant EEPROM path(s) are not found
+ * in the system config JSON, corresponding fields will have empty value in the
+ * returning tuple.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
+ *
+ * @return On success returns tuple of EEPROM path, inventory path & redundant
+ * path, on failure returns tuple with given input path alone.
+ */
+inline std::tuple<std::string, std::string, std::string>
+ getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
+ std::string io_vpdPath)
+{
+ types::Path l_inventoryObjPath;
+ types::Path l_redundantFruPath;
+ try
+ {
+ if (!i_sysCfgJsonObj.empty())
+ {
+ // Get hardware path from system config JSON.
+ const types::Path l_fruPath =
+ jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath);
+
+ if (!l_fruPath.empty())
+ {
+ io_vpdPath = l_fruPath;
+
+ // Get inventory object path from system config JSON
+ l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
+ i_sysCfgJsonObj, l_fruPath);
+
+ // Get redundant hardware path if present in system config JSON
+ l_redundantFruPath =
+ jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
+ l_fruPath);
+ }
+ }
+ }
+ catch (const std::exception& l_exception)
+ {
+ logging::logMessage(
+ "Failed to get all paths to update keyword value, error " +
+ std::string(l_exception.what()));
+ }
+ return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
+}
+
+/**
+ * @brief An API to get DBus service name.
+ *
+ * Given DBus inventory path, this API returns DBus service name if present in
+ * the JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] l_inventoryPath - DBus inventory path.
+ *
+ * @return On success returns the service name present in the system config
+ * JSON, otherwise empty string.
+ *
+ * Note: Caller has to handle in case of empty string received.
+ */
+inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& l_inventoryPath)
+{
+ try
+ {
+ if (l_inventoryPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_frus : l_listOfFrus.items())
+ {
+ for (const auto& l_inventoryItem : l_frus.value())
+ {
+ if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
+ constants::STR_CMP_SUCCESS)
+ {
+ return l_inventoryItem["serviceName"];
+ }
+ }
+ }
+ throw std::runtime_error(
+ "Inventory path not found in the system config JSON");
+ }
+ catch (const std::exception& l_exception)
+ {
+ logging::logMessage(
+ "Error while getting DBus service name for given path " +
+ l_inventoryPath + ", error: " + std::string(l_exception.what()));
+ // TODO:log PEL
+ }
+ return std::string{};
+}
+
+/**
+ * @brief An API to check if a FRU is tagged as "powerOffOnly"
+ *
+ * Given the system config JSON and VPD FRU path, this API checks if the FRU
+ * VPD can be collected at Chassis Power Off state only.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ * @return - True if FRU VPD can be collected at Chassis Power Off state only.
+ * False otherwise
+ */
+inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ if (i_vpdFruPath.empty())
+ {
+ logging::logMessage("FRU path is empty.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
+ {
+ logging::logMessage("JSON object does not contain EEPROM path \'" +
+ i_vpdFruPath + "\'");
+ return false;
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("powerOffOnly") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
+}
+
+/**
+ * @brief API which tells if the FRU is replaceable at runtime
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ *
+ * @return true if FRU is replaceable at runtime. false otherwise.
+ */
+inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ try
+ {
+ if (i_vpdFruPath.empty())
+ {
+ throw std::runtime_error("Given FRU path is empty.");
+ }
+
+ if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
+ {
+ throw std::runtime_error("Invalid system config JSON object.");
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("replaceableAtRuntime") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
+ 0)["replaceableAtRuntime"]));
+ }
+ catch (const std::exception& l_error)
+ {
+ // TODO: Log PEL
+ logging::logMessage(l_error.what());
+ }
+
+ return false;
+}
+
+/**
+ * @brief API which tells if the FRU is replaceable at standby
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ *
+ * @return true if FRU is replaceable at standby. false otherwise.
+ */
+inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ try
+ {
+ if (i_vpdFruPath.empty())
+ {
+ throw std::runtime_error("Given FRU path is empty.");
+ }
+
+ if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
+ {
+ throw std::runtime_error("Invalid system config JSON object.");
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("replaceableAtStandby") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
+ 0)["replaceableAtStandby"]));
+ }
+ catch (const std::exception& l_error)
+ {
+ // TODO: Log PEL
+ logging::logMessage(l_error.what());
+ }
+
+ return false;
+}
+
+/**
+ * @brief API to get list of FRUs replaceable at standby from JSON.
+ *
+ * The API will return a vector of FRUs inventory path which are replaceable at
+ * standby.
+ * The API can throw exception in case of error scenarios. Caller's
+ * responsibility to handle those exceptions.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return - List of FRUs replaceable at standby.
+ */
+inline std::vector<std::string>
+ getListOfFrusReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj)
+{
+ std::vector<std::string> l_frusReplaceableAtStandby;
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON.");
+ return l_frusReplaceableAtStandby;
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
+ "replaceableAtStandby", false))
+ {
+ const std::string& l_inventoryObjectPath =
+ i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
+ "inventoryPath", "");
+
+ if (!l_inventoryObjectPath.empty())
+ {
+ l_frusReplaceableAtStandby.emplace_back(l_inventoryObjectPath);
+ }
+ }
+ }
+
+ return l_frusReplaceableAtStandby;
+}
+
+} // namespace jsonUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/vpd_specific_utility.hpp b/vpd-manager/include/utility/vpd_specific_utility.hpp
new file mode 100644
index 0000000..d6b92fd
--- /dev/null
+++ b/vpd-manager/include/utility/vpd_specific_utility.hpp
@@ -0,0 +1,556 @@
+#pragma once
+
+#include "config.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+#include <utility/common_utility.hpp>
+#include <utility/dbus_utility.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <regex>
+
+namespace vpd
+{
+namespace vpdSpecificUtility
+{
+/**
+ * @brief API to generate file name for bad VPD.
+ *
+ * For i2c eeproms - the pattern of the vpd-name will be
+ * i2c-<bus-number>-<eeprom-address>.
+ * For spi eeproms - the pattern of the vpd-name will be spi-<spi-number>.
+ *
+ * @param[in] vpdFilePath - file path of the vpd.
+ * @return Generated file name.
+ */
+inline std::string generateBadVPDFileName(const std::string& vpdFilePath)
+{
+ std::string badVpdFileName = BAD_VPD_DIR;
+ if (vpdFilePath.find("i2c") != std::string::npos)
+ {
+ badVpdFileName += "i2c-";
+ std::regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
+ std::smatch match;
+ if (std::regex_search(vpdFilePath, match, i2cPattern))
+ {
+ badVpdFileName += match.str(2);
+ }
+ }
+ else if (vpdFilePath.find("spi") != std::string::npos)
+ {
+ std::regex spiPattern("((spi)[0-9]+)(.0)");
+ std::smatch match;
+ if (std::regex_search(vpdFilePath, match, spiPattern))
+ {
+ badVpdFileName += match.str(1);
+ }
+ }
+ return badVpdFileName;
+}
+
+/**
+ * @brief API which dumps the broken/bad vpd in a directory.
+ * When the vpd is bad, this API places the bad vpd file inside
+ * "/tmp/bad-vpd" in BMC, in order to collect bad VPD data as a part of user
+ * initiated BMC dump.
+ *
+ * Note: Throws exception in case of any failure.
+ *
+ * @param[in] vpdFilePath - vpd file path
+ * @param[in] vpdVector - vpd vector
+ */
+inline void dumpBadVpd(const std::string& vpdFilePath,
+ const types::BinaryVector& vpdVector)
+{
+ std::filesystem::create_directory(BAD_VPD_DIR);
+ auto badVpdPath = generateBadVPDFileName(vpdFilePath);
+
+ if (std::filesystem::exists(badVpdPath))
+ {
+ std::error_code ec;
+ std::filesystem::remove(badVpdPath, ec);
+ if (ec) // error code
+ {
+ std::string error = "Error removing the existing broken vpd in ";
+ error += badVpdPath;
+ error += ". Error code : ";
+ error += ec.value();
+ error += ". Error message : ";
+ error += ec.message();
+ throw std::runtime_error(error);
+ }
+ }
+
+ std::ofstream badVpdFileStream(badVpdPath, std::ofstream::binary);
+ if (badVpdFileStream.is_open())
+ {
+ throw std::runtime_error(
+ "Failed to open bad vpd file path in /tmp/bad-vpd. "
+ "Unable to dump the broken/bad vpd file.");
+ }
+
+ badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()),
+ vpdVector.size());
+}
+
+/**
+ * @brief An API to read value of a keyword.
+ *
+ * Note: Throws exception. Caller needs to handle.
+ *
+ * @param[in] kwdValueMap - A map having Kwd value pair.
+ * @param[in] kwd - keyword name.
+ * @param[out] kwdValue - Value of the keyword read from map.
+ */
+inline void getKwVal(const types::IPZKwdValueMap& kwdValueMap,
+ const std::string& kwd, std::string& kwdValue)
+{
+ if (kwd.empty())
+ {
+ logging::logMessage("Invalid parameters");
+ throw std::runtime_error("Invalid parameters");
+ }
+
+ auto itrToKwd = kwdValueMap.find(kwd);
+ if (itrToKwd != kwdValueMap.end())
+ {
+ kwdValue = itrToKwd->second;
+ return;
+ }
+
+ throw std::runtime_error("Keyword not found");
+}
+
+/**
+ * @brief An API to process encoding of a keyword.
+ *
+ * @param[in] keyword - Keyword to be processed.
+ * @param[in] encoding - Type of encoding.
+ * @return Value after being processed for encoded type.
+ */
+inline std::string encodeKeyword(const std::string& keyword,
+ const std::string& encoding)
+{
+ // Default value is keyword value
+ std::string result(keyword.begin(), keyword.end());
+
+ if (encoding == "MAC")
+ {
+ result.clear();
+ size_t firstByte = keyword[0];
+ result += commonUtility::toHex(firstByte >> 4);
+ result += commonUtility::toHex(firstByte & 0x0f);
+ for (size_t i = 1; i < keyword.size(); ++i)
+ {
+ result += ":";
+ result += commonUtility::toHex(keyword[i] >> 4);
+ result += commonUtility::toHex(keyword[i] & 0x0f);
+ }
+ }
+ else if (encoding == "DATE")
+ {
+ // Date, represent as
+ // <year>-<month>-<day> <hour>:<min>
+ result.clear();
+ static constexpr uint8_t skipPrefix = 3;
+
+ auto strItr = keyword.begin();
+ advance(strItr, skipPrefix);
+ for_each(strItr, keyword.end(), [&result](size_t c) { result += c; });
+
+ result.insert(constants::BD_YEAR_END, 1, '-');
+ result.insert(constants::BD_MONTH_END, 1, '-');
+ result.insert(constants::BD_DAY_END, 1, ' ');
+ result.insert(constants::BD_HOUR_END, 1, ':');
+ }
+
+ return result;
+}
+
+/**
+ * @brief Helper function to insert or merge in map.
+ *
+ * This method checks in an interface if the given interface exists. If the
+ * interface key already exists, property map is inserted corresponding to it.
+ * If the key does'nt exist then given interface and property map pair is newly
+ * created. If the property present in propertymap already exist in the
+ * InterfaceMap, then the new property value is ignored.
+ *
+ * @param[in,out] map - Interface map.
+ * @param[in] interface - Interface to be processed.
+ * @param[in] propertyMap - new property map that needs to be emplaced.
+ */
+inline void insertOrMerge(types::InterfaceMap& map,
+ const std::string& interface,
+ types::PropertyMap&& propertyMap)
+{
+ if (map.find(interface) != map.end())
+ {
+ try
+ {
+ auto& prop = map.at(interface);
+ std::for_each(propertyMap.begin(), propertyMap.end(),
+ [&prop](auto l_keyValue) {
+ prop[l_keyValue.first] = l_keyValue.second;
+ });
+ }
+ catch (const std::exception& l_ex)
+ {
+ // ToDo:: Log PEL
+ logging::logMessage(
+ "Inserting properties into interface[" + interface +
+ "] map is failed, reason: " + std::string(l_ex.what()));
+ }
+ }
+ else
+ {
+ map.emplace(interface, propertyMap);
+ }
+}
+
+/**
+ * @brief API to expand unpanded location code.
+ *
+ * Note: The API handles all the exception internally, in case of any error
+ * unexpanded location code will be returned as it is.
+ *
+ * @param[in] unexpandedLocationCode - Unexpanded location code.
+ * @param[in] parsedVpdMap - Parsed VPD map.
+ * @return Expanded location code. In case of any error, unexpanded is returned
+ * as it is.
+ */
+inline std::string
+ getExpandedLocationCode(const std::string& unexpandedLocationCode,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ auto expanded{unexpandedLocationCode};
+
+ try
+ {
+ // Expanded location code is formed by combining two keywords
+ // depending on type in unexpanded one. Second one is always "SE".
+ std::string kwd1, kwd2{constants::kwdSE};
+
+ // interface to search for required keywords;
+ std::string kwdInterface;
+
+ // record which holds the required keywords.
+ std::string recordName;
+
+ auto pos = unexpandedLocationCode.find("fcs");
+ if (pos != std::string::npos)
+ {
+ kwd1 = constants::kwdFC;
+ kwdInterface = constants::vcenInf;
+ recordName = constants::recVCEN;
+ }
+ else
+ {
+ pos = unexpandedLocationCode.find("mts");
+ if (pos != std::string::npos)
+ {
+ kwd1 = constants::kwdTM;
+ kwdInterface = constants::vsysInf;
+ recordName = constants::recVSYS;
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Error detecting type of unexpanded location code.");
+ }
+ }
+
+ std::string firstKwdValue, secondKwdValue;
+
+ if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
+ ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
+ {
+ auto itrToVCEN = (*ipzVpdMap).find(recordName);
+ // The exceptions will be cautght at end.
+ getKwVal(itrToVCEN->second, kwd1, firstKwdValue);
+ getKwVal(itrToVCEN->second, kwd2, secondKwdValue);
+ }
+ else
+ {
+ std::array<const char*, 1> interfaceList = {kwdInterface.c_str()};
+
+ types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
+ std::string(constants::systemVpdInvPath), interfaceList);
+
+ if (mapperRetValue.empty())
+ {
+ throw std::runtime_error("Mapper failed to get service");
+ }
+
+ const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
+
+ auto retVal = dbusUtility::readDbusProperty(
+ serviceName, std::string(constants::systemVpdInvPath),
+ kwdInterface, kwd1);
+
+ if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
+ {
+ firstKwdValue.assign(
+ reinterpret_cast<const char*>(kwdVal->data()),
+ kwdVal->size());
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Failed to read value of " + kwd1 + " from Bus");
+ }
+
+ retVal = dbusUtility::readDbusProperty(
+ serviceName, std::string(constants::systemVpdInvPath),
+ kwdInterface, kwd2);
+
+ if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
+ {
+ secondKwdValue.assign(
+ reinterpret_cast<const char*>(kwdVal->data()),
+ kwdVal->size());
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Failed to read value of " + kwd2 + " from Bus");
+ }
+ }
+
+ if (unexpandedLocationCode.find("fcs") != std::string::npos)
+ {
+ // TODO: See if ND0 can be placed in the JSON
+ expanded.replace(
+ pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
+ }
+ else
+ {
+ replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
+ expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ logging::logMessage("Failed to expand location code with exception: " +
+ std::string(ex.what()));
+ }
+
+ return expanded;
+}
+
+/**
+ * @brief An API to get VPD in a vector.
+ *
+ * The vector is required by the respective parser to fill the VPD map.
+ * Note: API throws exception in case of failure. Caller needs to handle.
+ *
+ * @param[in] vpdFilePath - EEPROM path of the FRU.
+ * @param[out] vpdVector - VPD in vector form.
+ * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
+ */
+inline void getVpdDataInVector(const std::string& vpdFilePath,
+ types::BinaryVector& vpdVector,
+ size_t& vpdStartOffset)
+{
+ try
+ {
+ std::fstream vpdFileStream;
+ vpdFileStream.exceptions(
+ std::ifstream::badbit | std::ifstream::failbit);
+ vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
+ auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
+ static_cast<uintmax_t>(65504));
+ vpdVector.resize(vpdSizeToRead);
+
+ vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
+ vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
+ vpdSizeToRead);
+
+ vpdVector.resize(vpdFileStream.gcount());
+ vpdFileStream.clear(std::ios_base::eofbit);
+ }
+ catch (const std::ifstream::failure& fail)
+ {
+ std::cerr << "Exception in file handling [" << vpdFilePath
+ << "] error : " << fail.what();
+ throw;
+ }
+}
+
+/**
+ * @brief An API to get D-bus representation of given VPD keyword.
+ *
+ * @param[in] i_keywordName - VPD keyword name.
+ *
+ * @return D-bus representation of given keyword.
+ */
+inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName)
+{
+ // Check for "#" prefixed VPD keyword.
+ if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
+ (i_keywordName.at(0) == constants::POUND_KW))
+ {
+ // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
+ // prefixed keywords.
+ return (std::string(constants::POUND_KW_PREFIX) +
+ i_keywordName.substr(1));
+ }
+
+ // Return the keyword name back, if D-bus representation is same as the VPD
+ // keyword name.
+ return i_keywordName;
+}
+
+/**
+ * @brief API to find CCIN in parsed VPD map.
+ *
+ * Few FRUs need some special handling. To identify those FRUs CCIN are used.
+ * The API will check from parsed VPD map if the FRU is the one with desired
+ * CCIN.
+ *
+ * @throw std::runtime_error
+ * @throw DataException
+ *
+ * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
+ * @param[in] i_parsedVpdMap - Parsed VPD map.
+ * @return True if found, false otherwise.
+ */
+inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
+ const types::VPDMapVariant& i_parsedVpdMap)
+{
+ if (i_JsonObject.empty())
+ {
+ throw std::runtime_error("Json object is empty. Can't find CCIN");
+ }
+
+ if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
+ {
+ auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
+ if (l_itrToRec == (*l_ipzVPDMap).end())
+ {
+ throw DataException(
+ "VINI record not found in parsed VPD. Can't find CCIN");
+ }
+
+ std::string l_ccinFromVpd;
+ vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC", l_ccinFromVpd);
+ if (l_ccinFromVpd.empty())
+ {
+ throw DataException("Empty CCIN value in VPD map. Can't find CCIN");
+ }
+
+ transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
+ l_ccinFromVpd.begin(), ::toupper);
+
+ for (std::string l_ccinValue : i_JsonObject["ccin"])
+ {
+ transform(l_ccinValue.begin(), l_ccinValue.end(),
+ l_ccinValue.begin(), ::toupper);
+
+ if (l_ccinValue.compare(l_ccinFromVpd) ==
+ constants::STR_CMP_SUCCESS)
+ {
+ // CCIN found
+ return true;
+ }
+ }
+
+ logging::logMessage("No match found for CCIN");
+ return false;
+ }
+
+ logging::logMessage("VPD type not supported. Can't find CCIN");
+ return false;
+}
+
+/**
+ * @brief API to reset data of a FRU populated under PIM.
+ *
+ * This API resets the data for particular interfaces of a FRU under PIM.
+ *
+ * @param[in] i_objectPath - DBus object path of the FRU.
+ * @param[in] io_interfaceMap - Interface and its properties map.
+ */
+inline void resetDataUnderPIM(const std::string& i_objectPath,
+ types::InterfaceMap& io_interfaceMap)
+{
+ try
+ {
+ std::array<const char*, 0> l_interfaces;
+ const types::MapperGetObject& l_getObjectMap =
+ dbusUtility::getObjectMap(i_objectPath, l_interfaces);
+
+ const std::vector<std::string>& l_vpdRelatedInterfaces{
+ constants::operationalStatusInf, constants::inventoryItemInf,
+ constants::assetInf};
+
+ for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
+ {
+ if (l_service.compare(constants::pimServiceName) !=
+ constants::STR_CMP_SUCCESS)
+ {
+ continue;
+ }
+
+ for (const auto& l_interface : l_interfaceList)
+ {
+ if ((l_interface.find(constants::ipzVpdInf) !=
+ std::string::npos) ||
+ ((std::find(l_vpdRelatedInterfaces.begin(),
+ l_vpdRelatedInterfaces.end(), l_interface)) !=
+ l_vpdRelatedInterfaces.end()))
+ {
+ const types::PropertyMap& l_propertyValueMap =
+ dbusUtility::getPropertyMap(l_service, i_objectPath,
+ l_interface);
+
+ types::PropertyMap l_propertyMap;
+
+ for (const auto& l_aProperty : l_propertyValueMap)
+ {
+ const std::string& l_propertyName = l_aProperty.first;
+ const auto& l_propertyValue = l_aProperty.second;
+
+ if (std::holds_alternative<types::BinaryVector>(
+ l_propertyValue))
+ {
+ l_propertyMap.emplace(l_propertyName,
+ types::BinaryVector{});
+ }
+ else if (std::holds_alternative<std::string>(
+ l_propertyValue))
+ {
+ l_propertyMap.emplace(l_propertyName,
+ std::string{});
+ }
+ else if (std::holds_alternative<bool>(l_propertyValue))
+ {
+ // ToDo -- Update the functional status property
+ // to true.
+ if (l_propertyName.compare("Present") ==
+ constants::STR_CMP_SUCCESS)
+ {
+ l_propertyMap.emplace(l_propertyName, false);
+ }
+ }
+ }
+ io_interfaceMap.emplace(l_interface,
+ std::move(l_propertyMap));
+ }
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
+ " with error: " + std::string(l_ex.what()));
+ }
+}
+} // namespace vpdSpecificUtility
+} // namespace vpd