update_engine: pipe stderr individually in SycnronousExec

Currently, SyncronousExec pipes stderr to stdout which is fine but not
ideal. Specially we have an issue with vpd_get_value script that exits
with 0 even with underlying failures. This is problematic, because we
get the combined stdout/stderr of the command and since the exit code is
0 we assume the output is correct. Then, we create the XML request based
on this output but with stderr combined (too much junk) as the value of
an XML attribute. This causes update failure. Fortunately, vpd_get_value
correctly separates its children's stderr and stdout. So as long as we
don't combine both stdout and stderr into one stream, this error wil not
happen again anymore.

Also a few other nitpicks in this CL:
- Constructing the command for shutdown using simpler syntax.
- Logging the command before running it for all external subprocess runs.

BUG=chromium:1010306
TEST=sudo FEATURES=test emerge update_engine

Change-Id: Ia620afed814e4fe9ba24b1a0ad01680481c6ba7c
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/1901886
Tested-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Andrew Lassalle <lassalle@chromium.org>
Reviewed-by: Jae Hoon Kim <kimjae@chromium.org>
Commit-Queue: Amin Hassani <ahassani@chromium.org>
diff --git a/common/subprocess.cc b/common/subprocess.cc
index 36655c7..5206503 100644
--- a/common/subprocess.cc
+++ b/common/subprocess.cc
@@ -95,6 +95,7 @@
   proc->RedirectUsingPipe(STDOUT_FILENO, false);
   proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags));
 
+  LOG(INFO) << "Running \"" << base::JoinString(cmd, " ") << "\"";
   return proc->Start();
 }
 
@@ -229,22 +230,20 @@
 
 bool Subprocess::SynchronousExec(const vector<string>& cmd,
                                  int* return_code,
-                                 string* stdout) {
-  // The default for SynchronousExec is to use kSearchPath since the code relies
-  // on that.
-  return SynchronousExecFlags(
-      cmd, kRedirectStderrToStdout | kSearchPath, return_code, stdout);
+                                 string* stdout,
+                                 string* stderr) {
+  // The default for |SynchronousExec| is to use |kSearchPath| since the code
+  // relies on that.
+  return SynchronousExecFlags(cmd, kSearchPath, return_code, stdout, stderr);
 }
 
 bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
                                       uint32_t flags,
                                       int* return_code,
-                                      string* stdout) {
+                                      string* stdout,
+                                      string* stderr) {
   brillo::ProcessImpl proc;
-  // It doesn't make sense to redirect some pipes in the synchronous case
-  // because we won't be reading on our end, so we don't expose the output_pipes
-  // in this case.
-  if (!LaunchProcess(cmd, flags, {}, &proc)) {
+  if (!LaunchProcess(cmd, flags, {STDERR_FILENO}, &proc)) {
     LOG(ERROR) << "Failed to launch subprocess";
     return false;
   }
@@ -252,21 +251,39 @@
   if (stdout) {
     stdout->clear();
   }
+  if (stderr) {
+    stderr->clear();
+  }
 
-  int fd = proc.GetPipe(STDOUT_FILENO);
+  // Read from both stdout and stderr individually.
+  int stdout_fd = proc.GetPipe(STDOUT_FILENO);
+  int stderr_fd = proc.GetPipe(STDERR_FILENO);
   vector<char> buffer(32 * 1024);
-  while (true) {
-    int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size()));
-    if (rc < 0) {
-      PLOG(ERROR) << "Reading from child's output";
-      break;
-    } else if (rc == 0) {
-      break;
-    } else {
-      if (stdout)
+  bool stdout_closed = false, stderr_closed = false;
+  while (!stdout_closed || !stderr_closed) {
+    if (!stdout_closed) {
+      int rc = HANDLE_EINTR(read(stdout_fd, buffer.data(), buffer.size()));
+      if (rc <= 0) {
+        stdout_closed = true;
+        if (rc < 0)
+          PLOG(ERROR) << "Reading from child's stdout";
+      } else if (stdout != nullptr) {
         stdout->append(buffer.data(), rc);
+      }
+    }
+
+    if (!stderr_closed) {
+      int rc = HANDLE_EINTR(read(stderr_fd, buffer.data(), buffer.size()));
+      if (rc <= 0) {
+        stderr_closed = true;
+        if (rc < 0)
+          PLOG(ERROR) << "Reading from child's stderr";
+      } else if (stderr != nullptr) {
+        stderr->append(buffer.data(), rc);
+      }
     }
   }
+
   // At this point, the subprocess already closed the output, so we only need to
   // wait for it to finish.
   int proc_return_code = proc.Wait();