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;
+}