diff --git a/vpd-tool/include/tool_constants.hpp b/vpd-tool/include/tool_constants.hpp index c8a2a1ae..ca5d8718 100644 --- a/vpd-tool/include/tool_constants.hpp +++ b/vpd-tool/include/tool_constants.hpp @@ -8,6 +8,11 @@ namespace constants { static constexpr auto KEYWORD_SIZE = 2; static constexpr auto RECORD_SIZE = 4; +static constexpr auto INDENTATION = 4; +constexpr auto inventoryManagerService = + "xyz.openbmc_project.Inventory.Manager"; +constexpr auto baseInventoryPath = "/xyz/openbmc_project/inventory"; +constexpr auto ipzVpdInfPrefix = "com.ibm.ipzvpd."; } // namespace constants } // namespace vpd diff --git a/vpd-tool/include/tool_types.hpp b/vpd-tool/include/tool_types.hpp new file mode 100644 index 00000000..b518b909 --- /dev/null +++ b/vpd-tool/include/tool_types.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace vpd +{ +namespace types +{ +using BinaryVector = std::vector; + +// This covers mostly all the data type supported over DBus for a property. +// clang-format off +using DbusVariantType = std::variant< + std::vector>, + std::vector, + std::vector, + std::string, + int64_t, + uint64_t, + double, + int32_t, + uint32_t, + int16_t, + uint16_t, + uint8_t, + bool, + BinaryVector, + std::vector, + std::vector, + sdbusplus::message::object_path, + std::tuple>>, + std::vector>, + std::vector>>, + std::vector>, + std::vector> + >; +} // namespace types +} // namespace vpd diff --git a/vpd-tool/include/tool_utils.hpp b/vpd-tool/include/tool_utils.hpp new file mode 100644 index 00000000..91eca66f --- /dev/null +++ b/vpd-tool/include/tool_utils.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include "tool_types.hpp" + +#include +#include +#include + +#include + +extern bool VerboseMode; + +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, if success. + * If failed, empty variant. + */ +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()) + { + if (VerboseMode) + { + std::cout << "One of the parameter to make Dbus read call is empty." + << std::endl; + } + return l_propertyValue; + } + + 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) + { + if (VerboseMode) + { + std::cout << std::string(l_ex.what()) << std::endl; + } + } + return l_propertyValue; +} + +/** + * @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 hex value to string. + * + * 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 hex. + * + * @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(l_byte); + } + } + + return l_oss.str(); +} +} // namespace utils +} // namespace vpd diff --git a/vpd-tool/include/vpd_tool.hpp b/vpd-tool/include/vpd_tool.hpp index 1fc52839..f2b236cb 100644 --- a/vpd-tool/include/vpd_tool.hpp +++ b/vpd-tool/include/vpd_tool.hpp @@ -25,17 +25,17 @@ class VpdTool * If the provided i_onHardware option is true, read keyword's value from * the hardware. Otherwise read keyword's value from DBus. * - * @param[in] i_fruPath - DBus object path or EEPROM path. + * @param[in] i_vpdPath - DBus object path or EEPROM path. * @param[in] i_recordName - Record name. * @param[in] i_keywordName - Keyword name. - * @param[in] i_onHardware - True if i_fruPath is EEPROM path, false + * @param[in] i_onHardware - True if i_vpdPath is EEPROM path, false * otherwise. * @param[in] i_fileToSave - File path to save keyword's value, if not given * result will redirect to a console. * * @return On success return 0, otherwise return -1. */ - int readKeyword(const std::string& i_fruPath, + int readKeyword(const std::string& i_vpdPath, const std::string& i_recordName, const std::string& i_keywordName, const bool i_onHardware, const std::string& i_fileToSave = {}); diff --git a/vpd-tool/meson.build b/vpd-tool/meson.build index f79f60ac..403c9cb8 100644 --- a/vpd-tool/meson.build +++ b/vpd-tool/meson.build @@ -5,7 +5,8 @@ else CLI11_dep = dependency('CLI11') endif -dependency_list = [CLI11_dep] +sdbusplus = dependency('sdbusplus', fallback: [ 'sdbusplus', 'sdbusplus_dep' ]) +dependency_list = [CLI11_dep, sdbusplus] sources = ['src/vpd_tool_main.cpp', 'src/vpd_tool.cpp'] diff --git a/vpd-tool/src/vpd_tool.cpp b/vpd-tool/src/vpd_tool.cpp index 9da6f763..c2f61139 100644 --- a/vpd-tool/src/vpd_tool.cpp +++ b/vpd-tool/src/vpd_tool.cpp @@ -1,20 +1,78 @@ #include "vpd_tool.hpp" +#include "tool_constants.hpp" +#include "tool_types.hpp" +#include "tool_utils.hpp" + +#include + +extern bool VerboseMode; + namespace vpd { -int VpdTool::readKeyword(const std::string& i_fruPath, +int VpdTool::readKeyword(const std::string& i_vpdPath, const std::string& i_recordName, const std::string& i_keywordName, const bool i_onHardware, const std::string& i_fileToSave) { - // ToDo: Need to add implementation details - (void)i_fruPath; - (void)i_recordName; - (void)i_keywordName; - (void)i_onHardware; - (void)i_fileToSave; - - return 0; + int l_rc = -1; + try + { + types::DbusVariantType l_keywordValue; + if (i_onHardware) + { + // TODO: Implement read keyword's value from hardware + } + else + { + std::string l_inventoryObjectPath(constants::baseInventoryPath + + i_vpdPath); + + l_keywordValue = utils::readDbusProperty( + constants::inventoryManagerService, l_inventoryObjectPath, + constants::ipzVpdInfPrefix + i_recordName, i_keywordName); + } + + if (const auto l_value = + std::get_if(&l_keywordValue); + l_value && !l_value->empty()) + { + const std::string& l_keywordStrValue = + utils::getPrintableValue(*l_value); + + if (i_fileToSave.empty()) + { + nlohmann::json l_resultInJson = nlohmann::json::object({}); + nlohmann::json l_keywordValInJson = nlohmann::json::object({}); + + l_keywordValInJson.emplace(i_keywordName, l_keywordStrValue); + l_resultInJson.emplace(i_vpdPath, l_keywordValInJson); + + utils::printJson(l_resultInJson); + } + else + { + // TODO: Write result to a given file path. + } + l_rc = 0; + } + else if (VerboseMode) + { + std::cout << "Invalid data type or empty data received." + << std::endl; + } + } + catch (const std::exception& l_ex) + { + if (VerboseMode) + { + std::cerr << "Read keyword's value for path: " << i_vpdPath + << ", Record: " << i_recordName + << ", Keyword: " << i_keywordName + << " is failed, exception: " << l_ex.what() << std::endl; + } + } + return l_rc; } } // namespace vpd diff --git a/vpd-tool/src/vpd_tool_main.cpp b/vpd-tool/src/vpd_tool_main.cpp index 44339ceb..acd70d46 100644 --- a/vpd-tool/src/vpd_tool_main.cpp +++ b/vpd-tool/src/vpd_tool_main.cpp @@ -1,10 +1,13 @@ #include "tool_constants.hpp" +#include "vpd_tool.hpp" #include #include #include +bool VerboseMode = false; + int main(int argc, char** argv) { int l_rc = -1; @@ -45,21 +48,27 @@ int main(int argc, char** argv) auto l_hardwareFlag = l_app.add_flag("--Hardware, -H", "CAUTION: Developer only option."); + auto l_verboseFlag = l_app.add_flag("--verbose, -v", "Enable Verbose Mode"); + + // ToDo: Take offset value from user for hardware path. + CLI11_PARSE(l_app, argc, argv); - if (*l_objectOption && l_vpdPath.empty()) + if ((l_objectOption->count() > 0) && l_vpdPath.empty()) { std::cout << "Given path is empty." << std::endl; - return -1; + return l_rc; } - if (*l_recordOption && (l_recordName.size() != vpd::constants::RECORD_SIZE)) + if ((l_recordOption->count() > 0) && + (l_recordName.size() != vpd::constants::RECORD_SIZE)) { std::cerr << "Record " << l_recordName << " is not supported." << std::endl; + return l_rc; } - if (*l_keywordOption && + if ((l_keywordOption->count() > 0) && (l_keywordName.size() != vpd::constants::KEYWORD_SIZE)) { std::cerr << "Keyword " << l_keywordName << " is not supported." @@ -67,18 +76,26 @@ int main(int argc, char** argv) return l_rc; } - if (*l_hardwareFlag && !std::filesystem::exists(l_vpdPath)) - { - std::cerr << "Given EEPROM file path doesn't exist : " + l_vpdPath - << std::endl; - return l_rc; - } - (void)l_fileOption; - if (*l_readFlag) + VerboseMode = ((l_verboseFlag->count() > 0) ? true : false); + + if (l_readFlag->count() > 0) { - // TODO: call read keyword implementation from here. + if ((l_hardwareFlag->count() > 0) && + !std::filesystem::exists(l_vpdPath)) + { + std::cerr << "Given EEPROM file path doesn't exist : " + l_vpdPath + << std::endl; + return l_rc; + } + + bool l_isHardwareOperation = ((l_hardwareFlag->count() > 0) ? true + : false); + vpd::VpdTool l_vpdToolObj; + + l_rc = l_vpdToolObj.readKeyword(l_vpdPath, l_recordName, l_keywordName, + l_isHardwareOperation, l_filePath); } else {