field mode: restore works on all systems

The restoreFieldModeStatus function previously read from a chip
that is not found on all systems. Changed it to read fieldmode
status using fw_printenv. Updated utils::executeCmd to return the
print output of the command it executes.

Tested:
On p10bmc running mmc chip:
- Set the fieldmode env variable to "true" and verified FieldModeEnabled
  was set to true:
root@p10bmc:~# fw_setenv fieldmode true
root@p10bmc:~# systemctl restart xyz.openbmc_project.Software.BMC.Updater.service
root@p10bmc:~# busctl introspect xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software
...
xyz.openbmc_project.Control.FieldMode       interface -          -
.FieldModeEnabled                           property  b          true

- Set the fieldmode env variable to "false" (fw_setenv fieldmode false)
  and also clear the variable (fw_setenv fieldmode) and verified
  FieldModeEnabled was set to false with no error messages in the
  journal:
root@p10bmc:~# fw_setenv fieldmode
root@p10bmc:~# systemctl restart xyz.openbmc_project.Software.BMC.Updater.service
root@p10bmc:~# busctl introspect xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software
...
xyz.openbmc_project.Control.FieldMode       interface -          -
.FieldModeEnabled                           property  b          false

Change-Id: Ib1b54d83f058015ff5967c445a40318a02baae92
Signed-off-by: Isaac Kurth <blisaac91@gmail.com>
Signed-off-by: Adriana Kobylak <anoo@linux.ibm.com>
diff --git a/item_updater.cpp b/item_updater.cpp
index 96782f2..2fdbc31 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -602,11 +602,20 @@
 
 void ItemUpdater::restoreFieldModeStatus()
 {
-    std::ifstream input("/dev/mtd/u-boot-env");
-    std::string envVar;
-    std::getline(input, envVar);
+    // The fieldmode u-boot environment variable may not exist since it is not
+    // part of the default environment, run fw_printenv with 2>&1 to ignore the
+    // error message in the journal "Error: "fieldmode" not defined"
+    std::pair<int, std::string> ret =
+        utils::execute("/sbin/fw_printenv", "-n", "fieldmode", "2>&1");
 
-    if (envVar.find("fieldmode=true") != std::string::npos)
+    if (ret.first != 0)
+    {
+        return;
+    }
+
+    // truncate any extra characters off the end to compare against a "true" str
+    std::string result = ret.second.substr(0, 4);
+    if (result == "true")
     {
         ItemUpdater::fieldModeEnabled(true);
     }
diff --git a/utils.cpp b/utils.cpp
index 91857fc..204affa 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -88,54 +88,35 @@
 {
 
 /* @brief Helper function to build a string from command arguments */
-static std::string buildCommandStr(const char* name, char** args)
+static std::string buildCommandStr(char** args)
 {
-    std::string command = name;
+    std::string command = "";
     for (int i = 0; args[i]; i++)
     {
-        command += " ";
         command += args[i];
+        command += " ";
     }
     return command;
 }
 
-int executeCmd(const char* path, char** args)
+std::pair<int, std::string> executeCmd(char** args)
 {
-    pid_t pid = fork();
-    if (pid == 0)
+    std::array<char, 512> buffer;
+    std::string cmd = buildCommandStr(args);
+    std::stringstream result;
+    int rc;
+    FILE* pipe = popen(cmd.c_str(), "r");
+    if (!pipe)
     {
-        execv(path, args);
-
-        // execv only retruns on err
-        auto err = errno;
-        auto command = buildCommandStr(path, args);
-        error("Failed ({ERRNO}) to execute command: {COMMAND}", "ERRNO", err,
-              "COMMAND", command);
-        return -1;
+        error("Failed to execute command: {COMMAND}", "COMMAND", cmd);
+        return {-1, std::string{}};
     }
-    else if (pid > 0)
+    while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
     {
-        int status;
-        if (waitpid(pid, &status, 0) < 0)
-        {
-            error("Error ({ERRNO}) during waitpid.", "ERRNO", errno);
-            return -1;
-        }
-        else if (WEXITSTATUS(status) != 0)
-        {
-            auto command = buildCommandStr(path, args);
-            error("Error ({STATUS}) occurred when executing command: {COMMAND}",
-                  "STATUS", status, "COMMAND", command);
-            return -1;
-        }
+        result << buffer.data();
     }
-    else
-    {
-        error("Error ({ERRNO}) during fork.", "ERRNO", errno);
-        return -1;
-    }
-
-    return 0;
+    rc = pclose(pipe);
+    return {rc, result.str()};
 }
 
 } // namespace internal
diff --git a/utils.hpp b/utils.hpp
index af6dd98..be4fb2c 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -96,11 +96,10 @@
 
 /**
  * @brief Helper function to execute command in child process
- * @param[in] path - Fully qualified name of the executable to run
- * @param[in] args - Optional arguments
- * @return 0 on success
+ * @param[in] args - Executable plus optional arguments
+ * @return 0 and command output on success
  */
-int executeCmd(const char* path, char** args);
+std::pair<int, std::string> executeCmd(char** args);
 
 } // namespace internal
 
@@ -108,14 +107,14 @@
  * @brief Execute command in child process
  * @param[in] path - Fully qualified name of the executable to run
  * @param[in] args - Optional arguments
- * @return 0 on success
+ * @return 0 and command output on success
  */
 template <typename... Arg>
-int execute(const char* path, Arg&&... args)
+std::pair<int, std::string> execute(const char* path, Arg&&... args)
 {
     auto argArray = internal::constructArgv(path, std::forward<Arg>(args)...);
 
-    return internal::executeCmd(path, argArray.data());
+    return internal::executeCmd(argArray.data());
 }
 
 } // namespace utils