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/README.md b/vpd-tool/README.md
new file mode 100644
index 0000000..4d02336
--- /dev/null
+++ b/vpd-tool/README.md
@@ -0,0 +1,6 @@
+# VPD Tool Overview
+
+VPD Tool is designed for managing BMC system FRU's Vital Product Data(VPD). It
+provides command line interface to read and write FRU's VPD. More information
+can be found
+[here](https://github.ibm.com/openbmc/openbmc/wiki/VPD-TOOL-HELPER).
diff --git a/vpd-tool/include/tool_constants.hpp b/vpd-tool/include/tool_constants.hpp
new file mode 100644
index 0000000..6e89957
--- /dev/null
+++ b/vpd-tool/include/tool_constants.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <cstdint>
+
+namespace vpd
+{
+namespace constants
+{
+static constexpr auto KEYWORD_SIZE = 2;
+static constexpr auto RECORD_SIZE = 4;
+static constexpr auto INDENTATION = 4;
+static constexpr auto SUCCESS = 0;
+static constexpr auto FAILURE = -1;
+
+// To be explicitly used for string comparison.
+static constexpr auto STR_CMP_SUCCESS = 0;
+
+constexpr auto inventoryManagerService =
+    "xyz.openbmc_project.Inventory.Manager";
+constexpr auto baseInventoryPath = "/xyz/openbmc_project/inventory";
+constexpr auto ipzVpdInfPrefix = "com.ibm.ipzvpd.";
+
+constexpr auto vpdManagerService = "com.ibm.VPD.Manager";
+constexpr auto vpdManagerObjectPath = "/com/ibm/VPD/Manager";
+constexpr auto vpdManagerInfName = "com.ibm.VPD.Manager";
+constexpr auto inventoryItemInf = "xyz.openbmc_project.Inventory.Item";
+constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
+constexpr auto locationCodeInf = "com.ibm.ipzvpd.Location";
+constexpr auto assetInf = "xyz.openbmc_project.Inventory.Decorator.Asset";
+constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
+constexpr auto objectMapperObjectPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto objectMapperInfName = "xyz.openbmc_project.ObjectMapper";
+} // namespace constants
+} // namespace vpd
diff --git a/vpd-tool/include/tool_types.hpp b/vpd-tool/include/tool_types.hpp
new file mode 100644
index 0000000..1e9ff7d
--- /dev/null
+++ b/vpd-tool/include/tool_types.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <sdbusplus/message/types.hpp>
+
+#include <cstdint>
+#include <tuple>
+#include <variant>
+#include <vector>
+
+namespace vpd
+{
+namespace types
+{
+using BinaryVector = std::vector<uint8_t>;
+
+// This covers mostly all the data type supported over DBus for a property.
+// clang-format off
+using DbusVariantType = std::variant<
+    std::vector<std::tuple<std::string, std::string, std::string>>,
+    std::vector<std::string>,
+    std::vector<double>,
+    std::string,
+    int64_t,
+    uint64_t,
+    double,
+    int32_t,
+    uint32_t,
+    int16_t,
+    uint16_t,
+    uint8_t,
+    bool,
+    BinaryVector,
+    std::vector<uint32_t>,
+    std::vector<uint16_t>,
+    sdbusplus::message::object_path,
+    std::tuple<uint64_t, std::vector<std::tuple<std::string, std::string, double, uint64_t>>>,
+    std::vector<std::tuple<std::string, std::string>>,
+    std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>,
+    std::vector<std::tuple<uint32_t, size_t>>,
+    std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+                           std::string, std::string>>
+ >;
+
+//IpzType contains tuple of <Record, Keyword>
+using IpzType = std::tuple<std::string, std::string>;
+
+//ReadVpdParams either of IPZ or keyword format
+using ReadVpdParams = std::variant<IpzType, std::string>;
+
+//KwData contains tuple of <keywordName, KeywordValue>
+using KwData = std::tuple<std::string, BinaryVector>;
+
+//IpzData contains tuple of <RecordName, KeywordName, KeywordValue>
+using IpzData = std::tuple<std::string, std::string, BinaryVector>;
+
+//WriteVpdParams either of IPZ or keyword format
+using WriteVpdParams = std::variant<IpzData, KwData>;
+// Return type of ObjectMapper GetObject API
+using MapperGetObject = std::map<std::string,std::vector<std::string>>;
+
+// Table row data
+using TableRowData = std::vector<std::string>;
+
+// Type used to populate table data
+using TableInputData = std::vector<TableRowData>;
+
+// A table column name-size pair
+using TableColumnNameSizePair = std::pair<std::string, std::size_t>;
+
+enum UserOption
+{
+    Exit,
+    UseBackupDataForAll,
+    UseSystemBackplaneDataForAll,
+    MoreOptions,
+    UseBackupDataForCurrent,
+    UseSystemBackplaneDataForCurrent,
+    NewValueOnBoth,
+    SkipCurrent
+};
+
+} // namespace types
+} // namespace vpd
diff --git a/vpd-tool/include/tool_utils.hpp b/vpd-tool/include/tool_utils.hpp
new file mode 100644
index 0000000..4d650c1
--- /dev/null
+++ b/vpd-tool/include/tool_utils.hpp
@@ -0,0 +1,726 @@
+#pragma once
+
+#include "tool_constants.hpp"
+#include "tool_types.hpp"
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
+
+#include <fstream>
+#include <iostream>
+
+namespace vpd
+{
+namespace utils
+{
+/**
+ * @brief An API to read property from Dbus.
+ *
+ * API reads the property value for the specified interface and object path from
+ * the given Dbus service.
+ *
+ * The caller of the API needs to validate the validity and correctness of the
+ * type and value of data returned. The API will just fetch and return the data
+ * without any data validation.
+ *
+ * Note: It will be caller's responsibility to check for empty value returned
+ * and generate appropriate error if required.
+ *
+ * @param[in] i_serviceName - Name of the Dbus service.
+ * @param[in] i_objectPath - Object path under the service.
+ * @param[in] i_interface - Interface under which property exist.
+ * @param[in] i_property - Property whose value is to be read.
+ *
+ * @return - Value read from Dbus.
+ *
+ * @throw std::runtime_error
+ */
+inline types::DbusVariantType readDbusProperty(
+    const std::string& i_serviceName, const std::string& i_objectPath,
+    const std::string& i_interface, const std::string& i_property)
+{
+    types::DbusVariantType l_propertyValue;
+
+    // Mandatory fields to make a dbus call.
+    if (i_serviceName.empty() || i_objectPath.empty() || i_interface.empty() ||
+        i_property.empty())
+    {
+        // TODO: Enable logging when verbose is enabled.
+        /*std::cout << "One of the parameter to make Dbus read call is empty."
+                  << std::endl;*/
+        throw std::runtime_error("Empty Parameter");
+    }
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method =
+            l_bus.new_method_call(i_serviceName.c_str(), i_objectPath.c_str(),
+                                  "org.freedesktop.DBus.Properties", "Get");
+        l_method.append(i_interface, i_property);
+
+        auto result = l_bus.call(l_method);
+        result.read(l_propertyValue);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: Enable logging when verbose is enabled.
+        // std::cout << std::string(l_ex.what()) << std::endl;
+        throw std::runtime_error(std::string(l_ex.what()));
+    }
+    return l_propertyValue;
+}
+
+/**
+ * @brief An API to print json data on stdout.
+ *
+ * @param[in] i_jsonData - JSON object.
+ */
+inline void printJson(const nlohmann::json& i_jsonData)
+{
+    try
+    {
+        std::cout << i_jsonData.dump(constants::INDENTATION) << std::endl;
+    }
+    catch (const nlohmann::json::type_error& l_ex)
+    {
+        throw std::runtime_error(
+            "Failed to dump JSON data, error: " + std::string(l_ex.what()));
+    }
+}
+
+/**
+ * @brief An API to convert binary value into ascii/hex representation.
+ *
+ * If given data contains printable characters, ASCII formated string value of
+ * the input data will be returned. Otherwise if the data has any non-printable
+ * value, returns the hex represented value of the given data in string format.
+ *
+ * @param[in] i_keywordValue - Data in binary format.
+ *
+ * @throw - Throws std::bad_alloc or std::terminate in case of error.
+ *
+ * @return - Returns the converted string value.
+ */
+inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue)
+{
+    bool l_allPrintable =
+        std::all_of(i_keywordValue.begin(), i_keywordValue.end(),
+                    [](const auto& l_byte) { return std::isprint(l_byte); });
+
+    std::ostringstream l_oss;
+    if (l_allPrintable)
+    {
+        l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end());
+    }
+    else
+    {
+        l_oss << "0x";
+        for (const auto& l_byte : i_keywordValue)
+        {
+            l_oss << std::setfill('0') << std::setw(2) << std::hex
+                  << static_cast<int>(l_byte);
+        }
+    }
+
+    return l_oss.str();
+}
+
+/**
+ * @brief API to read keyword's value from hardware.
+ *
+ * This API reads keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'ReadKeyword' method to read keyword's value.
+ *
+ * @param[in] i_eepromPath - EEPROM file path.
+ * @param[in] i_paramsToReadData - Property whose value has to be read.
+ *
+ * @return - Value read from hardware
+ *
+ * @throw std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline types::DbusVariantType
+    readKeywordFromHardware(const std::string& i_eepromPath,
+                            const types::ReadVpdParams i_paramsToReadData)
+{
+    if (i_eepromPath.empty())
+    {
+        throw std::runtime_error("Empty EEPROM path");
+    }
+
+    try
+    {
+        types::DbusVariantType l_propertyValue;
+
+        auto l_bus = sdbusplus::bus::new_default();
+
+        auto l_method = l_bus.new_method_call(
+            constants::vpdManagerService, constants::vpdManagerObjectPath,
+            constants::vpdManagerInfName, "ReadKeyword");
+
+        l_method.append(i_eepromPath, i_paramsToReadData);
+        auto l_result = l_bus.call(l_method);
+
+        l_result.read(l_propertyValue);
+
+        return l_propertyValue;
+    }
+    catch (const sdbusplus::exception::SdBusError& l_error)
+    {
+        throw;
+    }
+}
+
+/**
+ * @brief API to save keyword's value on file.
+ *
+ * API writes keyword's value on the given file path. If the data is in hex
+ * format, API strips '0x' and saves the value on the given file.
+ *
+ * @param[in] i_filePath - File path.
+ * @param[in] i_keywordValue - Keyword's value.
+ *
+ * @return - true on successfully writing to file, false otherwise.
+ */
+inline bool saveToFile(const std::string& i_filePath,
+                       const std::string& i_keywordValue)
+{
+    bool l_returnStatus = false;
+
+    if (i_keywordValue.empty())
+    {
+        // ToDo: log only when verbose is enabled
+        std::cerr << "Save to file[ " << i_filePath
+                  << "] failed, reason: Empty keyword's value received"
+                  << std::endl;
+        return l_returnStatus;
+    }
+
+    std::string l_keywordValue{i_keywordValue};
+    if (i_keywordValue.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
+    {
+        l_keywordValue = i_keywordValue.substr(2);
+    }
+
+    std::ofstream l_outPutFileStream;
+    l_outPutFileStream.exceptions(
+        std::ifstream::badbit | std::ifstream::failbit);
+    try
+    {
+        l_outPutFileStream.open(i_filePath);
+
+        if (l_outPutFileStream.is_open())
+        {
+            l_outPutFileStream.write(l_keywordValue.c_str(),
+                                     l_keywordValue.size());
+            l_returnStatus = true;
+        }
+        else
+        {
+            // ToDo: log only when verbose is enabled
+            std::cerr << "Error opening output file " << i_filePath
+                      << std::endl;
+        }
+    }
+    catch (const std::ios_base::failure& l_ex)
+    {
+        // ToDo: log only when verbose is enabled
+        std::cerr
+            << "Failed to write to file: " << i_filePath
+            << ", either base folder path doesn't exist or internal error occured, error: "
+            << l_ex.what() << '\n';
+    }
+
+    return l_returnStatus;
+}
+
+/**
+ * @brief API to print data in JSON format on console
+ *
+ * @param[in] i_fruPath - FRU path.
+ * @param[in] i_keywordName - Keyword name.
+ * @param[in] i_keywordStrValue - Keyword's value.
+ */
+inline void displayOnConsole(const std::string& i_fruPath,
+                             const std::string& i_keywordName,
+                             const std::string& i_keywordStrValue)
+{
+    nlohmann::json l_resultInJson = nlohmann::json::object({});
+    nlohmann::json l_keywordValInJson = nlohmann::json::object({});
+
+    l_keywordValInJson.emplace(i_keywordName, i_keywordStrValue);
+    l_resultInJson.emplace(i_fruPath, l_keywordValInJson);
+
+    printJson(l_resultInJson);
+}
+
+/**
+ * @brief API to write keyword's value.
+ *
+ * This API writes keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'UpdateKeyword' method to update keyword's value.
+ *
+ * @param[in] i_vpdPath - EEPROM or object path, where keyword is present.
+ * @param[in] i_paramsToWriteData - Data required to update keyword's value.
+ *
+ * @return - Number of bytes written on success, -1 on failure.
+ *
+ * @throw - std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline int writeKeyword(const std::string& i_vpdPath,
+                        const types::WriteVpdParams& i_paramsToWriteData)
+{
+    if (i_vpdPath.empty())
+    {
+        throw std::runtime_error("Empty path");
+    }
+
+    int l_rc = constants::FAILURE;
+    auto l_bus = sdbusplus::bus::new_default();
+
+    auto l_method = l_bus.new_method_call(
+        constants::vpdManagerService, constants::vpdManagerObjectPath,
+        constants::vpdManagerInfName, "UpdateKeyword");
+
+    l_method.append(i_vpdPath, i_paramsToWriteData);
+    auto l_result = l_bus.call(l_method);
+
+    l_result.read(l_rc);
+    return l_rc;
+}
+
+/**
+ * @brief API to write keyword's value on hardware.
+ *
+ * This API writes keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'WriteKeywordOnHardware' method to update keyword's value.
+ *
+ * Note: This API updates keyword's value only on the given hardware path, any
+ * backup or redundant EEPROM (if exists) paths won't get updated.
+ *
+ * @param[in] i_eepromPath - EEPROM where keyword is present.
+ * @param[in] i_paramsToWriteData - Data required to update keyword's value.
+ *
+ * @return - Number of bytes written on success, -1 on failure.
+ *
+ * @throw - std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline int
+    writeKeywordOnHardware(const std::string& i_eepromPath,
+                           const types::WriteVpdParams& i_paramsToWriteData)
+{
+    if (i_eepromPath.empty())
+    {
+        throw std::runtime_error("Empty path");
+    }
+
+    int l_rc = constants::FAILURE;
+    auto l_bus = sdbusplus::bus::new_default();
+
+    auto l_method = l_bus.new_method_call(
+        constants::vpdManagerService, constants::vpdManagerObjectPath,
+        constants::vpdManagerInfName, "WriteKeywordOnHardware");
+
+    l_method.append(i_eepromPath, i_paramsToWriteData);
+    auto l_result = l_bus.call(l_method);
+
+    l_result.read(l_rc);
+
+    if (l_rc > 0)
+    {
+        std::cout << "Data updated successfully " << std::endl;
+    }
+    return l_rc;
+}
+
+/**
+ * @brief API to get data in binary format.
+ *
+ * This API converts given string value into array of binary data.
+ *
+ * @param[in] i_value - Input data.
+ *
+ * @return - Array of binary data on success, throws as exception in case
+ * of any error.
+ *
+ * @throw std::runtime_error, std::out_of_range, std::bad_alloc,
+ * std::invalid_argument
+ */
+inline types::BinaryVector convertToBinary(const std::string& i_value)
+{
+    if (i_value.empty())
+    {
+        throw std::runtime_error(
+            "Provide a valid hexadecimal input. (Ex. 0x30313233)");
+    }
+
+    std::vector<uint8_t> l_binaryValue{};
+
+    if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
+    {
+        if (i_value.length() % 2 != 0)
+        {
+            throw std::runtime_error(
+                "Write option accepts 2 digit hex numbers. (Ex. 0x1 "
+                "should be given as 0x01).");
+        }
+
+        auto l_value = i_value.substr(2);
+
+        if (l_value.empty())
+        {
+            throw std::runtime_error(
+                "Provide a valid hexadecimal input. (Ex. 0x30313233)");
+        }
+
+        if (l_value.find_first_not_of("0123456789abcdefABCDEF") !=
+            std::string::npos)
+        {
+            throw std::runtime_error("Provide a valid hexadecimal input.");
+        }
+
+        for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2)
+        {
+            uint8_t l_byte = static_cast<uint8_t>(
+                std::stoi(l_value.substr(l_pos, 2), nullptr, 16));
+            l_binaryValue.push_back(l_byte);
+        }
+    }
+    else
+    {
+        l_binaryValue.assign(i_value.begin(), i_value.end());
+    }
+    return l_binaryValue;
+}
+
+/**
+ * @brief API to parse respective JSON.
+ *
+ * @param[in] i_pathToJson - Path to JSON.
+ *
+ * @return Parsed JSON, throws exception in case of error.
+ *
+ * @throw std::runtime_error
+ */
+inline nlohmann::json getParsedJson(const std::string& i_pathToJson)
+{
+    if (i_pathToJson.empty())
+    {
+        throw std::runtime_error("Path to JSON is missing");
+    }
+
+    std::error_code l_ec;
+    if (!std::filesystem::exists(i_pathToJson, l_ec))
+    {
+        std::string l_message{
+            "file system call failed for file: " + i_pathToJson};
+
+        if (l_ec)
+        {
+            l_message += ", error: " + l_ec.message();
+        }
+        throw std::runtime_error(l_message);
+    }
+
+    if (std::filesystem::is_empty(i_pathToJson, l_ec))
+    {
+        throw std::runtime_error("Empty file: " + i_pathToJson);
+    }
+    else if (l_ec)
+    {
+        throw std::runtime_error("is_empty file system call failed for file: " +
+                                 i_pathToJson + ", error: " + l_ec.message());
+    }
+
+    std::ifstream l_jsonFile(i_pathToJson);
+    if (!l_jsonFile)
+    {
+        throw std::runtime_error("Failed to access Json path: " + i_pathToJson);
+    }
+
+    try
+    {
+        return nlohmann::json::parse(l_jsonFile);
+    }
+    catch (const nlohmann::json::parse_error& l_ex)
+    {
+        throw std::runtime_error("Failed to parse JSON file: " + i_pathToJson);
+    }
+}
+
+/**
+ * @brief API to get list of interfaces under a given object path.
+ *
+ * Given a DBus object path, this API returns a map of service -> implemented
+ * interface(s) under that object path. This API calls DBus method GetObject
+ * hosted by ObjectMapper DBus service.
+ *
+ * @param[in] i_objectPath - DBus object path.
+ * @param[in] i_constrainingInterfaces - An array of result set constraining
+ * interfaces.
+ *
+ * @return On success, returns a map of service -> implemented interface(s),
+ * else returns an empty map. The caller of this
+ * API should check for empty map.
+ */
+inline types::MapperGetObject GetServiceInterfacesForObject(
+    const std::string& i_objectPath,
+    const std::vector<std::string>& i_constrainingInterfaces) noexcept
+{
+    types::MapperGetObject l_serviceInfMap;
+    if (i_objectPath.empty())
+    {
+        // TODO: log only when verbose is enabled
+        std::cerr << "Object path is empty." << std::endl;
+        return l_serviceInfMap;
+    }
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method = l_bus.new_method_call(
+            constants::objectMapperService, constants::objectMapperObjectPath,
+            constants::objectMapperInfName, "GetObject");
+
+        l_method.append(i_objectPath, i_constrainingInterfaces);
+
+        auto l_result = l_bus.call(l_method);
+        l_result.read(l_serviceInfMap);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: log only when verbose is enabled
+        // std::cerr << std::string(l_ex.what()) << std::endl;
+    }
+    return l_serviceInfMap;
+}
+
+/** @brief API to get list of sub tree paths for a given object path
+ *
+ * Given a DBus object path, this API returns a list of object paths under that
+ * object path in the DBus tree. This API calls DBus method GetSubTreePaths
+ * hosted by ObjectMapper DBus service.
+ *
+ * @param[in] i_objectPath - DBus object path.
+ * @param[in] i_constrainingInterfaces - An array of result set constraining
+ * interfaces.
+ * @param[in] i_depth - The maximum subtree depth for which results should be
+ * fetched. For unconstrained fetches use a depth of zero.
+ *
+ * @return On success, returns a std::vector<std::string> of object paths in
+ * Phosphor Inventory Manager DBus service's tree, else returns an empty vector.
+ * The caller of this API should check for empty vector.
+ */
+inline std::vector<std::string> GetSubTreePaths(
+    const std::string i_objectPath, const int i_depth = 0,
+    const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
+{
+    std::vector<std::string> l_objectPaths;
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method = l_bus.new_method_call(
+            constants::objectMapperService, constants::objectMapperObjectPath,
+            constants::objectMapperInfName, "GetSubTreePaths");
+
+        l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
+
+        auto l_result = l_bus.call(l_method);
+        l_result.read(l_objectPaths);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: log only when verbose is enabled
+        std::cerr << std::string(l_ex.what()) << std::endl;
+    }
+    return l_objectPaths;
+}
+
+/**
+ * @brief A class to print data in tabular format
+ *
+ * This class implements methods to print data in a two dimensional tabular
+ * format. All entries in the table must be in string format.
+ *
+ */
+class Table
+{
+    class Column : public types::TableColumnNameSizePair
+    {
+      public:
+        /**
+         * @brief API to get the name of the Column
+         *
+         * @return Name of the Column.
+         */
+        const std::string& Name() const
+        {
+            return this->first;
+        }
+
+        /**
+         * @brief API to get the width of the Column
+         *
+         * @return Width of the Column.
+         */
+        std::size_t Width() const
+        {
+            return this->second;
+        }
+    };
+
+    // Current width of the table
+    std::size_t m_currentWidth;
+
+    // Character to be used as fill character between entries
+    char m_fillCharacter;
+
+    // Separator character to be used between columns
+    char m_separator;
+
+    // Array of columns
+    std::vector<Column> m_columns;
+
+    /**
+     * @brief API to Print Header
+     *
+     * Header line prints the names of the Column headers separated by the
+     * specified separator character and spaced accordingly.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintHeader() const
+    {
+        for (const auto& l_column : m_columns)
+        {
+            PrintEntry(l_column.Name(), l_column.Width());
+        }
+        std::cout << m_separator << std::endl;
+    }
+
+    /**
+     * @brief API to Print Horizontal Line
+     *
+     * A horizontal line is a sequence of '*'s.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintHorizontalLine() const
+    {
+        std::cout << std::string(m_currentWidth, '*') << std::endl;
+    }
+
+    /**
+     * @brief API to print an entry in the table
+     *
+     * An entry is a separator character followed by the text to print.
+     * The text is centre-aligned.
+     *
+     * @param[in] i_text - text to print
+     * @param[in] i_columnWidth - width of the column
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintEntry(const std::string& i_text, std::size_t i_columnWidth) const
+    {
+        const std::size_t l_textLength{i_text.length()};
+
+        constexpr std::size_t l_minFillChars{3};
+        const std::size_t l_numFillChars =
+            ((l_textLength >= i_columnWidth ? l_minFillChars
+                                            : i_columnWidth - l_textLength)) -
+            1; // -1 for the separator character
+
+        const unsigned l_oddFill = l_numFillChars % 2;
+
+        std::cout << m_separator
+                  << std::string((l_numFillChars / 2) + l_oddFill,
+                                 m_fillCharacter)
+                  << i_text << std::string(l_numFillChars / 2, m_fillCharacter);
+    }
+
+  public:
+    /**
+     * @brief Table Constructor
+     *
+     * Parameterized constructor for a Table object
+     *
+     */
+    constexpr explicit Table(const char i_fillCharacter = ' ',
+                             const char i_separator = '|') noexcept :
+        m_currentWidth{0}, m_fillCharacter{i_fillCharacter},
+        m_separator{i_separator}
+    {}
+
+    // deleted methods
+    Table(const Table&) = delete;
+    Table operator=(const Table&) = delete;
+    Table(const Table&&) = delete;
+    Table operator=(const Table&&) = delete;
+
+    ~Table() = default;
+
+    /**
+     * @brief API to add column to Table
+     *
+     * @param[in] i_name - Name of the column.
+     *
+     * @param[in] i_width - Width to allocate for the column.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int AddColumn(const std::string& i_name, std::size_t i_width)
+    {
+        if (i_width < i_name.length())
+            return constants::FAILURE;
+        m_columns.emplace_back(types::TableColumnNameSizePair(i_name, i_width));
+        m_currentWidth += i_width;
+        return constants::SUCCESS;
+    }
+
+    /**
+     * @brief API to print the Table to console.
+     *
+     * This API prints the table data to console.
+     *
+     * @param[in] i_tableData - The data to be printed.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    int Print(const types::TableInputData& i_tableData) const
+    {
+        PrintHorizontalLine();
+        PrintHeader();
+        PrintHorizontalLine();
+
+        // print the table data
+        for (const auto& l_row : i_tableData)
+        {
+            unsigned l_columnNumber{0};
+
+            // number of columns in input data is greater than the number of
+            // columns specified in Table
+            if (l_row.size() > m_columns.size())
+            {
+                return constants::FAILURE;
+            }
+
+            for (const auto& l_entry : l_row)
+            {
+                PrintEntry(l_entry, m_columns[l_columnNumber].Width());
+
+                ++l_columnNumber;
+            }
+            std::cout << m_separator << std::endl;
+        }
+        PrintHorizontalLine();
+        return constants::SUCCESS;
+    }
+};
+
+} // namespace utils
+} // namespace vpd
diff --git a/vpd-tool/include/vpd_tool.hpp b/vpd-tool/include/vpd_tool.hpp
new file mode 100644
index 0000000..80be8e5
--- /dev/null
+++ b/vpd-tool/include/vpd_tool.hpp
@@ -0,0 +1,287 @@
+#pragma once
+
+#include "tool_utils.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <optional>
+#include <string>
+
+namespace vpd
+{
+/**
+ * @brief Class to support operations on VPD.
+ *
+ * The class provides API's to,
+ * Read keyword value from DBus/hardware.
+ * Update keyword value to DBus/hardware.
+ * Dump DBus object's critical information.
+ * Fix system VPD if any mismatch between DBus and hardware data.
+ * Reset specific system VPD keywords to its default value.
+ * Force VPD collection for hardware.
+ */
+class VpdTool
+{
+  private:
+    /**
+     * @brief Get specific properties of a FRU in JSON format.
+     *
+     * For a given object path of a FRU, this API returns the following
+     * properties of the FRU in JSON format:
+     * - Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     *
+     * @param[in] i_objectPath - DBus object path
+     *
+     * @return On success, returns the properties of the FRU in JSON format,
+     * otherwise returns an empty JSON.
+     * If FRU's "Present" property is false, this API returns an empty JSON.
+     * Note: The caller of this API should handle empty JSON.
+     *
+     * @throw json::exception
+     */
+    nlohmann::json getFruProperties(const std::string& i_objectPath) const;
+
+    /**
+     * @brief Get any inventory property in JSON.
+     *
+     * API to get any property of a FRU in JSON format. Given an object path,
+     * interface and property name, this API does a D-Bus read property on PIM
+     * to get the value of that property and returns it in JSON format. This API
+     * returns empty JSON in case of failure. The caller of the API must check
+     * for empty JSON.
+     *
+     * @param[in] i_objectPath - DBus object path
+     * @param[in] i_interface - Interface name
+     * @param[in] i_propertyName - Property name
+     *
+     * @return On success, returns the property and its value in JSON format,
+     * otherwise return empty JSON.
+     * {"SN" : "ABCD"}
+     */
+    template <typename PropertyType>
+    nlohmann::json getInventoryPropertyJson(
+        const std::string& i_objectPath, const std::string& i_interface,
+        const std::string& i_propertyName) const noexcept;
+
+    /**
+     * @brief Get the "type" property for a FRU.
+     *
+     * Given a FRU path, and parsed System Config JSON, this API returns the
+     * "type" property for the FRU in JSON format. This API gets
+     * these properties from Phosphor Inventory Manager.
+     *
+     * @param[in] i_objectPath - DBus object path.
+     *
+     * @return On success, returns the "type" property in JSON
+     * format, otherwise returns empty JSON. The caller of this API should
+     * handle empty JSON.
+     */
+    nlohmann::json
+        getFruTypeProperty(const std::string& i_objectPath) const noexcept;
+
+    /**
+     * @brief Check if a FRU is present in the system.
+     *
+     * Given a FRU's object path, this API checks if the FRU is present in the
+     * system by reading the "Present" property of the FRU.
+     *
+     * @param[in] i_objectPath - DBus object path.
+     *
+     * @return true if FRU's "Present" property is true, false otherwise.
+     */
+    bool isFruPresent(const std::string& i_objectPath) const noexcept;
+
+    /**
+     * @brief An API to get backup-restore config JSON of the system.
+     *
+     * API gets this file by prasing system config JSON file and reading
+     * backupRestoreConfigPath tag.
+     *
+     * @return On success returns valid JSON object, otherwise returns empty
+     * JSON object.
+     *
+     * Note: The caller of this API should verify, is received JSON object is
+     * empty or not.
+     */
+    nlohmann::json getBackupRestoreCfgJsonObj() const noexcept;
+
+    /**
+     * @brief Prints the user options for fix system VPD command.
+     *
+     * @param[in] i_option - Option to use.
+     */
+    void printFixSystemVpdOption(
+        const types::UserOption& i_option) const noexcept;
+
+    /**
+     * @brief API to update source and destination keyword's value.
+     *
+     * API fetches source and destination keyword's value,
+     * for each keyword entries found in the input JSON object and updates the
+     * JSON object. If the path(source / destination) in JSON object is
+     * inventory object path, API sends the request to Inventory.Manager DBus
+     * service. Otherwise if its a hardware path, API sends the request to
+     * vpd-manager DBus service to get the keyword's value.
+     *
+     * @param[in,out] io_parsedJsonObj - Parsed JSON object.
+     *
+     * @return true on success, false in case of any error.
+     */
+    bool fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept;
+
+    /**
+     * @brief API to print system VPD keyword's information.
+     *
+     * The API prints source and destination keyword's information in the table
+     * format, found in the JSON object.
+     *
+     * @param[in] i_parsedJsonObj - Parsed JSON object.
+     */
+    void printSystemVpd(const nlohmann::json& i_parsedJsonObj) const noexcept;
+
+    /**
+     * @brief API to update keyword's value.
+     *
+     * API iterates the given JSON object for all record-keyword pairs, if there
+     * is any mismatch between source and destination keyword's value, API calls
+     * the utils::writeKeyword API to update keyword's value.
+     *
+     * Note: writeKeyword API, internally updates primary, backup, redundant
+     * EEPROM paths(if exists) with the given keyword's value.
+     *
+     * @param i_parsedJsonObj - Parsed JSON object.
+     * @param i_useBackupData - Specifies whether to use source or destination
+     * keyword's value to update the keyword's value.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
+                          bool i_useBackupData) const noexcept;
+
+    /**
+     * @brief API to handle more option for fix system VPD command.
+     *
+     * @param i_parsedJsonObj - Parsed JSON object.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int handleMoreOption(const nlohmann::json& i_parsedJsonObj) const noexcept;
+
+  public:
+    /**
+     * @brief Read keyword value.
+     *
+     * API to read VPD keyword's value from the given input path.
+     * If the provided i_onHardware option is true, read keyword's value from
+     * the hardware. Otherwise read keyword's value from DBus.
+     *
+     * @param[in] i_vpdPath - DBus object path or EEPROM path.
+     * @param[in] i_recordName - Record name.
+     * @param[in] i_keywordName - Keyword name.
+     * @param[in] i_onHardware - True if i_vpdPath is EEPROM path, false
+     * otherwise.
+     * @param[in] i_fileToSave - File path to save keyword's value, if not given
+     * result will redirect to a console.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int readKeyword(const std::string& i_vpdPath,
+                    const std::string& i_recordName,
+                    const std::string& i_keywordName, const bool i_onHardware,
+                    const std::string& i_fileToSave = {});
+
+    /**
+     * @brief Dump the given inventory object in JSON format to console.
+     *
+     * For a given object path of a FRU, this API dumps the following properties
+     * of the FRU in JSON format to console:
+     * - Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     * If the FRU's "Present" property is not true, the above properties are not
+     * dumped to console.
+     *
+     * @param[in] i_fruPath - DBus object path.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int dumpObject(std::string i_fruPath) const noexcept;
+
+    /**
+     * @brief API to fix system VPD keywords.
+     *
+     * The API to fix the system VPD keywords. Mainly used when there
+     * is a mismatch between the primary and backup(secondary) VPD. User can
+     * choose option to update all primary keywords value with corresponding
+     * backup keywords value or can choose primary keyword value to sync
+     * secondary VPD. Otherwise, user can also interactively choose different
+     * action for individual keyword.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int fixSystemVpd() const noexcept;
+
+    /**
+     * @brief Write keyword's value.
+     *
+     * API to update VPD keyword's value to the given input path.
+     * If i_onHardware value in true, i_vpdPath is considered has hardware path
+     * otherwise it will be considered as DBus object path.
+     *
+     * For provided DBus object path both primary path or secondary path will
+     * get updated, also redundant EEPROM(if any) path with new keyword's value.
+     *
+     * In case of hardware path, only given hardware path gets updated with new
+     * keyword’s value, any backup or redundant EEPROM (if exists) paths won't
+     * get updated.
+     *
+     * @param[in] i_vpdPath - DBus object path or EEPROM path.
+     * @param[in] i_recordName - Record name.
+     * @param[in] i_keywordName - Keyword name.
+     * @param[in] i_keywordValue - Keyword value.
+     * @param[in] i_onHardware - True if i_vpdPath is EEPROM path, false
+     * otherwise.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int 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;
+
+    /**
+     * @brief Reset specific keywords on System VPD to default value.
+     *
+     * This API resets specific System VPD keywords to default value. The
+     * keyword values are reset on:
+     * 1. Primary EEPROM path.
+     * 2. Secondary EEPROM path.
+     * 3. D-Bus cache.
+     * 4. Backup path.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int cleanSystemVpd() const noexcept;
+
+    /**
+     * @brief Dump all the inventory objects in JSON or table format to console.
+     *
+     * This API dumps specific properties of all the inventory objects to
+     * console in JSON or table format to console. The inventory object paths
+     * are extracted from PIM. For each object, the following properties are
+     * dumped to console:
+     * - Present property, Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     * If the "Present" property of a FRU is false, the FRU is not dumped to
+     * console.
+     * FRUs whose object path end in "unit([0-9][0-9]?)" are also not dumped to
+     * console.
+     *
+     * @param[in] i_dumpTable - Flag which specifies if the inventory should be
+     * dumped in table format or not.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int dumpInventory(bool i_dumpTable = false) const noexcept;
+};
+} // namespace vpd
diff --git a/vpd-tool/meson.build b/vpd-tool/meson.build
new file mode 100644
index 0000000..a2c1ea1
--- /dev/null
+++ b/vpd-tool/meson.build
@@ -0,0 +1,19 @@
+compiler = meson.get_compiler('cpp')
+if compiler.has_header('CLI/CLI.hpp')
+    CLI11_dep = declare_dependency()
+else
+    CLI11_dep = dependency('CLI11')
+endif
+
+sdbusplus = dependency('sdbusplus', fallback: [ 'sdbusplus', 'sdbusplus_dep' ])
+dependency_list = [CLI11_dep, sdbusplus]
+
+sources = ['src/vpd_tool_main.cpp',
+            'src/vpd_tool.cpp']
+
+vpd_tool_exe = executable('vpd-tool',
+                          sources,
+                          include_directories : ['../', 'include/'],
+                          dependencies: dependency_list,
+                          install: true
+                        )
\ No newline at end of file
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;
+}