VPD Tool: Write keyword command line interface

This commit has the command line interface
for writeKeyword option in the VPD tool.

--help option will provide the user - the argument format
for writeKeyword option.

Note:
	The application "vpd-manager" acts as the backend for the keyword updation.
	The vpd-manager executable should run in the background while testing in simics.
	The commits which points to the vpd-manager backend application are,
		< https://gerrit.openbmc-project.xyz/c/openbmc/openpower-vpd-parser/+/28991/43 >
		< https://gerrit.openbmc-project.xyz/c/openbmc/openpower-vpd-parser/+/29039/43 >
		< https://gerrit.openbmc-project.xyz/c/openbmc/openpower-vpd-parser/+/32439/10 >

Tested on rainier:
root@rainier:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/vdd_vrm1 -R VINI -K FN
{
    "/system/chassis/motherboard/vdd_vrm1": {
        "FN": "F190827"
    }
}
root@rainier:/tmp#
root@rainier:/tmp# ./vpd-tool -w -O /system/chassis/motherboard/vdd_vrm1 -R VINI -K FN -V 0x616263
root@rainier:/tmp#
root@rainier:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/vdd_vrm1 -R VINI -K FN
{
    "/system/chassis/motherboard/vdd_vrm1": {
        "FN": "abc0827"
    }
}

Change-Id: I6c586a9848497e44bf88eda78694989d0287cbba
Signed-off-by: Priyanga Ramasamy <priyanga24@in.ibm.com>
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
index d988f63..0514cc5 100644
--- a/vpd_tool.cpp
+++ b/vpd_tool.cpp
@@ -9,18 +9,24 @@
 
 int main(int argc, char** argv)
 {
+    int rc = 0;
     App app{"VPD Command line tool to dump the inventory and to read and "
             "update the keywords"};
 
     string objectPath{};
     string recordName{};
     string keyword{};
+    string val{};
 
     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 valOption = app.add_option(
+        "--value, -V", val,
+        "Enter the value. The value to be updated should be either in ascii or "
+        "in hex. ascii eg: 01234; hex eg: 0x30313233");
 
     auto dumpObjFlag =
         app.add_flag("--dumpObject, -o",
@@ -42,6 +48,18 @@
             ->needs(record)
             ->needs(kw);
 
+    auto writeFlag =
+        app.add_flag(
+               "--writeKeyword, -w, --updateKeyword, -u",
+               "Update the value. { vpd-tool-exe "
+               "--writeKeyword/-w/--updateKeyword/-u "
+               "--object/-O object-name --record/-R record-name --keyword/-K "
+               "keyword-name --value/-V value-to-be-updated }")
+            ->needs(object)
+            ->needs(record)
+            ->needs(kw)
+            ->needs(valOption);
+
     CLI11_PARSE(app, argc, argv);
 
     ifstream inventoryJson(INVENTORY_JSON);
@@ -68,6 +86,13 @@
             vpdToolObj.readKeyword();
         }
 
+        else if (*writeFlag)
+        {
+            VpdTool vpdToolObj(move(objectPath), move(recordName),
+                               move(keyword), move(val));
+            rc = vpdToolObj.updateKeyword();
+        }
+
         else
         {
             throw runtime_error("One of the valid options is required. Refer "
@@ -80,5 +105,5 @@
         cerr << e.what();
     }
 
-    return 0;
+    return rc;
 }
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index d7c8504..893109c 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -1,5 +1,6 @@
 #include "vpd_tool_impl.hpp"
 
+#include <iomanip>
 #include <iostream>
 #include <sdbusplus/bus.hpp>
 #include <sstream>
@@ -289,3 +290,50 @@
         }
     }
 }
+
+int VpdTool::updateKeyword()
+{
+    Binary val;
+
+    if (value.find("0x") == string::npos)
+    {
+        val.assign(value.begin(), value.end());
+    }
+    else if (value.find("0x") != string::npos)
+    {
+        stringstream ss;
+        ss.str(value.substr(2));
+        string byteStr{};
+
+        while (!ss.eof())
+        {
+            ss >> setw(2) >> byteStr;
+            uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
+
+            val.push_back(byte);
+        }
+    }
+
+    else
+    {
+        throw runtime_error("The value to be updated should be either in ascii "
+                            "or in hex. Refer --help option");
+    }
+
+    // writeKeyword(fruPath, recordName, keyword, val);
+
+    auto bus = sdbusplus::bus::new_default();
+    auto properties =
+        bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
+    properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
+    properties.append(recordName);
+    properties.append(keyword);
+    properties.append(val);
+    auto result = bus.call(properties);
+
+    if (result.is_method_error())
+    {
+        throw runtime_error("Get api failed");
+    }
+    return 0;
+}
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index d31bc25..6c9cf12 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -12,6 +12,7 @@
     const std::string fruPath;
     const std::string recordName;
     const std::string keyword;
+    const std::string value;
 
     /**
      * @brief Debugger
@@ -127,6 +128,14 @@
     void readKeyword();
 
     /**
+     * @brief Update Keyword
+     * Update the given keyword with the given value.
+     *
+     * @return return code (Success(0)/Failure(-1))
+     */
+    int updateKeyword();
+
+    /**
      * @brief A Constructor
      * Constructor is called during the
      * object instantiation for dumpInventory option.
@@ -155,4 +164,18 @@
         recordName(std::move(recName)), keyword(std::move(kw))
     {
     }
+
+    /**
+     * @brief Constructor
+     * Constructor is called during the
+     * object instantiation for updateKeyword option.
+     */
+
+    VpdTool(const std::string&& fru, const std::string&& recName,
+            const std::string&& kw, const std::string&& val) :
+        fruPath(std::move(fru)),
+        recordName(std::move(recName)), keyword(std::move(kw)),
+        value(std::move(val))
+    {
+    }
 };