Refactor wait-vpd-status from script to executable

This commit implements wait-vpd-status.sh logic as an application
written in C++. Moving the logic to C++ allows better error handling and
more flexibility with respect to future requirements.

Test:
```
1. Ensure vpd-manager CollectionStatus property is in "Completed" state.
2. Run wait-vpd-parser executable
3. Observe executable waits for 2s, then reads vpd-manager
   CollectionStatus property and outputs a trace saying VPD collection
   is completed, and then exits with return code 0
4. Using busctl change vpd-manager CollectionStatus property to
   "InProgress"
5. Run wait-vpd-parser executable
6. Observe executable waits for 2s, and reads vpd-manager
   CollectionStatus property for a total of 100 retries. After 100
   retries, it outputs a trace saying timeout and exits with return code 1
7. Using busctl change vpd-manager CollectionStatus property to
   "InProgress"
8. Run wait-vpd-parser executable
9. Observe executable waits for 2s, and reads vpd-manager with retry
   count starting from 100 and counting down.
10. Now change CollectionStatus property to "Completed"
11. Observe executable reads vpd-manager CollectionStatus property and
    outputs a trace saying VPD collection is completed, and then exits
   with return code 0.
```

Change-Id: Ifa96a1262b73f4eacc6e13d4e05c710d6e693035
Signed-off-by: Souvik Roy <souvikroyofficial10@gmail.com>
diff --git a/meson.build b/meson.build
index e7983ea..3b99744 100644
--- a/meson.build
+++ b/meson.build
@@ -63,6 +63,7 @@
 
 if get_option('ibm_system').allowed()
     subdir('vpd-tool')
+    subdir('wait-vpd-parser')
     scripts = ['scripts/wait-vpd-status.sh']
 
     install_data(
diff --git a/wait-vpd-parser/meson.build b/wait-vpd-parser/meson.build
new file mode 100644
index 0000000..c9d6a6e
--- /dev/null
+++ b/wait-vpd-parser/meson.build
@@ -0,0 +1,19 @@
+compiler = meson.get_compiler('cpp')
+if compiler.has_header('CLI/CLI.hpp')
+    CLI_dep = declare_dependency()
+else
+    CLI_dep = dependency('CLI11')
+endif
+
+sdbusplus = dependency('sdbusplus', fallback: ['sdbusplus', 'sdbusplus_dep'])
+dependency_list = [CLI_dep, sdbusplus]
+
+sources = ['src/wait_vpd_parser.cpp', '../vpd-manager/src/logger.cpp']
+
+wait_vpd_parser_exe = executable(
+    'wait-vpd-parser',
+    sources,
+    include_directories: ['../', '../vpd-manager/include'],
+    dependencies: dependency_list,
+    install: true,
+)
diff --git a/wait-vpd-parser/src/wait_vpd_parser.cpp b/wait-vpd-parser/src/wait_vpd_parser.cpp
new file mode 100644
index 0000000..56128f3
--- /dev/null
+++ b/wait-vpd-parser/src/wait_vpd_parser.cpp
@@ -0,0 +1,91 @@
+#include "config.h"
+
+#include "constants.hpp"
+#include "logger.hpp"
+#include "utility/dbus_utility.hpp"
+
+#include <CLI/CLI.hpp>
+
+#include <chrono>
+#include <thread>
+
+/**
+ * @brief API to check for VPD collection status
+ *
+ * This API checks for VPD manager collection status by reading the
+ * "CollectionStatus" property exposed by vpd-manager on Dbus. The read logic
+ * uses a retry loop with a specific number of retries with specific sleep time
+ * between each retry.
+ *
+ * @param[in] i_retryLimit - Maximum number of retries
+ * @param[in] i_sleepDurationInSeconds - Sleep time in seconds between each
+ * retry
+ *
+ * @return If "CollectionStatus" property is "Completed", returns 0, otherwise
+ * returns 1.
+ */
+int checkVpdCollectionStatus(const unsigned i_retryLimit,
+                             const unsigned i_sleepDurationInSeconds) noexcept
+{
+    auto l_logger = vpd::Logger::getLoggerInstance();
+
+    try
+    {
+        l_logger->logMessage(
+            "Checking every " + std::to_string(i_sleepDurationInSeconds) +
+            "s for VPD collection status ....");
+
+        for (unsigned l_retries = i_retryLimit;
+             l_retries != vpd::constants::VALUE_0; --l_retries)
+        {
+            // check at specified time interval
+            std::this_thread::sleep_for(
+                std::chrono::seconds(i_sleepDurationInSeconds));
+
+            // TODO: revisit this once "CollectionStatus" property is moved to
+            // xyz interface
+            const auto l_propValue = vpd::dbusUtility::readDbusProperty(
+                IFACE, OBJPATH, IFACE, "CollectionStatus");
+
+            if (auto l_val = std::get_if<std::string>(&l_propValue))
+            {
+                if (*l_val == "Completed")
+                {
+                    l_logger->logMessage("VPD collection is completed");
+                    return vpd::constants::VALUE_0;
+                }
+            }
+
+            l_logger->logMessage(
+                "Waiting for VPD status update. Retries remaining: " +
+                std::to_string(l_retries));
+        }
+
+        l_logger->logMessage(
+            "Exit wait for VPD services to finish with timeout");
+    }
+    catch (const std::exception& l_ex)
+    {
+        l_logger->logMessage("Error while checking VPD collection status: " +
+                             std::string(l_ex.what()));
+    }
+
+    return vpd::constants::VALUE_1;
+}
+
+int main(int argc, char** argv)
+{
+    CLI::App l_app{"Wait VPD parser app"};
+
+    // default retry limit and sleep duration values
+    unsigned l_retryLimit{100};
+    unsigned l_sleepDurationInSeconds{2};
+
+    l_app.add_option("--retryLimit, -r", l_retryLimit, "Retry limit");
+    l_app.add_option("--sleepDurationInSeconds, -s", l_sleepDurationInSeconds,
+                     "Sleep duration in seconds between each retry");
+
+    CLI11_PARSE(l_app, argc, argv);
+
+    return checkVpdCollectionStatus(l_retryLimit, l_sleepDurationInSeconds);
+}