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-tool/src/vpd_tool.cpp b/vpd-tool/src/vpd_tool.cpp
new file mode 100644
index 0000000..f6e4cd9
--- /dev/null
+++ b/vpd-tool/src/vpd_tool.cpp
@@ -0,0 +1,1195 @@
+#include "config.h"
+
+#include "vpd_tool.hpp"
+
+#include "tool_constants.hpp"
+#include "tool_types.hpp"
+#include "tool_utils.hpp"
+
+#include <iostream>
+#include <regex>
+#include <tuple>
+namespace vpd
+{
+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)
+{
+ int l_rc = constants::FAILURE;
+ try
+ {
+ types::DbusVariantType l_keywordValue;
+ if (i_onHardware)
+ {
+ l_keywordValue = utils::readKeywordFromHardware(
+ i_vpdPath, std::make_tuple(i_recordName, i_keywordName));
+ }
+ 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<types::BinaryVector>(&l_keywordValue);
+ l_value && !l_value->empty())
+ {
+ // ToDo: Print value in both ASCII and hex formats
+ const std::string& l_keywordStrValue =
+ utils::getPrintableValue(*l_value);
+
+ if (i_fileToSave.empty())
+ {
+ utils::displayOnConsole(i_vpdPath, i_keywordName,
+ l_keywordStrValue);
+ l_rc = constants::SUCCESS;
+ }
+ else
+ {
+ if (utils::saveToFile(i_fileToSave, l_keywordStrValue))
+ {
+ std::cout
+ << "Value read is saved on the file: " << i_fileToSave
+ << std::endl;
+ l_rc = constants::SUCCESS;
+ }
+ else
+ {
+ std::cerr
+ << "Error while saving the read value on the file: "
+ << i_fileToSave
+ << "\nDisplaying the read value on console"
+ << std::endl;
+ utils::displayOnConsole(i_vpdPath, i_keywordName,
+ l_keywordStrValue);
+ }
+ }
+ }
+ else
+ {
+ // TODO: Enable logging when verbose is enabled.
+ // std::cout << "Invalid data type or empty data received." <<
+ // std::endl;
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ /*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;
+}
+
+int VpdTool::dumpObject(std::string i_fruPath) const noexcept
+{
+ int l_rc{constants::FAILURE};
+ try
+ {
+ // ToDo: For PFuture system take only full path from the user.
+ i_fruPath = constants::baseInventoryPath + i_fruPath;
+
+ nlohmann::json l_resultJsonArray = nlohmann::json::array({});
+ const nlohmann::json l_fruJson = getFruProperties(i_fruPath);
+ if (!l_fruJson.empty())
+ {
+ l_resultJsonArray += l_fruJson;
+
+ utils::printJson(l_resultJsonArray);
+ }
+ else
+ {
+ std::cout << "FRU [" << i_fruPath
+ << "] is not present in the system" << std::endl;
+ }
+ l_rc = constants::SUCCESS;
+ }
+ catch (std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ // std::cerr << "Dump Object failed for FRU [" << i_fruPath
+ // << "], Error: " << l_ex.what() << std::endl;
+ }
+ return l_rc;
+}
+
+nlohmann::json VpdTool::getFruProperties(const std::string& i_objectPath) const
+{
+ // check if FRU is present in the system
+ if (!isFruPresent(i_objectPath))
+ {
+ return nlohmann::json::object_t();
+ }
+
+ nlohmann::json l_fruJson = nlohmann::json::object_t({});
+
+ l_fruJson.emplace(i_objectPath, nlohmann::json::object_t({}));
+
+ auto& l_fruObject = l_fruJson[i_objectPath];
+
+ const auto l_prettyNameInJson = getInventoryPropertyJson<std::string>(
+ i_objectPath, constants::inventoryItemInf, "PrettyName");
+ if (!l_prettyNameInJson.empty())
+ {
+ l_fruObject.insert(l_prettyNameInJson.cbegin(),
+ l_prettyNameInJson.cend());
+ }
+
+ const auto l_locationCodeInJson = getInventoryPropertyJson<std::string>(
+ i_objectPath, constants::locationCodeInf, "LocationCode");
+ if (!l_locationCodeInJson.empty())
+ {
+ l_fruObject.insert(l_locationCodeInJson.cbegin(),
+ l_locationCodeInJson.cend());
+ }
+
+ const auto l_subModelInJson = getInventoryPropertyJson<std::string>(
+ i_objectPath, constants::assetInf, "SubModel");
+
+ if (!l_subModelInJson.empty() &&
+ !l_subModelInJson.value("SubModel", "").empty())
+ {
+ l_fruObject.insert(l_subModelInJson.cbegin(), l_subModelInJson.cend());
+ }
+
+ // Get the properties under VINI interface.
+
+ nlohmann::json l_viniPropertiesInJson = nlohmann::json::object({});
+
+ auto l_readViniKeyWord = [i_objectPath, &l_viniPropertiesInJson,
+ this](const std::string& i_keyWord) {
+ const nlohmann::json l_keyWordJson =
+ getInventoryPropertyJson<vpd::types::BinaryVector>(
+ i_objectPath, constants::kwdVpdInf, i_keyWord);
+ l_viniPropertiesInJson.insert(l_keyWordJson.cbegin(),
+ l_keyWordJson.cend());
+ };
+
+ const std::vector<std::string> l_viniKeywords = {"SN", "PN", "CC", "FN",
+ "DR"};
+
+ std::for_each(l_viniKeywords.cbegin(), l_viniKeywords.cend(),
+ l_readViniKeyWord);
+
+ if (!l_viniPropertiesInJson.empty())
+ {
+ l_fruObject.insert(l_viniPropertiesInJson.cbegin(),
+ l_viniPropertiesInJson.cend());
+ }
+
+ const auto l_typePropertyJson = getFruTypeProperty(i_objectPath);
+ if (!l_typePropertyJson.empty())
+ {
+ l_fruObject.insert(l_typePropertyJson.cbegin(),
+ l_typePropertyJson.cend());
+ }
+
+ return l_fruJson;
+}
+
+template <typename PropertyType>
+nlohmann::json VpdTool::getInventoryPropertyJson(
+ const std::string& i_objectPath, const std::string& i_interface,
+ const std::string& i_propertyName) const noexcept
+{
+ nlohmann::json l_resultInJson = nlohmann::json::object({});
+ try
+ {
+ types::DbusVariantType l_keyWordValue;
+
+ l_keyWordValue =
+ utils::readDbusProperty(constants::inventoryManagerService,
+ i_objectPath, i_interface, i_propertyName);
+
+ if (const auto l_value = std::get_if<PropertyType>(&l_keyWordValue))
+ {
+ if constexpr (std::is_same<PropertyType, std::string>::value)
+ {
+ l_resultInJson.emplace(i_propertyName, *l_value);
+ }
+ else if constexpr (std::is_same<PropertyType, bool>::value)
+ {
+ l_resultInJson.emplace(i_propertyName,
+ *l_value ? "true" : "false");
+ }
+ else if constexpr (std::is_same<PropertyType,
+ types::BinaryVector>::value)
+ {
+ const std::string& l_keywordStrValue =
+ vpd::utils::getPrintableValue(*l_value);
+
+ l_resultInJson.emplace(i_propertyName, l_keywordStrValue);
+ }
+ }
+ else
+ {
+ // TODO: Enable logging when verbose is enabled.
+ // std::cout << "Invalid data type received." << std::endl;
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ /*std::cerr << "Read " << i_propertyName << " value for FRU path: " <<
+ i_objectPath
+ << ", failed with exception: " << l_ex.what() << std::endl;*/
+ }
+ return l_resultInJson;
+}
+
+int VpdTool::fixSystemVpd() const noexcept
+{
+ int l_rc = constants::FAILURE;
+
+ nlohmann::json l_backupRestoreCfgJsonObj = getBackupRestoreCfgJsonObj();
+ if (!fetchKeywordInfo(l_backupRestoreCfgJsonObj))
+ {
+ return l_rc;
+ }
+
+ printSystemVpd(l_backupRestoreCfgJsonObj);
+
+ do
+ {
+ printFixSystemVpdOption(types::UserOption::UseBackupDataForAll);
+ printFixSystemVpdOption(
+ types::UserOption::UseSystemBackplaneDataForAll);
+ printFixSystemVpdOption(types::UserOption::MoreOptions);
+ printFixSystemVpdOption(types::UserOption::Exit);
+
+ int l_userSelectedOption = types::UserOption::Exit;
+ std::cin >> l_userSelectedOption;
+
+ std::cout << std::endl << std::string(191, '=') << std::endl;
+
+ if (types::UserOption::UseBackupDataForAll == l_userSelectedOption)
+ {
+ l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, true);
+ break;
+ }
+ else if (types::UserOption::UseSystemBackplaneDataForAll ==
+ l_userSelectedOption)
+ {
+ l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, false);
+ break;
+ }
+ else if (types::UserOption::MoreOptions == l_userSelectedOption)
+ {
+ l_rc = handleMoreOption(l_backupRestoreCfgJsonObj);
+ break;
+ }
+ else if (types::UserOption::Exit == l_userSelectedOption)
+ {
+ std::cout << "Exit successfully" << std::endl;
+ break;
+ }
+ else
+ {
+ std::cout << "Provide a valid option. Retry." << std::endl;
+ }
+ } while (true);
+
+ return l_rc;
+}
+
+int VpdTool::writeKeyword(
+ std::string i_vpdPath, const std::string& i_recordName,
+ const std::string& i_keywordName, const std::string& i_keywordValue,
+ const bool i_onHardware) noexcept
+{
+ int l_rc = constants::FAILURE;
+ try
+ {
+ if (i_vpdPath.empty() || i_recordName.empty() ||
+ i_keywordName.empty() || i_keywordValue.empty())
+ {
+ throw std::runtime_error("Received input is empty.");
+ }
+
+ auto l_paramsToWrite =
+ std::make_tuple(i_recordName, i_keywordName,
+ utils::convertToBinary(i_keywordValue));
+
+ if (i_onHardware)
+ {
+ l_rc = utils::writeKeywordOnHardware(i_vpdPath, l_paramsToWrite);
+ }
+ else
+ {
+ i_vpdPath = constants::baseInventoryPath + i_vpdPath;
+ l_rc = utils::writeKeyword(i_vpdPath, l_paramsToWrite);
+ }
+
+ if (l_rc > 0)
+ {
+ std::cout << "Data updated successfully " << std::endl;
+ l_rc = constants::SUCCESS;
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable log when verbose is enabled.
+ std::cerr << "Write keyword's value for path: " << i_vpdPath
+ << ", Record: " << i_recordName
+ << ", Keyword: " << i_keywordName
+ << " is failed. Exception: " << l_ex.what() << std::endl;
+ }
+ return l_rc;
+}
+
+nlohmann::json VpdTool::getBackupRestoreCfgJsonObj() const noexcept
+{
+ nlohmann::json l_parsedBackupRestoreJson{};
+ try
+ {
+ nlohmann::json l_parsedSystemJson =
+ utils::getParsedJson(INVENTORY_JSON_SYM_LINK);
+
+ // check for mandatory fields at this point itself.
+ if (!l_parsedSystemJson.contains("backupRestoreConfigPath"))
+ {
+ throw std::runtime_error(
+ "backupRestoreConfigPath tag is missing from system config JSON : " +
+ std::string(INVENTORY_JSON_SYM_LINK));
+ }
+
+ l_parsedBackupRestoreJson =
+ utils::getParsedJson(l_parsedSystemJson["backupRestoreConfigPath"]);
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << l_ex.what() << std::endl;
+ }
+
+ return l_parsedBackupRestoreJson;
+}
+
+int VpdTool::cleanSystemVpd() const noexcept
+{
+ try
+ {
+ // get the keyword map from backup_restore json
+ // iterate through the keyword map get default value of
+ // l_keywordName.
+ // use writeKeyword API to update default value on hardware,
+ // backup and D - Bus.
+ const nlohmann::json l_parsedBackupRestoreJson =
+ getBackupRestoreCfgJsonObj();
+
+ // check for mandatory tags
+ if (l_parsedBackupRestoreJson.contains("source") &&
+ l_parsedBackupRestoreJson.contains("backupMap") &&
+ l_parsedBackupRestoreJson["source"].contains("hardwarePath") &&
+ l_parsedBackupRestoreJson["backupMap"].is_array())
+ {
+ // get the source hardware path
+ const auto& l_hardwarePath =
+ l_parsedBackupRestoreJson["source"]["hardwarePath"];
+
+ // iterate through the backup map
+ for (const auto& l_aRecordKwInfo :
+ l_parsedBackupRestoreJson["backupMap"])
+ {
+ // check if Manufacturing Reset is required for this entry
+ const bool l_isMfgCleanRequired =
+ l_aRecordKwInfo.value("isManufactureResetRequired", false);
+
+ if (l_isMfgCleanRequired)
+ {
+ // get the Record name and Keyword name
+ const std::string& l_srcRecordName =
+ l_aRecordKwInfo.value("sourceRecord", "");
+ const std::string& l_srcKeywordName =
+ l_aRecordKwInfo.value("sourceKeyword", "");
+
+ // validate the Record name, Keyword name and the
+ // defaultValue
+ if (!l_srcRecordName.empty() && !l_srcKeywordName.empty() &&
+ l_aRecordKwInfo.contains("defaultValue") &&
+ l_aRecordKwInfo["defaultValue"].is_array())
+ {
+ const types::BinaryVector l_defaultBinaryValue =
+ l_aRecordKwInfo["defaultValue"]
+ .get<types::BinaryVector>();
+
+ // update the Keyword with default value, use D-Bus
+ // method UpdateKeyword exposed by vpd-manager.
+ // Note: writing to all paths (Primary EEPROM path,
+ // Secondary EEPROM path, D-Bus cache and Backup path)
+ // is the responsibility of vpd-manager's UpdateKeyword
+ // API
+ if (constants::FAILURE ==
+ utils::writeKeyword(
+ l_hardwarePath,
+ std::make_tuple(l_srcRecordName,
+ l_srcKeywordName,
+ l_defaultBinaryValue)))
+ {
+ // TODO: Enable logging when verbose
+ // is enabled.
+ std::cerr << "Failed to update " << l_srcRecordName
+ << ":" << l_srcKeywordName << std::endl;
+ }
+ }
+ else
+ {
+ std::cerr
+ << "Unrecognized Entry Record [" << l_srcRecordName
+ << "] Keyword [" << l_srcKeywordName
+ << "] in Backup Restore JSON backup map"
+ << std::endl;
+ }
+ } // mfgClean required check
+ } // keyword list loop
+ }
+ else // backupRestoreJson is not valid
+ {
+ std::cerr << "Backup Restore JSON is not valid" << std::endl;
+ }
+
+ // success/failure message
+ std::cout << "The critical keywords from system backplane VPD has "
+ "been reset successfully."
+ << std::endl;
+
+ } // try block end
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr
+ << "Manufacturing reset on system vpd keywords is unsuccessful. Error : "
+ << l_ex.what() << std::endl;
+ }
+ return constants::SUCCESS;
+}
+
+bool VpdTool::fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept
+{
+ bool l_returnValue = false;
+ try
+ {
+ if (io_parsedJsonObj.empty() || !io_parsedJsonObj.contains("source") ||
+ !io_parsedJsonObj.contains("destination") ||
+ !io_parsedJsonObj.contains("backupMap"))
+ {
+ throw std::runtime_error("Invalid JSON");
+ }
+
+ std::string l_srcVpdPath;
+ std::string l_dstVpdPath;
+
+ bool l_isSourceOnHardware = false;
+ if (l_srcVpdPath = io_parsedJsonObj["source"].value("hardwarePath", "");
+ !l_srcVpdPath.empty())
+ {
+ l_isSourceOnHardware = true;
+ }
+ else if (l_srcVpdPath =
+ io_parsedJsonObj["source"].value("inventoryPath", "");
+ l_srcVpdPath.empty())
+ {
+ throw std::runtime_error("Source path is empty in JSON");
+ }
+
+ bool l_isDestinationOnHardware = false;
+ if (l_dstVpdPath =
+ io_parsedJsonObj["destination"].value("hardwarePath", "");
+ !l_dstVpdPath.empty())
+ {
+ l_isDestinationOnHardware = true;
+ }
+ else if (l_dstVpdPath =
+ io_parsedJsonObj["destination"].value("inventoryPath", "");
+ l_dstVpdPath.empty())
+ {
+ throw std::runtime_error("Destination path is empty in JSON");
+ }
+
+ for (auto& l_aRecordKwInfo : io_parsedJsonObj["backupMap"])
+ {
+ const std::string& l_srcRecordName =
+ l_aRecordKwInfo.value("sourceRecord", "");
+ const std::string& l_srcKeywordName =
+ l_aRecordKwInfo.value("sourceKeyword", "");
+ const std::string& l_dstRecordName =
+ l_aRecordKwInfo.value("destinationRecord", "");
+ const std::string& l_dstKeywordName =
+ l_aRecordKwInfo.value("destinationKeyword", "");
+
+ if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
+ l_srcKeywordName.empty() || l_dstKeywordName.empty())
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cout << "Record or keyword not found in the JSON."
+ << std::endl;
+ continue;
+ }
+
+ types::DbusVariantType l_srcKeywordVariant;
+ if (l_isSourceOnHardware)
+ {
+ l_srcKeywordVariant = utils::readKeywordFromHardware(
+ l_srcVpdPath,
+ std::make_tuple(l_srcRecordName, l_srcKeywordName));
+ }
+ else
+ {
+ l_srcKeywordVariant = utils::readDbusProperty(
+ constants::inventoryManagerService, l_srcVpdPath,
+ constants::ipzVpdInfPrefix + l_srcRecordName,
+ l_srcKeywordName);
+ }
+
+ if (auto l_srcKeywordValue =
+ std::get_if<types::BinaryVector>(&l_srcKeywordVariant);
+ l_srcKeywordValue && !l_srcKeywordValue->empty())
+ {
+ l_aRecordKwInfo["sourcekeywordValue"] = *l_srcKeywordValue;
+ }
+ else
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cout
+ << "Invalid data type or empty data received, for source record: "
+ << l_srcRecordName << ", keyword: " << l_srcKeywordName
+ << std::endl;
+ continue;
+ }
+
+ types::DbusVariantType l_dstKeywordVariant;
+ if (l_isDestinationOnHardware)
+ {
+ l_dstKeywordVariant = utils::readKeywordFromHardware(
+ l_dstVpdPath,
+ std::make_tuple(l_dstRecordName, l_dstKeywordName));
+ }
+ else
+ {
+ l_dstKeywordVariant = utils::readDbusProperty(
+ constants::inventoryManagerService, l_dstVpdPath,
+ constants::ipzVpdInfPrefix + l_dstRecordName,
+ l_dstKeywordName);
+ }
+
+ if (auto l_dstKeywordValue =
+ std::get_if<types::BinaryVector>(&l_dstKeywordVariant);
+ l_dstKeywordValue && !l_dstKeywordValue->empty())
+ {
+ l_aRecordKwInfo["destinationkeywordValue"] = *l_dstKeywordValue;
+ }
+ else
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cout
+ << "Invalid data type or empty data received, for destination record: "
+ << l_dstRecordName << ", keyword: " << l_dstKeywordName
+ << std::endl;
+ continue;
+ }
+ }
+
+ l_returnValue = true;
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << l_ex.what() << std::endl;
+ }
+
+ return l_returnValue;
+}
+
+nlohmann::json
+ VpdTool::getFruTypeProperty(const std::string& i_objectPath) const noexcept
+{
+ nlohmann::json l_resultInJson = nlohmann::json::object({});
+ std::vector<std::string> l_pimInfList;
+
+ auto l_serviceInfMap = utils::GetServiceInterfacesForObject(
+ i_objectPath, std::vector<std::string>{constants::inventoryItemInf});
+ if (l_serviceInfMap.contains(constants::inventoryManagerService))
+ {
+ l_pimInfList = l_serviceInfMap[constants::inventoryManagerService];
+
+ // iterate through the list and find
+ // "xyz.openbmc_project.Inventory.Item.*"
+ for (const auto& l_interface : l_pimInfList)
+ {
+ if (l_interface.find(constants::inventoryItemInf) !=
+ std::string::npos &&
+ l_interface.length() >
+ std::string(constants::inventoryItemInf).length())
+ {
+ l_resultInJson.emplace("type", l_interface);
+ }
+ }
+ }
+ return l_resultInJson;
+}
+
+bool VpdTool::isFruPresent(const std::string& i_objectPath) const noexcept
+{
+ bool l_returnValue{false};
+ try
+ {
+ types::DbusVariantType l_keyWordValue;
+
+ l_keyWordValue = utils::readDbusProperty(
+ constants::inventoryManagerService, i_objectPath,
+ constants::inventoryItemInf, "Present");
+
+ if (const auto l_value = std::get_if<bool>(&l_keyWordValue))
+ {
+ l_returnValue = *l_value;
+ }
+ }
+ catch (const std::runtime_error& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ // std::cerr << "Failed to check \"Present\" property for FRU "
+ // << i_objectPath << " Error: " << l_ex.what() <<
+ // std::endl;
+ }
+ return l_returnValue;
+}
+
+void VpdTool::printFixSystemVpdOption(
+ const types::UserOption& i_option) const noexcept
+{
+ switch (i_option)
+ {
+ case types::UserOption::Exit:
+ std::cout << "Enter 0 => To exit successfully : ";
+ break;
+ case types::UserOption::UseBackupDataForAll:
+ std::cout << "Enter 1 => If you choose the data on backup for all "
+ "mismatching record-keyword pairs"
+ << std::endl;
+ break;
+ case types::UserOption::UseSystemBackplaneDataForAll:
+ std::cout << "Enter 2 => If you choose the data on primary for all "
+ "mismatching record-keyword pairs"
+ << std::endl;
+ break;
+ case types::UserOption::MoreOptions:
+ std::cout << "Enter 3 => If you wish to explore more options"
+ << std::endl;
+ break;
+ case types::UserOption::UseBackupDataForCurrent:
+ std::cout << "Enter 4 => If you choose the data on backup as the "
+ "right value"
+ << std::endl;
+ break;
+ case types::UserOption::UseSystemBackplaneDataForCurrent:
+ std::cout << "Enter 5 => If you choose the data on primary as the "
+ "right value"
+ << std::endl;
+ break;
+ case types::UserOption::NewValueOnBoth:
+ std::cout
+ << "Enter 6 => If you wish to enter a new value to update "
+ "both on backup and primary"
+ << std::endl;
+ break;
+ case types::UserOption::SkipCurrent:
+ std::cout << "Enter 7 => If you wish to skip the above "
+ "record-keyword pair"
+ << std::endl;
+ break;
+ }
+}
+
+int VpdTool::dumpInventory(bool i_dumpTable) const noexcept
+{
+ int l_rc{constants::FAILURE};
+
+ try
+ {
+ // get all object paths under PIM
+ const auto l_objectPaths = utils::GetSubTreePaths(
+ constants::baseInventoryPath, 0,
+ std::vector<std::string>{constants::inventoryItemInf});
+
+ if (!l_objectPaths.empty())
+ {
+ nlohmann::json l_resultInJson = nlohmann::json::array({});
+
+ std::for_each(l_objectPaths.begin(), l_objectPaths.end(),
+ [&](const auto& l_objectPath) {
+ const auto l_fruJson =
+ getFruProperties(l_objectPath);
+ if (!l_fruJson.empty())
+ {
+ if (l_resultInJson.empty())
+ {
+ l_resultInJson += l_fruJson;
+ }
+ else
+ {
+ l_resultInJson.at(0).insert(
+ l_fruJson.cbegin(), l_fruJson.cend());
+ }
+ }
+ });
+
+ if (i_dumpTable)
+ {
+ // create Table object
+ utils::Table l_inventoryTable{};
+
+ // columns to be populated in the Inventory table
+ const std::vector<types::TableColumnNameSizePair>
+ l_tableColumns = {
+ {"FRU", 100}, {"CC", 6}, {"DR", 20},
+ {"LocationCode", 32}, {"PN", 8}, {"PrettyName", 80},
+ {"SubModel", 10}, {"SN", 15}, {"type", 60}};
+
+ types::TableInputData l_tableData;
+
+ // First prepare the Table Columns
+ for (const auto& l_column : l_tableColumns)
+ {
+ if (constants::FAILURE ==
+ l_inventoryTable.AddColumn(l_column.first,
+ l_column.second))
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "Failed to add column " << l_column.first
+ << " in Inventory Table." << std::endl;
+ }
+ }
+
+ // iterate through the json array
+ for (const auto& l_fruEntry : l_resultInJson[0].items())
+ {
+ // if object path ends in "unit([0-9][0-9]?)", skip adding
+ // the object path in the table
+ if (std::regex_search(l_fruEntry.key(),
+ std::regex("unit([0-9][0-9]?)")))
+ {
+ continue;
+ }
+
+ std::vector<std::string> l_row;
+ for (const auto& l_column : l_tableColumns)
+ {
+ const auto& l_fruJson = l_fruEntry.value();
+
+ if (l_column.first == "FRU")
+ {
+ l_row.push_back(l_fruEntry.key());
+ }
+ else
+ {
+ if (l_fruJson.contains(l_column.first))
+ {
+ l_row.push_back(l_fruJson[l_column.first]);
+ }
+ else
+ {
+ l_row.push_back("");
+ }
+ }
+ }
+
+ l_tableData.push_back(l_row);
+ }
+
+ l_rc = l_inventoryTable.Print(l_tableData);
+ }
+ else
+ {
+ // print JSON to console
+ utils::printJson(l_resultInJson);
+ l_rc = constants::SUCCESS;
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "Dump inventory failed. Error: " << l_ex.what()
+ << std::endl;
+ }
+ return l_rc;
+}
+
+void VpdTool::printSystemVpd(
+ const nlohmann::json& i_parsedJsonObj) const noexcept
+{
+ if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "Invalid JSON to print system VPD" << std::endl;
+ }
+
+ std::string l_outline(191, '=');
+ std::cout << "\nRestorable record-keyword pairs and their data on backup & "
+ "primary.\n\n"
+ << l_outline << std::endl;
+
+ std::cout << std::left << std::setw(6) << "S.No" << std::left
+ << std::setw(8) << "Record" << std::left << std::setw(9)
+ << "Keyword" << std::left << std::setw(75) << "Data On Backup"
+ << std::left << std::setw(75) << "Data On Primary" << std::left
+ << std::setw(14) << "Data Mismatch\n"
+ << l_outline << std::endl;
+
+ uint8_t l_slNum = 0;
+
+ for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
+ {
+ if (l_aRecordKwInfo.contains("sourceRecord") ||
+ l_aRecordKwInfo.contains("sourceKeyword") ||
+ l_aRecordKwInfo.contains("destinationkeywordValue") ||
+ l_aRecordKwInfo.contains("sourcekeywordValue"))
+ {
+ std::string l_mismatchFound{
+ (l_aRecordKwInfo["destinationkeywordValue"] !=
+ l_aRecordKwInfo["sourcekeywordValue"])
+ ? "YES"
+ : "NO"};
+
+ std::string l_splitLine(191, '-');
+
+ try
+ {
+ std::cout << std::left << std::setw(6)
+ << static_cast<int>(++l_slNum) << std::left
+ << std::setw(8)
+ << l_aRecordKwInfo.value("sourceRecord", "")
+ << std::left << std::setw(9)
+ << l_aRecordKwInfo.value("sourceKeyword", "")
+ << std::left << std::setw(75) << std::setfill(' ')
+ << utils::getPrintableValue(
+ l_aRecordKwInfo["destinationkeywordValue"])
+ << std::left << std::setw(75) << std::setfill(' ')
+ << utils::getPrintableValue(
+ l_aRecordKwInfo["sourcekeywordValue"])
+ << std::left << std::setw(14) << l_mismatchFound
+ << '\n'
+ << l_splitLine << std::endl;
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << l_ex.what() << std::endl;
+ }
+ }
+ }
+}
+
+int VpdTool::updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
+ bool i_useBackupData) const noexcept
+{
+ int l_rc = constants::FAILURE;
+
+ if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("source") ||
+ !i_parsedJsonObj.contains("backupMap"))
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "Invalid JSON" << std::endl;
+ return l_rc;
+ }
+
+ std::string l_srcVpdPath;
+ if (auto l_vpdPath = i_parsedJsonObj["source"].value("hardwarePath", "");
+ !l_vpdPath.empty())
+ {
+ l_srcVpdPath = l_vpdPath;
+ }
+ else if (auto l_vpdPath =
+ i_parsedJsonObj["source"].value("inventoryPath", "");
+ !l_vpdPath.empty())
+ {
+ l_srcVpdPath = l_vpdPath;
+ }
+ else
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "source path information is missing in JSON" << std::endl;
+ return l_rc;
+ }
+
+ bool l_anyMismatchFound = false;
+ for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
+ {
+ if (!l_aRecordKwInfo.contains("sourceRecord") ||
+ !l_aRecordKwInfo.contains("sourceKeyword") ||
+ !l_aRecordKwInfo.contains("destinationkeywordValue") ||
+ !l_aRecordKwInfo.contains("sourcekeywordValue"))
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "Missing required information in the JSON"
+ << std::endl;
+ continue;
+ }
+
+ if (l_aRecordKwInfo["sourcekeywordValue"] !=
+ l_aRecordKwInfo["destinationkeywordValue"])
+ {
+ l_anyMismatchFound = true;
+
+ auto l_keywordValue =
+ i_useBackupData ? l_aRecordKwInfo["destinationkeywordValue"]
+ : l_aRecordKwInfo["sourcekeywordValue"];
+
+ auto l_paramsToWrite = std::make_tuple(
+ l_aRecordKwInfo["sourceRecord"],
+ l_aRecordKwInfo["sourceKeyword"], l_keywordValue);
+
+ try
+ {
+ l_rc = utils::writeKeyword(l_srcVpdPath, l_paramsToWrite);
+ if (l_rc > 0)
+ {
+ l_rc = constants::SUCCESS;
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << "write keyword failed for record: "
+ << l_aRecordKwInfo["sourceRecord"]
+ << ", keyword: " << l_aRecordKwInfo["sourceKeyword"]
+ << ", error: " << l_ex.what() << std::ends;
+ }
+ }
+ }
+
+ std::string l_dataUsed =
+ (i_useBackupData ? "data from backup" : "data from primary VPD");
+ if (l_anyMismatchFound)
+ {
+ std::cout << "Data updated successfully for all mismatching "
+ "record-keyword pairs by choosing their corresponding "
+ << l_dataUsed << ". Exit successfully." << std::endl;
+ }
+ else
+ {
+ std::cout << "No mismatch found for any of the above mentioned "
+ "record-keyword pair. Exit successfully."
+ << std::endl;
+ }
+
+ return l_rc;
+}
+
+int VpdTool::handleMoreOption(
+ const nlohmann::json& i_parsedJsonObj) const noexcept
+{
+ int l_rc = constants::FAILURE;
+
+ try
+ {
+ if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap"))
+ {
+ throw std::runtime_error("Invalid JSON");
+ }
+
+ std::string l_srcVpdPath;
+
+ if (auto l_vpdPath =
+ i_parsedJsonObj["source"].value("hardwarePath", "");
+ !l_vpdPath.empty())
+ {
+ l_srcVpdPath = l_vpdPath;
+ }
+ else if (auto l_vpdPath =
+ i_parsedJsonObj["source"].value("inventoryPath", "");
+ !l_vpdPath.empty())
+ {
+ l_srcVpdPath = l_vpdPath;
+ }
+ else
+ {
+ throw std::runtime_error(
+ "source path information is missing in JSON");
+ }
+
+ auto updateKeywordValue =
+ [](std::string io_vpdPath, const std::string& i_recordName,
+ const std::string& i_keywordName,
+ const types::BinaryVector& i_keywordValue) -> int {
+ int l_rc = constants::FAILURE;
+
+ try
+ {
+ auto l_paramsToWrite = std::make_tuple(
+ i_recordName, i_keywordName, i_keywordValue);
+ l_rc = utils::writeKeyword(io_vpdPath, l_paramsToWrite);
+
+ if (l_rc > 0)
+ {
+ std::cout << std::endl
+ << "Data updated successfully." << std::endl;
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable log when verbose is enabled.
+ std::cerr << l_ex.what() << std::endl;
+ }
+ return l_rc;
+ };
+
+ do
+ {
+ int l_slNum = 0;
+ bool l_exit = false;
+
+ for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"])
+ {
+ if (!l_aRecordKwInfo.contains("sourceRecord") ||
+ !l_aRecordKwInfo.contains("sourceKeyword") ||
+ !l_aRecordKwInfo.contains("destinationkeywordValue") ||
+ !l_aRecordKwInfo.contains("sourcekeywordValue"))
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr
+ << "Source or destination information is missing in the JSON."
+ << std::endl;
+ continue;
+ }
+
+ const std::string l_mismatchFound{
+ (l_aRecordKwInfo["sourcekeywordValue"] !=
+ l_aRecordKwInfo["destinationkeywordValue"])
+ ? "YES"
+ : "NO"};
+
+ std::cout << std::endl
+ << std::left << std::setw(6) << "S.No" << std::left
+ << std::setw(8) << "Record" << std::left
+ << std::setw(9) << "Keyword" << std::left
+ << std::setw(75) << std::setfill(' ') << "Backup Data"
+ << std::left << std::setw(75) << std::setfill(' ')
+ << "Primary Data" << std::left << std::setw(14)
+ << "Data Mismatch" << std::endl;
+
+ std::cout << std::left << std::setw(6)
+ << static_cast<int>(++l_slNum) << std::left
+ << std::setw(8)
+ << l_aRecordKwInfo.value("sourceRecord", "")
+ << std::left << std::setw(9)
+ << l_aRecordKwInfo.value("sourceKeyword", "")
+ << std::left << std::setw(75) << std::setfill(' ')
+ << utils::getPrintableValue(
+ l_aRecordKwInfo["destinationkeywordValue"])
+ << std::left << std::setw(75) << std::setfill(' ')
+ << utils::getPrintableValue(
+ l_aRecordKwInfo["sourcekeywordValue"])
+ << std::left << std::setw(14) << l_mismatchFound
+ << std::endl;
+
+ std::cout << std::string(191, '=') << std::endl;
+
+ if (constants::STR_CMP_SUCCESS ==
+ l_mismatchFound.compare("YES"))
+ {
+ printFixSystemVpdOption(
+ types::UserOption::UseBackupDataForCurrent);
+ printFixSystemVpdOption(
+ types::UserOption::UseSystemBackplaneDataForCurrent);
+ printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
+ printFixSystemVpdOption(types::UserOption::SkipCurrent);
+ printFixSystemVpdOption(types::UserOption::Exit);
+ }
+ else
+ {
+ std::cout << "No mismatch found." << std::endl << std::endl;
+ printFixSystemVpdOption(types::UserOption::NewValueOnBoth);
+ printFixSystemVpdOption(types::UserOption::SkipCurrent);
+ printFixSystemVpdOption(types::UserOption::Exit);
+ }
+
+ int l_userSelectedOption = types::UserOption::Exit;
+ std::cin >> l_userSelectedOption;
+
+ if (types::UserOption::UseBackupDataForCurrent ==
+ l_userSelectedOption)
+ {
+ l_rc = updateKeywordValue(
+ l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
+ l_aRecordKwInfo["sourceKeyword"],
+ l_aRecordKwInfo["destinationkeywordValue"]);
+ }
+ else if (types::UserOption::UseSystemBackplaneDataForCurrent ==
+ l_userSelectedOption)
+ {
+ l_rc = updateKeywordValue(
+ l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
+ l_aRecordKwInfo["sourceKeyword"],
+ l_aRecordKwInfo["sourcekeywordValue"]);
+ }
+ else if (types::UserOption::NewValueOnBoth ==
+ l_userSelectedOption)
+ {
+ std::string l_newValue;
+ std::cout
+ << std::endl
+ << "Enter the new value to update on both "
+ "primary & backup. Value should be in ASCII or "
+ "in HEX(prefixed with 0x) : ";
+ std::cin >> l_newValue;
+ std::cout << std::endl
+ << std::string(191, '=') << std::endl;
+
+ try
+ {
+ l_rc = updateKeywordValue(
+ l_srcVpdPath, l_aRecordKwInfo["sourceRecord"],
+ l_aRecordKwInfo["sourceKeyword"],
+ utils::convertToBinary(l_newValue));
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << l_ex.what() << std::endl;
+ }
+ }
+ else if (types::UserOption::SkipCurrent == l_userSelectedOption)
+ {
+ std::cout << std::endl
+ << "Skipped the above record-keyword pair. "
+ "Continue to the next available pair."
+ << std::endl;
+ }
+ else if (types::UserOption::Exit == l_userSelectedOption)
+ {
+ std::cout << "Exit successfully" << std::endl;
+ l_exit = true;
+ break;
+ }
+ else
+ {
+ std::cout << "Provide a valid option. Retrying for the "
+ "current record-keyword pair"
+ << std::endl;
+ }
+ }
+ if (l_exit)
+ {
+ l_rc = constants::SUCCESS;
+ break;
+ }
+ } while (true);
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Enable logging when verbose is enabled.
+ std::cerr << 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
new file mode 100644
index 0000000..3fdf5d1
--- /dev/null
+++ b/vpd-tool/src/vpd_tool_main.cpp
@@ -0,0 +1,335 @@
+#include "tool_constants.hpp"
+#include "vpd_tool.hpp"
+
+#include <CLI/CLI.hpp>
+
+#include <filesystem>
+#include <iostream>
+
+/**
+ * @brief API to perform manufacturing clean.
+ *
+ * @param[in] i_mfgCleanConfirmFlag - Confirmation flag to perform manufacturing
+ * clean.
+ *
+ * @return Status returned by cleanSystemVpd operation, success otherwise.
+ */
+int doMfgClean(const auto& i_mfgCleanConfirmFlag)
+{
+ if (i_mfgCleanConfirmFlag->empty())
+ {
+ constexpr auto MAX_CONFIRMATION_STR_LENGTH{3};
+ std::string l_confirmation{};
+ std::cout
+ << "This option resets some of the system VPD keywords to their default values. Do you really wish to proceed further?[yes/no]:";
+ std::cin >> std::setw(MAX_CONFIRMATION_STR_LENGTH) >> l_confirmation;
+
+ if (l_confirmation != "yes")
+ {
+ return vpd::constants::SUCCESS;
+ }
+ }
+
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.cleanSystemVpd();
+}
+
+/**
+ * @brief API to write keyword's value.
+ *
+ * @param[in] i_hardwareFlag - Flag to perform write on hardware.
+ * @param[in] i_keywordValueOption - Option to read keyword value from command.
+ * @param[in] i_vpdPath - DBus object path or EEPROM path.
+ * @param[in] i_recordName - Record to be updated.
+ * @param[in] i_keywordName - Keyword to be updated.
+ * @param[in] i_keywordValue - Value to be updated in keyword.
+ *
+ * @return Status of writeKeyword operation, failure otherwise.
+ */
+int writeKeyword(const auto& i_hardwareFlag, const auto& i_keywordValueOption,
+ const std::string& i_vpdPath, const std::string& i_recordName,
+ const std::string& i_keywordName,
+ const std::string& i_keywordValue)
+{
+ std::error_code l_ec;
+
+ if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
+ {
+ std::cerr << "Given EEPROM file path doesn't exist : " + i_vpdPath
+ << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ if (l_ec)
+ {
+ std::cerr << "filesystem call exists failed for file: " << i_vpdPath
+ << ", reason: " + l_ec.message() << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ if (!i_keywordValueOption->empty() && i_keywordValue.empty())
+ {
+ std::cerr
+ << "Please provide keyword value.\nUse --value/--file to give "
+ "keyword value. Refer --help."
+ << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ if (i_keywordValueOption->empty())
+ {
+ std::cerr
+ << "Please provide keyword value.\nUse --value/--file to give "
+ "keyword value. Refer --help."
+ << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.writeKeyword(i_vpdPath, i_recordName, i_keywordName,
+ i_keywordValue, !i_hardwareFlag->empty());
+}
+
+/**
+ * @brief API to read keyword's value.
+ *
+ * @param[in] i_hardwareFlag - Flag to perform write on hardware.
+ * @param[in] i_vpdPath - DBus object path or EEPROM path.
+ * @param[in] i_recordName - Record to be updated.
+ * @param[in] i_keywordName - Keyword to be updated.
+ * @param[in] i_filePath - File path to save keyword's read value.
+ *
+ * @return Status of readKeyword operation, failure otherwise.
+ */
+int readKeyword(const auto& i_hardwareFlag, const std::string& i_vpdPath,
+ const std::string& i_recordName,
+ const std::string& i_keywordName, const std::string& i_filePath)
+{
+ std::error_code l_ec;
+
+ if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
+ {
+ std::string l_errMessage{
+ "Given EEPROM file path doesn't exist : " + i_vpdPath};
+
+ if (l_ec)
+ {
+ l_errMessage += ". filesystem call exists failed, reason: " +
+ l_ec.message();
+ }
+
+ std::cerr << l_errMessage << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ bool l_isHardwareOperation = (!i_hardwareFlag->empty() ? true : false);
+
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.readKeyword(i_vpdPath, i_recordName, i_keywordName,
+ l_isHardwareOperation, i_filePath);
+}
+
+/**
+ * @brief API to check option value pair in the tool command.
+ *
+ * In VPD tool command, some of the option(s) mandate values to be passed along
+ * with the option. This API based on option, detects those mandatory value(s).
+ *
+ * @param[in] i_objectOption - Option to pass object path.
+ * @param[in] i_vpdPath - Object path, DBus or EEPROM.
+ * @param[in] i_recordOption - Option to pass record name.
+ * @param[in] i_recordName - Record name.
+ * @param[in] i_keywordOption - Option to pass keyword name.
+ * @param[in] i_keywordName - Keyword name.
+ *
+ * @return Success if corresponding value is found against option, failure
+ * otherwise.
+ */
+int checkOptionValuePair(const auto& i_objectOption, const auto& i_vpdPath,
+ const auto& i_recordOption, const auto& i_recordName,
+ const auto& i_keywordOption, const auto& i_keywordName)
+{
+ if (!i_objectOption->empty() && i_vpdPath.empty())
+ {
+ std::cout << "Given path is empty." << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ if (!i_recordOption->empty() &&
+ (i_recordName.size() != vpd::constants::RECORD_SIZE))
+ {
+ std::cerr << "Record " << i_recordName << " is not supported."
+ << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ if (!i_keywordOption->empty() &&
+ (i_keywordName.size() != vpd::constants::KEYWORD_SIZE))
+ {
+ std::cerr << "Keyword " << i_keywordName << " is not supported."
+ << std::endl;
+ return vpd::constants::FAILURE;
+ }
+
+ return vpd::constants::SUCCESS;
+}
+
+/**
+ * @brief API to create app footer.
+ *
+ * @param[in] i_app - CLI::App object.
+ */
+void updateFooter(CLI::App& i_app)
+{
+ i_app.footer(
+ "Read:\n"
+ " IPZ Format:\n"
+ " From DBus to console: "
+ "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name>\n"
+ " From DBus to file: "
+ "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
+ " From hardware to console: "
+ "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name>\n"
+ " From hardware to file: "
+ "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
+ "Write:\n"
+ " IPZ Format:\n"
+ " On DBus: "
+ "vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
+ " On DBus, take keyword value from file:\n"
+ " vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
+ " On hardware: "
+ "vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
+ " On hardware, take keyword value from file:\n"
+ " vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
+ "Dump Object:\n"
+ " From DBus to console: "
+ "vpd-tool -o -O <DBus Object Path>\n"
+ "Fix System VPD:\n"
+ " vpd-tool --fixSystemVPD\n"
+ "MfgClean:\n"
+ " Flag to clean and reset specific keywords on system VPD to its default value.\n"
+ " vpd-tool --mfgClean\n"
+ "Dump Inventory:\n"
+ " From DBus to console in JSON format: "
+ "vpd-tool -i\n"
+ " From DBus to console in Table format: "
+ "vpd-tool -i -t\n");
+}
+
+int main(int argc, char** argv)
+{
+ CLI::App l_app{"VPD Command Line Tool"};
+
+ std::string l_vpdPath{};
+ std::string l_recordName{};
+ std::string l_keywordName{};
+ std::string l_filePath{};
+ std::string l_keywordValue{};
+
+ updateFooter(l_app);
+
+ auto l_objectOption =
+ l_app.add_option("--object, -O", l_vpdPath, "File path");
+ auto l_recordOption =
+ l_app.add_option("--record, -R", l_recordName, "Record name");
+ auto l_keywordOption =
+ l_app.add_option("--keyword, -K", l_keywordName, "Keyword name");
+
+ // Enable when file option is implemented.
+ /*auto l_fileOption = l_app.add_option("--file", l_filePath,
+ "Absolute file path");*/
+
+ auto l_keywordValueOption =
+ l_app.add_option("--value, -V", l_keywordValue,
+ "Keyword value in ascii/hex format."
+ " ascii ex: 01234; hex ex: 0x30313233");
+
+ auto l_hardwareFlag =
+ l_app.add_flag("--Hardware, -H", "CAUTION: Developer only option.");
+
+ auto l_readFlag = l_app.add_flag("--readKeyword, -r", "Read keyword")
+ ->needs(l_objectOption)
+ ->needs(l_recordOption)
+ ->needs(l_keywordOption);
+
+ auto l_writeFlag =
+ l_app
+ .add_flag(
+ "--writeKeyword, -w,--updateKeyword, -u",
+ "Write keyword, Note: Irrespective of DBus or hardware path provided, primary and backup, redundant EEPROM(if any) paths will get updated with given key value")
+ ->needs(l_objectOption)
+ ->needs(l_recordOption)
+ ->needs(l_keywordOption);
+
+ // ToDo: Take offset value from user for hardware path.
+
+ auto l_dumpObjFlag =
+ l_app
+ .add_flag("--dumpObject, -o",
+ "Dump specific properties of an inventory object")
+ ->needs(l_objectOption);
+
+ auto l_fixSystemVpdFlag = l_app.add_flag(
+ "--fixSystemVPD",
+ "Use this option to interactively fix critical system VPD keywords");
+ auto l_dumpInventoryFlag =
+ l_app.add_flag("--dumpInventory, -i", "Dump all the inventory objects");
+
+ auto l_mfgCleanFlag = l_app.add_flag(
+ "--mfgClean", "Manufacturing clean on system VPD keyword");
+
+ auto l_mfgCleanConfirmFlag = l_app.add_flag(
+ "--yes", "Using this flag with --mfgClean option, assumes "
+ "yes to proceed without confirmation.");
+
+ auto l_dumpInventoryTableFlag =
+ l_app.add_flag("--table, -t", "Dump inventory in table format");
+
+ CLI11_PARSE(l_app, argc, argv);
+
+ if (checkOptionValuePair(l_objectOption, l_vpdPath, l_recordOption,
+ l_recordName, l_keywordOption, l_keywordName) ==
+ vpd::constants::FAILURE)
+ {
+ return vpd::constants::FAILURE;
+ }
+
+ if (!l_readFlag->empty())
+ {
+ return readKeyword(l_hardwareFlag, l_vpdPath, l_recordName,
+ l_keywordName, l_filePath);
+ }
+
+ if (!l_writeFlag->empty())
+ {
+ return writeKeyword(l_hardwareFlag, l_keywordValueOption, l_vpdPath,
+ l_recordName, l_keywordName, l_keywordValue);
+ }
+
+ if (!l_dumpObjFlag->empty())
+ {
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.dumpObject(l_vpdPath);
+ }
+
+ if (!l_fixSystemVpdFlag->empty())
+ {
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.fixSystemVpd();
+ }
+
+ if (!l_mfgCleanFlag->empty())
+ {
+ return doMfgClean(l_mfgCleanConfirmFlag);
+ }
+
+ if (!l_dumpInventoryFlag->empty())
+ {
+ vpd::VpdTool l_vpdToolObj;
+ return l_vpdToolObj.dumpInventory(!l_dumpInventoryTableFlag->empty());
+ }
+
+ std::cout << l_app.help() << std::endl;
+ return vpd::constants::FAILURE;
+}