TPM code updater

This commit introduces a TPM code updater that currently supports
reading the firmware version for both Infineon and Nuvoton TPM 2.0.
Support for firmware updates will be introduced in a future patch.

The updater's configuration are managed by the EM [1].
[1] https://gerrit.openbmc.org/c/openbmc/entity-manager/+/82416

Tested on Yosemite5 with the following steps:

1. Display the fw inventory:
```
curl --silent $creds https://$bmc/redfish/v1/UpdateService/FirmwareInventory
{
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory",
  "@odata.type": "#SoftwareInventoryCollection.SoftwareInventoryCollection",
  "Members": [
    {...},
    {
      "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945"
    },
    {...}
  ],
  "Members@odata.count": 4,
  "Name": "Software Inventory Collection"
}
```

2. Query TPM version:
```
curl --silent $creds https://$bmc/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945
{
  "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory/Yosemite5_TPM_4945",
  "@odata.type": "#SoftwareInventory.v1_1_0.SoftwareInventory",
  "Description": "Unknown image",
  "Id": "Yosemite5_TPM_4945",
  "Name": "Software Inventory",
  "Status": {
    "Health": "Warning",
    "HealthRollup": "OK",
    "State": "Disabled"
  },
  "Updateable": false,
  "Version": "15.23"
}
```

Change-Id: I42568242356d55fe005ba1f41ddf8aaf9f682fc8
Signed-off-by: Kevin Tung <kevin.tung.openbmc@gmail.com>
diff --git a/common/include/utils.hpp b/common/include/utils.hpp
index 928b136..65d7022 100644
--- a/common/include/utils.hpp
+++ b/common/include/utils.hpp
@@ -2,11 +2,15 @@
 
 #include <sdbusplus/async.hpp>
 
+#include <functional>
+#include <optional>
+
 /**
  * @brief Asynchronously executes a shell command.
  * @param ctx Async context for monitoring the pipe.
  * @param cmd Shell command to execute.
  * @return Task resolving to true on success (exit code 0), false otherwise.
  */
-sdbusplus::async::task<bool> asyncSystem(sdbusplus::async::context& ctx,
-                                         const std::string& cmd);
+sdbusplus::async::task<bool> asyncSystem(
+    sdbusplus::async::context& ctx, const std::string& cmd,
+    std::optional<std::reference_wrapper<std::string>> result = std::nullopt);
diff --git a/common/src/utils.cpp b/common/src/utils.cpp
index 683ca84..2e6d147 100644
--- a/common/src/utils.cpp
+++ b/common/src/utils.cpp
@@ -4,11 +4,14 @@
 
 PHOSPHOR_LOG2_USING;
 
-sdbusplus::async::task<bool> asyncSystem(sdbusplus::async::context& ctx,
-                                         const std::string& cmd)
+sdbusplus::async::task<bool> asyncSystem(
+    sdbusplus::async::context& ctx, const std::string& cmd,
+    std::optional<std::reference_wrapper<std::string>> result)
 {
-    int pipefd[2];
-    if (pipe(pipefd) == -1)
+    int exitPipefd[2];
+    int resultPipefd[2];
+
+    if (pipe(exitPipefd) == -1 || (result && pipe(resultPipefd) == -1))
     {
         error("Failed to create pipe for command: {CMD}", "CMD", cmd);
         co_return false;
@@ -17,32 +20,59 @@
     pid_t pid = fork();
     if (pid == 0)
     {
-        close(pipefd[0]);
+        close(exitPipefd[0]);
+
+        if (result)
+        {
+            close(resultPipefd[0]);
+            dup2(resultPipefd[1], STDOUT_FILENO);
+            dup2(resultPipefd[1], STDERR_FILENO);
+            close(resultPipefd[1]);
+        }
 
         int exitCode = std::system(cmd.c_str());
-        ssize_t status = write(pipefd[1], &exitCode, sizeof(exitCode));
+        ssize_t status = write(exitPipefd[1], &exitCode, sizeof(exitCode));
 
-        close(pipefd[1]);
+        close(exitPipefd[1]);
         _exit((status == sizeof(exitCode)) ? 0 : 1);
     }
     else if (pid > 0)
     {
-        close(pipefd[1]);
+        close(exitPipefd[1]);
 
-        auto fdio = std::make_unique<sdbusplus::async::fdio>(ctx, pipefd[0]);
+        if (result)
+        {
+            close(resultPipefd[1]);
+        }
+
+        auto fdio =
+            std::make_unique<sdbusplus::async::fdio>(ctx, exitPipefd[0]);
 
         if (!fdio)
         {
             error("Failed to create fdio for command: {CMD}", "CMD", cmd);
-            close(pipefd[0]);
+            close(exitPipefd[0]);
             co_return false;
         }
 
         co_await fdio->next();
 
+        if (result)
+        {
+            auto& resStr = result->get();
+            resStr.clear();
+            char buffer[1024];
+            ssize_t n;
+            while ((n = read(resultPipefd[0], buffer, sizeof(buffer))) > 0)
+            {
+                resStr.append(buffer, n);
+            }
+            close(resultPipefd[0]);
+        }
+
         int exitCode = -1;
-        ssize_t bytesRead = read(pipefd[0], &exitCode, sizeof(exitCode));
-        close(pipefd[0]);
+        ssize_t bytesRead = read(exitPipefd[0], &exitCode, sizeof(exitCode));
+        close(exitPipefd[0]);
 
         if (bytesRead != sizeof(exitCode))
         {
@@ -72,8 +102,13 @@
     else
     {
         error("Fork failed for command: {CMD}", "CMD", cmd);
-        close(pipefd[0]);
-        close(pipefd[1]);
+        close(exitPipefd[0]);
+        close(exitPipefd[1]);
+        if (result)
+        {
+            close(resultPipefd[0]);
+            close(resultPipefd[1]);
+        }
         co_return false;
     }
 }