utils: Add execute command

As issue openbmc/phosphor-bmc-code-mgmt#6 describes, the code
makes use of the systemd services to execute commands via a
script, but calls to systemd service files are async, so if
there is a need to wait for the service to finish executing,
workarounds like sleep commands have been added to the code, ex:
https://github.com/openbmc/phosphor-bmc-code-mgmt/commit/60f5ccfd5ab0fc8cedc3a2bf5f5adcab77318b7d

Create a function that would execute a command in a child
process so that it's possible to wait for it to finish. In
addition, errors can be more obvious by checking the return
of the execute function and taking action to notify the caller
of the error instead of relying on system unit dependencies
to run recovery actions on error.

Tested: Called new execute function from the code and verified
        it was successful. Ex:
        utils::execute("/bin/mkdir", "/tmp/test-execute");

Change-Id: I81a6aa0a50276abb6aba40196a214629ec9baa13
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/utils.cpp b/utils.cpp
index b9611a3..f392983 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -1,5 +1,7 @@
 #include "utils.hpp"
 
+#include <unistd.h>
+
 #include <phosphor-logging/log.hpp>
 
 namespace utils
@@ -66,4 +68,63 @@
     outFile.close();
 }
 
+namespace internal
+{
+
+/* @brief Helper function to build a string from command arguments */
+static std::string buildCommandStr(const char* name, char** args)
+{
+    std::string command = name;
+    for (int i = 0; args[i]; i++)
+    {
+        command += " ";
+        command += args[i];
+    }
+    return command;
+}
+
+int executeCmd(const char* path, char** args)
+{
+    pid_t pid = fork();
+    if (pid == 0)
+    {
+        execv(path, args);
+
+        // execv only retruns on error
+        auto error = errno;
+        auto command = buildCommandStr(path, args);
+        log<level::ERR>("Failed to execute command", entry("ERRNO=%d", error),
+                        entry("COMMAND=%s", command.c_str()));
+        return -1;
+    }
+    else if (pid > 0)
+    {
+        int status;
+        if (waitpid(pid, &status, 0) < 0)
+        {
+            auto error = errno;
+            log<level::ERR>("waitpid error", entry("ERRNO=%d", error));
+            return -1;
+        }
+        else if (WEXITSTATUS(status) != 0)
+        {
+            auto command = buildCommandStr(path, args);
+            log<level::ERR>("Error occurred when executing command",
+                            entry("STATUS=%d", status),
+                            entry("COMMAND=%s", command.c_str()));
+            return -1;
+        }
+    }
+    else
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
+        return -1;
+    }
+
+    return 0;
+}
+
+} // namespace internal
+
 } // namespace utils