| #pragma once | 
 |  | 
 | #include "tool_constants.hpp" | 
 | #include "tool_types.hpp" | 
 |  | 
 | #include <nlohmann/json.hpp> | 
 | #include <sdbusplus/bus.hpp> | 
 | #include <sdbusplus/exception.hpp> | 
 |  | 
 | #include <fstream> | 
 | #include <iostream> | 
 |  | 
 | namespace vpd | 
 | { | 
 | namespace utils | 
 | { | 
 | /** | 
 |  * @brief An API to read property from Dbus. | 
 |  * | 
 |  * API reads the property value for the specified interface and object path from | 
 |  * the given Dbus service. | 
 |  * | 
 |  * The caller of the API needs to validate the validity and correctness of the | 
 |  * type and value of data returned. The API will just fetch and return 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] i_serviceName - Name of the Dbus service. | 
 |  * @param[in] i_objectPath - Object path under the service. | 
 |  * @param[in] i_interface - Interface under which property exist. | 
 |  * @param[in] i_property - Property whose value is to be read. | 
 |  * | 
 |  * @return - Value read from Dbus. | 
 |  * | 
 |  * @throw std::runtime_error | 
 |  */ | 
 | inline types::DbusVariantType readDbusProperty( | 
 |     const std::string& i_serviceName, const std::string& i_objectPath, | 
 |     const std::string& i_interface, const std::string& i_property) | 
 | { | 
 |     types::DbusVariantType l_propertyValue; | 
 |  | 
 |     // Mandatory fields to make a dbus call. | 
 |     if (i_serviceName.empty() || i_objectPath.empty() || i_interface.empty() || | 
 |         i_property.empty()) | 
 |     { | 
 |         // TODO: Enable logging when verbose is enabled. | 
 |         /*std::cout << "One of the parameter to make Dbus read call is empty." | 
 |                   << std::endl;*/ | 
 |         throw std::runtime_error("Empty Parameter"); | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         auto l_bus = sdbusplus::bus::new_default(); | 
 |         auto l_method = | 
 |             l_bus.new_method_call(i_serviceName.c_str(), i_objectPath.c_str(), | 
 |                                   "org.freedesktop.DBus.Properties", "Get"); | 
 |         l_method.append(i_interface, i_property); | 
 |  | 
 |         auto result = l_bus.call(l_method); | 
 |         result.read(l_propertyValue); | 
 |     } | 
 |     catch (const sdbusplus::exception::SdBusError& l_ex) | 
 |     { | 
 |         // TODO: Enable logging when verbose is enabled. | 
 |         // std::cout << std::string(l_ex.what()) << std::endl; | 
 |         throw std::runtime_error(std::string(l_ex.what())); | 
 |     } | 
 |     return l_propertyValue; | 
 | } | 
 |  | 
 | /** | 
 |  * @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) noexcept | 
 | { | 
 |     types::PropertyMap l_propertyValueMap; | 
 |     if (i_service.empty() || i_objectPath.empty() || i_interface.empty()) | 
 |     { | 
 |         // TODO: Enable logging when verbose is enabled. | 
 |         // std::cout << "Invalid parameters to get property map" << std::endl; | 
 |         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) | 
 |     { | 
 |         // TODO: Enable logging when verbose is enabled. | 
 |         // std::cerr << "Failed to get property map for service: [" << i_service | 
 |         //           << "], object path: [" << i_objectPath | 
 |         //           << "] Error : " << l_ex.what() << std::endl; | 
 |     } | 
 |  | 
 |     return l_propertyValueMap; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief An API to print json data on stdout. | 
 |  * | 
 |  * @param[in] i_jsonData - JSON object. | 
 |  */ | 
 | inline void printJson(const nlohmann::json& i_jsonData) | 
 | { | 
 |     try | 
 |     { | 
 |         std::cout << i_jsonData.dump(constants::INDENTATION) << std::endl; | 
 |     } | 
 |     catch (const nlohmann::json::type_error& l_ex) | 
 |     { | 
 |         throw std::runtime_error( | 
 |             "Failed to dump JSON data, error: " + std::string(l_ex.what())); | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * @brief An API to convert binary value into ascii/hex representation. | 
 |  * | 
 |  * If given data contains printable characters, ASCII formated string value of | 
 |  * the input data will be returned. Otherwise if the data has any non-printable | 
 |  * value, returns the hex represented value of the given data in string format. | 
 |  * | 
 |  * @param[in] i_keywordValue - Data in binary format. | 
 |  * | 
 |  * @throw - Throws std::bad_alloc or std::terminate in case of error. | 
 |  * | 
 |  * @return - Returns the converted string value. | 
 |  */ | 
 | inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue) | 
 | { | 
 |     bool l_allPrintable = | 
 |         std::all_of(i_keywordValue.begin(), i_keywordValue.end(), | 
 |                     [](const auto& l_byte) { return std::isprint(l_byte); }); | 
 |  | 
 |     std::ostringstream l_oss; | 
 |     if (l_allPrintable) | 
 |     { | 
 |         l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end()); | 
 |     } | 
 |     else | 
 |     { | 
 |         l_oss << "0x"; | 
 |         for (const auto& l_byte : i_keywordValue) | 
 |         { | 
 |             l_oss << std::setfill('0') << std::setw(2) << std::hex | 
 |                   << static_cast<int>(l_byte); | 
 |         } | 
 |     } | 
 |  | 
 |     return l_oss.str(); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to read keyword's value from hardware. | 
 |  * | 
 |  * This API reads keyword's value by requesting DBus service(vpd-manager) who | 
 |  * hosts the 'ReadKeyword' method to read keyword's value. | 
 |  * | 
 |  * @param[in] i_eepromPath - EEPROM file path. | 
 |  * @param[in] i_paramsToReadData - Property whose value has to be read. | 
 |  * | 
 |  * @return - Value read from hardware | 
 |  * | 
 |  * @throw std::runtime_error, sdbusplus::exception::SdBusError | 
 |  */ | 
 | inline types::DbusVariantType readKeywordFromHardware( | 
 |     const std::string& i_eepromPath, | 
 |     const types::ReadVpdParams i_paramsToReadData) | 
 | { | 
 |     if (i_eepromPath.empty()) | 
 |     { | 
 |         throw std::runtime_error("Empty EEPROM path"); | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         types::DbusVariantType l_propertyValue; | 
 |  | 
 |         auto l_bus = sdbusplus::bus::new_default(); | 
 |  | 
 |         auto l_method = l_bus.new_method_call( | 
 |             constants::vpdManagerService, constants::vpdManagerObjectPath, | 
 |             constants::vpdManagerInfName, "ReadKeyword"); | 
 |  | 
 |         l_method.append(i_eepromPath, i_paramsToReadData); | 
 |         auto l_result = l_bus.call(l_method); | 
 |  | 
 |         l_result.read(l_propertyValue); | 
 |  | 
 |         return l_propertyValue; | 
 |     } | 
 |     catch (const sdbusplus::exception::SdBusError& l_error) | 
 |     { | 
 |         throw; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to save keyword's value on file. | 
 |  * | 
 |  * API writes keyword's value on the given file path. If the data is in hex | 
 |  * format, API strips '0x' and saves the value on the given file. | 
 |  * | 
 |  * @param[in] i_filePath - File path. | 
 |  * @param[in] i_keywordValue - Keyword's value. | 
 |  * | 
 |  * @return - true on successfully writing to file, false otherwise. | 
 |  */ | 
 | inline bool saveToFile(const std::string& i_filePath, | 
 |                        const std::string& i_keywordValue) | 
 | { | 
 |     bool l_returnStatus = false; | 
 |  | 
 |     if (i_keywordValue.empty()) | 
 |     { | 
 |         // ToDo: log only when verbose is enabled | 
 |         std::cerr << "Save to file[ " << i_filePath | 
 |                   << "] failed, reason: Empty keyword's value received" | 
 |                   << std::endl; | 
 |         return l_returnStatus; | 
 |     } | 
 |  | 
 |     std::string l_keywordValue{i_keywordValue}; | 
 |     if (i_keywordValue.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS) | 
 |     { | 
 |         l_keywordValue = i_keywordValue.substr(2); | 
 |     } | 
 |  | 
 |     std::ofstream l_outPutFileStream; | 
 |     l_outPutFileStream.exceptions( | 
 |         std::ifstream::badbit | std::ifstream::failbit); | 
 |     try | 
 |     { | 
 |         l_outPutFileStream.open(i_filePath); | 
 |  | 
 |         if (l_outPutFileStream.is_open()) | 
 |         { | 
 |             l_outPutFileStream.write(l_keywordValue.c_str(), | 
 |                                      l_keywordValue.size()); | 
 |             l_returnStatus = true; | 
 |         } | 
 |         else | 
 |         { | 
 |             // ToDo: log only when verbose is enabled | 
 |             std::cerr << "Error opening output file " << i_filePath | 
 |                       << std::endl; | 
 |         } | 
 |     } | 
 |     catch (const std::ios_base::failure& l_ex) | 
 |     { | 
 |         // ToDo: log only when verbose is enabled | 
 |         std::cerr | 
 |             << "Failed to write to file: " << i_filePath | 
 |             << ", either base folder path doesn't exist or internal error occured, error: " | 
 |             << l_ex.what() << '\n'; | 
 |     } | 
 |  | 
 |     return l_returnStatus; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to print data in JSON format on console | 
 |  * | 
 |  * @param[in] i_fruPath - FRU path. | 
 |  * @param[in] i_keywordName - Keyword name. | 
 |  * @param[in] i_keywordStrValue - Keyword's value. | 
 |  */ | 
 | inline void displayOnConsole(const std::string& i_fruPath, | 
 |                              const std::string& i_keywordName, | 
 |                              const std::string& i_keywordStrValue) | 
 | { | 
 |     nlohmann::json l_resultInJson = nlohmann::json::object({}); | 
 |     nlohmann::json l_keywordValInJson = nlohmann::json::object({}); | 
 |  | 
 |     l_keywordValInJson.emplace(i_keywordName, i_keywordStrValue); | 
 |     l_resultInJson.emplace(i_fruPath, l_keywordValInJson); | 
 |  | 
 |     printJson(l_resultInJson); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to write keyword's value. | 
 |  * | 
 |  * This API writes keyword's value by requesting DBus service(vpd-manager) who | 
 |  * hosts the 'UpdateKeyword' method to update keyword's value. | 
 |  * | 
 |  * @param[in] i_vpdPath - EEPROM or object path, where keyword is present. | 
 |  * @param[in] i_paramsToWriteData - Data required to update keyword's value. | 
 |  * | 
 |  * @return - Number of bytes written on success, -1 on failure. | 
 |  * | 
 |  * @throw - std::runtime_error, sdbusplus::exception::SdBusError | 
 |  */ | 
 | inline int writeKeyword(const std::string& i_vpdPath, | 
 |                         const types::WriteVpdParams& i_paramsToWriteData) | 
 | { | 
 |     if (i_vpdPath.empty()) | 
 |     { | 
 |         throw std::runtime_error("Empty path"); | 
 |     } | 
 |  | 
 |     int l_rc = constants::FAILURE; | 
 |     auto l_bus = sdbusplus::bus::new_default(); | 
 |  | 
 |     auto l_method = l_bus.new_method_call( | 
 |         constants::vpdManagerService, constants::vpdManagerObjectPath, | 
 |         constants::vpdManagerInfName, "UpdateKeyword"); | 
 |  | 
 |     l_method.append(i_vpdPath, i_paramsToWriteData); | 
 |     auto l_result = l_bus.call(l_method); | 
 |  | 
 |     l_result.read(l_rc); | 
 |     return l_rc; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to write keyword's value on hardware. | 
 |  * | 
 |  * This API writes keyword's value by requesting DBus service(vpd-manager) who | 
 |  * hosts the 'WriteKeywordOnHardware' method to update keyword's value. | 
 |  * | 
 |  * Note: This API updates keyword's value only on the given hardware path, any | 
 |  * backup or redundant EEPROM (if exists) paths won't get updated. | 
 |  * | 
 |  * @param[in] i_eepromPath - EEPROM where keyword is present. | 
 |  * @param[in] i_paramsToWriteData - Data required to update keyword's value. | 
 |  * | 
 |  * @return - Number of bytes written on success, -1 on failure. | 
 |  * | 
 |  * @throw - std::runtime_error, sdbusplus::exception::SdBusError | 
 |  */ | 
 | inline int writeKeywordOnHardware( | 
 |     const std::string& i_eepromPath, | 
 |     const types::WriteVpdParams& i_paramsToWriteData) | 
 | { | 
 |     if (i_eepromPath.empty()) | 
 |     { | 
 |         throw std::runtime_error("Empty path"); | 
 |     } | 
 |  | 
 |     int l_rc = constants::FAILURE; | 
 |     auto l_bus = sdbusplus::bus::new_default(); | 
 |  | 
 |     auto l_method = l_bus.new_method_call( | 
 |         constants::vpdManagerService, constants::vpdManagerObjectPath, | 
 |         constants::vpdManagerInfName, "WriteKeywordOnHardware"); | 
 |  | 
 |     l_method.append(i_eepromPath, i_paramsToWriteData); | 
 |     auto l_result = l_bus.call(l_method); | 
 |  | 
 |     l_result.read(l_rc); | 
 |  | 
 |     return l_rc; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to get data in binary format. | 
 |  * | 
 |  * This API converts given string value into array of binary data. | 
 |  * | 
 |  * @param[in] i_value - Input data. | 
 |  * | 
 |  * @return - Array of binary data on success, throws as exception in case | 
 |  * of any error. | 
 |  * | 
 |  * @throw std::runtime_error, std::out_of_range, std::bad_alloc, | 
 |  * std::invalid_argument | 
 |  */ | 
 | inline types::BinaryVector convertToBinary(const std::string& i_value) | 
 | { | 
 |     if (i_value.empty()) | 
 |     { | 
 |         throw std::runtime_error( | 
 |             "Provide a valid hexadecimal input. (Ex. 0x30313233)"); | 
 |     } | 
 |  | 
 |     std::vector<uint8_t> l_binaryValue{}; | 
 |  | 
 |     if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS) | 
 |     { | 
 |         if (i_value.length() % 2 != 0) | 
 |         { | 
 |             throw std::runtime_error( | 
 |                 "Write option accepts 2 digit hex numbers. (Ex. 0x1 " | 
 |                 "should be given as 0x01)."); | 
 |         } | 
 |  | 
 |         auto l_value = i_value.substr(2); | 
 |  | 
 |         if (l_value.empty()) | 
 |         { | 
 |             throw std::runtime_error( | 
 |                 "Provide a valid hexadecimal input. (Ex. 0x30313233)"); | 
 |         } | 
 |  | 
 |         if (l_value.find_first_not_of("0123456789abcdefABCDEF") != | 
 |             std::string::npos) | 
 |         { | 
 |             throw std::runtime_error("Provide a valid hexadecimal input."); | 
 |         } | 
 |  | 
 |         for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2) | 
 |         { | 
 |             uint8_t l_byte = static_cast<uint8_t>( | 
 |                 std::stoi(l_value.substr(l_pos, 2), nullptr, 16)); | 
 |             l_binaryValue.push_back(l_byte); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         l_binaryValue.assign(i_value.begin(), i_value.end()); | 
 |     } | 
 |     return l_binaryValue; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to parse respective JSON. | 
 |  * | 
 |  * @param[in] i_pathToJson - Path to JSON. | 
 |  * | 
 |  * @return Parsed JSON, throws exception in case of error. | 
 |  * | 
 |  * @throw std::runtime_error | 
 |  */ | 
 | inline nlohmann::json getParsedJson(const std::string& i_pathToJson) | 
 | { | 
 |     if (i_pathToJson.empty()) | 
 |     { | 
 |         throw std::runtime_error("Path to JSON is missing"); | 
 |     } | 
 |  | 
 |     std::error_code l_ec; | 
 |     if (!std::filesystem::exists(i_pathToJson, l_ec)) | 
 |     { | 
 |         std::string l_message{ | 
 |             "file system call failed for file: " + i_pathToJson}; | 
 |  | 
 |         if (l_ec) | 
 |         { | 
 |             l_message += ", error: " + l_ec.message(); | 
 |         } | 
 |         throw std::runtime_error(l_message); | 
 |     } | 
 |  | 
 |     if (std::filesystem::is_empty(i_pathToJson, l_ec)) | 
 |     { | 
 |         throw std::runtime_error("Empty file: " + i_pathToJson); | 
 |     } | 
 |     else if (l_ec) | 
 |     { | 
 |         throw std::runtime_error("is_empty file system call failed for file: " + | 
 |                                  i_pathToJson + ", error: " + l_ec.message()); | 
 |     } | 
 |  | 
 |     std::ifstream l_jsonFile(i_pathToJson); | 
 |     if (!l_jsonFile) | 
 |     { | 
 |         throw std::runtime_error("Failed to access Json path: " + i_pathToJson); | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         return nlohmann::json::parse(l_jsonFile); | 
 |     } | 
 |     catch (const nlohmann::json::parse_error& l_ex) | 
 |     { | 
 |         throw std::runtime_error("Failed to parse JSON file: " + i_pathToJson); | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to get list of interfaces under a given object path. | 
 |  * | 
 |  * Given a DBus object path, this API returns a map of service -> implemented | 
 |  * interface(s) under that object path. This API calls DBus method GetObject | 
 |  * hosted by ObjectMapper DBus service. | 
 |  * | 
 |  * @param[in] i_objectPath - DBus object path. | 
 |  * @param[in] i_constrainingInterfaces - An array of result set constraining | 
 |  * interfaces. | 
 |  * | 
 |  * @return On success, returns a map of service -> implemented interface(s), | 
 |  * else returns an empty map. The caller of this | 
 |  * API should check for empty map. | 
 |  */ | 
 | inline types::MapperGetObject GetServiceInterfacesForObject( | 
 |     const std::string& i_objectPath, | 
 |     const std::vector<std::string>& i_constrainingInterfaces) noexcept | 
 | { | 
 |     types::MapperGetObject l_serviceInfMap; | 
 |     if (i_objectPath.empty()) | 
 |     { | 
 |         // TODO: log only when verbose is enabled | 
 |         std::cerr << "Object path is empty." << std::endl; | 
 |         return l_serviceInfMap; | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         auto l_bus = sdbusplus::bus::new_default(); | 
 |         auto l_method = l_bus.new_method_call( | 
 |             constants::objectMapperService, constants::objectMapperObjectPath, | 
 |             constants::objectMapperInfName, "GetObject"); | 
 |  | 
 |         l_method.append(i_objectPath, i_constrainingInterfaces); | 
 |  | 
 |         auto l_result = l_bus.call(l_method); | 
 |         l_result.read(l_serviceInfMap); | 
 |     } | 
 |     catch (const sdbusplus::exception::SdBusError& l_ex) | 
 |     { | 
 |         // TODO: log only when verbose is enabled | 
 |         std::cerr << std::string(l_ex.what()) << std::endl; | 
 |     } | 
 |     return l_serviceInfMap; | 
 | } | 
 |  | 
 | /** @brief API to get list of sub tree paths for a given object path | 
 |  * | 
 |  * Given a DBus object path, this API returns a list of object paths under that | 
 |  * object path in the DBus tree. This API calls DBus method GetSubTreePaths | 
 |  * hosted by ObjectMapper DBus service. | 
 |  * | 
 |  * @param[in] i_objectPath - DBus object path. | 
 |  * @param[in] i_constrainingInterfaces - An array of result set constraining | 
 |  * interfaces. | 
 |  * @param[in] i_depth - The maximum subtree depth for which results should be | 
 |  * fetched. For unconstrained fetches use a depth of zero. | 
 |  * | 
 |  * @return On success, returns a std::vector<std::string> of object paths in | 
 |  * Phosphor Inventory Manager DBus service's tree, else returns an empty vector. | 
 |  * The caller of this API should check for empty vector. | 
 |  */ | 
 | inline std::vector<std::string> GetSubTreePaths( | 
 |     const std::string i_objectPath, const int i_depth = 0, | 
 |     const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept | 
 | { | 
 |     std::vector<std::string> l_objectPaths; | 
 |  | 
 |     try | 
 |     { | 
 |         auto l_bus = sdbusplus::bus::new_default(); | 
 |         auto l_method = l_bus.new_method_call( | 
 |             constants::objectMapperService, constants::objectMapperObjectPath, | 
 |             constants::objectMapperInfName, "GetSubTreePaths"); | 
 |  | 
 |         l_method.append(i_objectPath, i_depth, i_constrainingInterfaces); | 
 |  | 
 |         auto l_result = l_bus.call(l_method); | 
 |         l_result.read(l_objectPaths); | 
 |     } | 
 |     catch (const sdbusplus::exception::SdBusError& l_ex) | 
 |     { | 
 |         // TODO: log only when verbose is enabled | 
 |         std::cerr << std::string(l_ex.what()) << std::endl; | 
 |     } | 
 |     return l_objectPaths; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief A class to print data in tabular format | 
 |  * | 
 |  * This class implements methods to print data in a two dimensional tabular | 
 |  * format. All entries in the table must be in string format. | 
 |  * | 
 |  */ | 
 | class Table | 
 | { | 
 |     class Column : public types::TableColumnNameSizePair | 
 |     { | 
 |       public: | 
 |         /** | 
 |          * @brief API to get the name of the Column | 
 |          * | 
 |          * @return Name of the Column. | 
 |          */ | 
 |         const std::string& Name() const | 
 |         { | 
 |             return this->first; | 
 |         } | 
 |  | 
 |         /** | 
 |          * @brief API to get the width of the Column | 
 |          * | 
 |          * @return Width of the Column. | 
 |          */ | 
 |         std::size_t Width() const | 
 |         { | 
 |             return this->second; | 
 |         } | 
 |     }; | 
 |  | 
 |     // Current width of the table | 
 |     std::size_t m_currentWidth; | 
 |  | 
 |     // Character to be used as fill character between entries | 
 |     char m_fillCharacter; | 
 |  | 
 |     // Separator character to be used between columns | 
 |     char m_separator; | 
 |  | 
 |     // Array of columns | 
 |     std::vector<Column> m_columns; | 
 |  | 
 |     /** | 
 |      * @brief API to Print Header | 
 |      * | 
 |      * Header line prints the names of the Column headers separated by the | 
 |      * specified separator character and spaced accordingly. | 
 |      * | 
 |      * @throw std::out_of_range, std::length_error, std::bad_alloc | 
 |      */ | 
 |     void PrintHeader() const | 
 |     { | 
 |         for (const auto& l_column : m_columns) | 
 |         { | 
 |             PrintEntry(l_column.Name(), l_column.Width()); | 
 |         } | 
 |         std::cout << m_separator << std::endl; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @brief API to Print Horizontal Line | 
 |      * | 
 |      * A horizontal line is a sequence of '*'s. | 
 |      * | 
 |      * @throw std::out_of_range, std::length_error, std::bad_alloc | 
 |      */ | 
 |     void PrintHorizontalLine() const | 
 |     { | 
 |         std::cout << std::string(m_currentWidth, '*') << std::endl; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @brief API to print an entry in the table | 
 |      * | 
 |      * An entry is a separator character followed by the text to print. | 
 |      * The text is centre-aligned. | 
 |      * | 
 |      * @param[in] i_text - text to print | 
 |      * @param[in] i_columnWidth - width of the column | 
 |      * | 
 |      * @throw std::out_of_range, std::length_error, std::bad_alloc | 
 |      */ | 
 |     void PrintEntry(const std::string& i_text, std::size_t i_columnWidth) const | 
 |     { | 
 |         const std::size_t l_textLength{i_text.length()}; | 
 |  | 
 |         constexpr std::size_t l_minFillChars{3}; | 
 |         const std::size_t l_numFillChars = | 
 |             ((l_textLength >= i_columnWidth ? l_minFillChars | 
 |                                             : i_columnWidth - l_textLength)) - | 
 |             1; // -1 for the separator character | 
 |  | 
 |         const unsigned l_oddFill = l_numFillChars % 2; | 
 |  | 
 |         std::cout << m_separator | 
 |                   << std::string((l_numFillChars / 2) + l_oddFill, | 
 |                                  m_fillCharacter) | 
 |                   << i_text << std::string(l_numFillChars / 2, m_fillCharacter); | 
 |     } | 
 |  | 
 |   public: | 
 |     /** | 
 |      * @brief Table Constructor | 
 |      * | 
 |      * Parameterized constructor for a Table object | 
 |      * | 
 |      */ | 
 |     constexpr explicit Table(const char i_fillCharacter = ' ', | 
 |                              const char i_separator = '|') noexcept : | 
 |         m_currentWidth{0}, m_fillCharacter{i_fillCharacter}, | 
 |         m_separator{i_separator} | 
 |     {} | 
 |  | 
 |     // deleted methods | 
 |     Table(const Table&) = delete; | 
 |     Table operator=(const Table&) = delete; | 
 |     Table(const Table&&) = delete; | 
 |     Table operator=(const Table&&) = delete; | 
 |  | 
 |     ~Table() = default; | 
 |  | 
 |     /** | 
 |      * @brief API to add column to Table | 
 |      * | 
 |      * @param[in] i_name - Name of the column. | 
 |      * | 
 |      * @param[in] i_width - Width to allocate for the column. | 
 |      * | 
 |      * @return On success returns 0, otherwise returns -1. | 
 |      */ | 
 |     int AddColumn(const std::string& i_name, std::size_t i_width) | 
 |     { | 
 |         if (i_width < i_name.length()) | 
 |             return constants::FAILURE; | 
 |         m_columns.emplace_back(types::TableColumnNameSizePair(i_name, i_width)); | 
 |         m_currentWidth += i_width; | 
 |         return constants::SUCCESS; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @brief API to print the Table to console. | 
 |      * | 
 |      * This API prints the table data to console. | 
 |      * | 
 |      * @param[in] i_tableData - The data to be printed. | 
 |      * | 
 |      * @return On success returns 0, otherwise returns -1. | 
 |      * | 
 |      * @throw std::out_of_range, std::length_error, std::bad_alloc | 
 |      */ | 
 |     int Print(const types::TableInputData& i_tableData) const | 
 |     { | 
 |         PrintHorizontalLine(); | 
 |         PrintHeader(); | 
 |         PrintHorizontalLine(); | 
 |  | 
 |         // print the table data | 
 |         for (const auto& l_row : i_tableData) | 
 |         { | 
 |             unsigned l_columnNumber{0}; | 
 |  | 
 |             // number of columns in input data is greater than the number of | 
 |             // columns specified in Table | 
 |             if (l_row.size() > m_columns.size()) | 
 |             { | 
 |                 return constants::FAILURE; | 
 |             } | 
 |  | 
 |             for (const auto& l_entry : l_row) | 
 |             { | 
 |                 PrintEntry(l_entry, m_columns[l_columnNumber].Width()); | 
 |  | 
 |                 ++l_columnNumber; | 
 |             } | 
 |             std::cout << m_separator << std::endl; | 
 |         } | 
 |         PrintHorizontalLine(); | 
 |         return constants::SUCCESS; | 
 |     } | 
 | }; | 
 |  | 
 | /** | 
 |  * @brief API to read value from file. | 
 |  * | 
 |  * The API reads the file and returns the read value. | 
 |  * | 
 |  * @param[in] i_filePath - File path. | 
 |  * | 
 |  * @return - Data from file if any in string format, else empty string. | 
 |  * | 
 |  */ | 
 | inline std::string readValueFromFile(const std::string& i_filePath) | 
 | { | 
 |     std::string l_valueRead{}; | 
 |  | 
 |     std::error_code l_ec; | 
 |     if (!std::filesystem::exists(i_filePath, l_ec)) | 
 |     { | 
 |         std::string l_message{ | 
 |             "filesystem call exists failed for file [" + i_filePath + "]."}; | 
 |  | 
 |         if (l_ec) | 
 |         { | 
 |             l_message += " Error: " + l_ec.message(); | 
 |         } | 
 |  | 
 |         std::cerr << l_message << std::endl; | 
 |         return l_valueRead; | 
 |     } | 
 |  | 
 |     if (std::filesystem::is_empty(i_filePath, l_ec)) | 
 |     { | 
 |         std::cerr << "File[" << i_filePath << "] is empty." << std::endl; | 
 |         return l_valueRead; | 
 |     } | 
 |     else if (l_ec) | 
 |     { | 
 |         std::cerr << "is_empty file system call failed for file[" << i_filePath | 
 |                   << "] , error: " << l_ec.message() << std::endl; | 
 |  | 
 |         return l_valueRead; | 
 |     } | 
 |  | 
 |     std::ifstream l_fileStream; | 
 |     l_fileStream.exceptions(std::ifstream::badbit | std::ifstream::failbit); | 
 |     try | 
 |     { | 
 |         l_fileStream.open(i_filePath, std::ifstream::in); | 
 |  | 
 |         std::getline(l_fileStream, l_valueRead); | 
 |  | 
 |         l_fileStream.close(); | 
 |         return l_valueRead; | 
 |     } | 
 |     catch (const std::ifstream::failure& l_ex) | 
 |     { | 
 |         if (l_fileStream.is_open()) | 
 |         { | 
 |             l_fileStream.close(); | 
 |         } | 
 |  | 
 |         std::cerr << "File read operation failed for path[" << i_filePath | 
 |                   << "], error: " << l_ex.what() << std::endl; | 
 |     } | 
 |  | 
 |     return l_valueRead; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to check if chassis is powered off. | 
 |  * | 
 |  * This API queries Phosphor Chassis State Manager to know whether | 
 |  * chassis is powered off. | 
 |  * | 
 |  * @return true if chassis is powered off, false otherwise. | 
 |  */ | 
 | inline bool isChassisPowerOff() | 
 | { | 
 |     try | 
 |     { | 
 |         // ToDo: Handle in case system has multiple chassis | 
 |         auto l_powerState = readDbusProperty( | 
 |             constants::chassisStateManagerService, | 
 |             constants::chassisStateManagerObjectPath, | 
 |             constants::chassisStateManagerInfName, "CurrentPowerState"); | 
 |  | 
 |         if (auto l_curPowerState = std::get_if<std::string>(&l_powerState); | 
 |             l_curPowerState && | 
 |             ("xyz.openbmc_project.State.Chassis.PowerState.Off" == | 
 |              *l_curPowerState)) | 
 |         { | 
 |             return true; | 
 |         } | 
 |     } | 
 |     catch (const std::exception& l_ex) | 
 |     { | 
 |         // Todo: Enale log when verbose is enabled | 
 |         std::cerr << l_ex.what() << std::endl; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | /** | 
 |  * @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) noexcept | 
 | { | 
 |     bool l_retVal = false; | 
 |  | 
 |     try | 
 |     { | 
 |         auto l_bus = sdbusplus::bus::new_default(); | 
 |         auto l_method = l_bus.new_method_call( | 
 |             constants::dbusService, constants::dbusObjectPath, | 
 |             constants::dbusInterface, "NameHasOwner"); | 
 |         l_method.append(i_serviceName); | 
 |  | 
 |         l_bus.call(l_method).read(l_retVal); | 
 |     } | 
 |     catch (const sdbusplus::exception::SdBusError& l_ex) | 
 |     { | 
 |         std::cout << "Call to check service status failed with exception: " + | 
 |                          std::string(l_ex.what()) | 
 |                   << std::endl; | 
 |     } | 
 |  | 
 |     return l_retVal; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief API to call "GetAttribute" method under BIOS Config Manager. | 
 |  * | 
 |  * The API reads the given attribute from BIOS Config Manager and returns a | 
 |  * variant containing current value for that attribute if the value is 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) noexcept | 
 | { | 
 |     types::BiosGetAttrRetType l_attributeVal; | 
 |  | 
 |     try | 
 |     { | 
 |         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); | 
 |  | 
 |         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) | 
 |     { | 
 |         // TODO : enable logging when verbose is implemented | 
 |         std::cerr << "Failed to read BIOS Attribute: " + i_attributeName + | 
 |                          " due to error " + std::string(l_ex.what()) | 
 |                   << std::endl; | 
 |     } | 
 |  | 
 |     return std::get<1>(l_attributeVal); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief Converts string to lower case. | 
 |  * | 
 |  * @param [in,out] io_string - Input string. | 
 |  * | 
 |  * @throw std::terminate, std::bad_alloc | 
 |  */ | 
 | inline void toLower(std::string& io_string) | 
 | { | 
 |     std::transform(io_string.begin(), io_string.end(), io_string.begin(), | 
 |                    [](const unsigned char& l_char) { | 
 |                        return std::tolower(l_char); | 
 |                    }); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief Converts an integral data value to a vector of bytes. | 
 |  * The LSB of integer is copied to MSB of the vector. | 
 |  * | 
 |  * @param[in] i_integralData - Input integral data. | 
 |  * @param[in] i_numBytesCopy - Number of bytes to copy. | 
 |  * | 
 |  * @return - On success, returns the Binary vector representation of the | 
 |  * integral data, empty binary vector otherwise. | 
 |  * | 
 |  * @throw std::length_error | 
 |  */ | 
 | template <typename T> | 
 |     requires std::integral<T> | 
 | inline types::BinaryVector convertIntegralTypeToBytes( | 
 |     const T& i_integralData, size_t i_numBytesCopy = constants::VALUE_1) | 
 | { | 
 |     types::BinaryVector l_result; | 
 |     constexpr auto l_byteMask{0xFF}; | 
 |  | 
 |     l_result.resize(i_numBytesCopy, constants::VALUE_0); | 
 |  | 
 |     // sanitize number of bytes to copy | 
 |     if (i_numBytesCopy > sizeof(T)) | 
 |     { | 
 |         i_numBytesCopy = sizeof(T); | 
 |     } | 
 |  | 
 |     // LSB of source -> MSB of result | 
 |     for (size_t l_byte = 0; l_byte < i_numBytesCopy; ++l_byte) | 
 |     { | 
 |         l_result[l_result.size() - (l_byte + constants::VALUE_1)] = | 
 |             (i_integralData >> (l_byte * constants::VALUE_8)) & l_byteMask; | 
 |     } | 
 |     return l_result; | 
 | } | 
 |  | 
 | } // namespace utils | 
 | } // namespace vpd |