VPD tool : Read Keyword option

One of the options the VPD tool provides is
to Read the value of the keyword. This commit has its implementation.

The user should provide a valid object path, valid record name
and valid keyword inorder to get the value of the keyword.

Test:
Tested on simics.

Output:

root@rainier:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/vdd_vrm1 -R VINI -K FN
{
    "/system/chassis/motherboard/vdd_vrm1": {
        "FN": "F190827"
    }
}

Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: I244b9fe276feefa27e4c99063a9e9aa01aeb2f12
diff --git a/meson.build b/meson.build
index ac6ba29..8dfed27 100644
--- a/meson.build
+++ b/meson.build
@@ -23,7 +23,8 @@
                        configuration :{
                        'INVENTORY_JSON': '"'+get_option('INVENTORY_JSON')+'"',
                        'INVENTORY_PATH': '"'+get_option('INVENTORY_PATH')+'"',
-		       'INVENTORY_MANAGER_SERVICE': '"'+get_option('INVENTORY_MANAGER_SERVICE')+'"'
+		       'INVENTORY_MANAGER_SERVICE': '"'+get_option('INVENTORY_MANAGER_SERVICE')+'"',
+		       'IPZ_INTERFACE': '"'+get_option('IPZ_INTERFACE')+'"'
                        }
   )
         ibm_read_vpd_SOURCES = ['ibm_vpd_app.cpp',
diff --git a/meson_options.txt b/meson_options.txt
index 9c094c7..b3e2a34 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,3 +6,4 @@
 option('INVENTORY_JSON',type: 'string', value: '/usr/share/vpd/vpd_inventory.json',  description: 'JSON file that defines inventory blueprint')
 option('INVENTORY_PATH',type: 'string', value: '/xyz/openbmc_project/inventory', description: 'Prefix for inventory D-Bus objects')
 option('INVENTORY_MANAGER_SERVICE',type: 'string', value: 'xyz.openbmc_project.Inventory.Manager', description: 'Inventory manager service')
+option('IPZ_INTERFACE', type: 'string', value: 'com.ibm.ipzvpd', description: 'IPZ vpd interface')
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
index 0498761..d988f63 100644
--- a/vpd_tool.cpp
+++ b/vpd_tool.cpp
@@ -13,9 +13,14 @@
             "update the keywords"};
 
     string objectPath{};
+    string recordName{};
+    string keyword{};
 
     auto object =
         app.add_option("--object, -O", objectPath, "Enter the Object Path");
+    auto record =
+        app.add_option("--record, -R", recordName, "Enter the Record Name");
+    auto kw = app.add_option("--keyword, -K", keyword, "Enter the Keyword");
 
     auto dumpObjFlag =
         app.add_flag("--dumpObject, -o",
@@ -27,6 +32,16 @@
         "--dumpInventory, -i", "Dump all the inventory objects. { vpd-tool-exe "
                                "--dumpInventory/-i }");
 
+    auto readFlag =
+        app.add_flag("--readKeyword, -r",
+                     "Read the data of the given keyword. { "
+                     "vpd-tool-exe --readKeyword/-r --object/-O "
+                     "\"object-name\" --record/-R \"record-name\" --keyword/-K "
+                     "\"keyword-name\" }")
+            ->needs(object)
+            ->needs(record)
+            ->needs(kw);
+
     CLI11_PARSE(app, argc, argv);
 
     ifstream inventoryJson(INVENTORY_JSON);
@@ -46,6 +61,13 @@
             vpdToolObj.dumpInventory(jsObject);
         }
 
+        else if (*readFlag)
+        {
+            VpdTool vpdToolObj(move(objectPath), move(recordName),
+                               move(keyword));
+            vpdToolObj.readKeyword();
+        }
+
         else
         {
             throw runtime_error("One of the valid options is required. Refer "
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index ccc2765..d7c8504 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -232,3 +232,60 @@
     json output = parseInvJson(jsObject, flag, fruPath);
     debugger(output);
 }
+
+void VpdTool::readKeyword()
+{
+    string interface = "com.ibm.ipzvpd.";
+    variant<Binary> response;
+
+    try
+    {
+        json output = json::object({});
+        json kwVal = json::object({});
+        makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, keyword)
+            .read(response);
+
+        if (auto vec = get_if<Binary>(&response))
+        {
+            kwVal.emplace(keyword, string(vec->begin(), vec->end()));
+        }
+
+        output.emplace(fruPath, kwVal);
+
+        debugger(output);
+    }
+    catch (json::exception& e)
+    {
+        json output = json::object({});
+        json kwVal = json::object({});
+
+        if (e.id == 316) // invalid UTF-8 byte exception
+        {
+            stringstream ss;
+            string hexByte;
+            string hexRep = "0x";
+            ss << hexRep;
+            hexByte = ss.str();
+
+            // convert Decimal to Hex
+            if (auto resp = get_if<Binary>(&response))
+            {
+                for (auto& vec : *resp)
+                {
+                    if ((int)vec == 0)
+                    {
+                        ss << hex << (int)vec;
+                        hexByte = ss.str();
+                    }
+                    ss << hex << (int)vec;
+                    hexByte = ss.str();
+                }
+            }
+
+            kwVal.emplace(keyword, hexByte);
+            output.emplace(fruPath, kwVal);
+
+            debugger(output);
+        }
+    }
+}
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index 378ece2..d31bc25 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -10,6 +10,8 @@
 {
   private:
     const std::string fruPath;
+    const std::string recordName;
+    const std::string keyword;
 
     /**
      * @brief Debugger
@@ -117,6 +119,14 @@
     void dumpObject(const nlohmann::basic_json<>& jsObject);
 
     /**
+     * @brief Read keyword
+     * Read the given object path, record name and keyword
+     * from the inventory and display the value of the keyword
+     * in JSON format.
+     */
+    void readKeyword();
+
+    /**
      * @brief A Constructor
      * Constructor is called during the
      * object instantiation for dumpInventory option.
@@ -133,4 +143,16 @@
     VpdTool(const std::string&& fru) : fruPath(std::move(fru))
     {
     }
+
+    /**
+     * @brief Constructor
+     * Constructor is called during the
+     * object instantiation for readKeyword option.
+     */
+    VpdTool(const std::string&& fru, const std::string&& recName,
+            const std::string&& kw) :
+        fruPath(std::move(fru)),
+        recordName(std::move(recName)), keyword(std::move(kw))
+    {
+    }
 };