Moves the subprocess function to a library

Test: run on gce
Bug: 79170615
Change-Id: I83ef0092e1a74930d98b35c4a5b359fa98a7b14a
Merged-In: I83ef0092e1a74930d98b35c4a5b359fa98a7b14a
(cherry picked from commit 6a9d629814eef712ff00438cb0e9f139b5cc74e3)
diff --git a/common/libs/Android.bp b/common/libs/Android.bp
index fff0a84..17a4c9d 100644
--- a/common/libs/Android.bp
+++ b/common/libs/Android.bp
@@ -21,4 +21,5 @@
     "tcp_socket",
     "threads",
     "time",
+    "utils",
 ]
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
new file mode 100644
index 0000000..3b6992c
--- /dev/null
+++ b/common/libs/utils/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libcuttlefish_utils",
+    srcs: [
+        "subprocess.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
diff --git a/common/libs/utils/subprocess.cpp b/common/libs/utils/subprocess.cpp
new file mode 100644
index 0000000..593c421
--- /dev/null
+++ b/common/libs/utils/subprocess.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/libs//utils/subprocess.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glog/logging.h>
+namespace {
+pid_t subprocess_impl(const char* const* command, const char* const* envp) {
+  pid_t pid = fork();
+  if (!pid) {
+    int rval;
+    // If envp is NULL, the current process's environment is used as the
+    // environment of the child process. To force an empty emvironment for
+    // the child process pass the address of a pointer to NULL
+    if (envp == NULL) {
+      rval = execv(command[0], const_cast<char* const*>(command));
+    } else {
+      rval = execve(command[0],
+                    const_cast<char* const*>(command),
+                    const_cast<char* const*>(envp));
+    }
+    // No need for an if: if exec worked it wouldn't have returned
+    LOG(ERROR) << "exec of " << command[0] << " failed (" << strerror(errno)
+               << ")";
+    exit(rval);
+  }
+  if (pid == -1) {
+    LOG(ERROR) << "fork failed (" << strerror(errno) << ")";
+  }
+  LOG(INFO) << "Started " << command[0] << ", pid: " << pid;
+  return pid;
+}
+
+int execute_impl(const char* const* command, const char* const* envp) {
+  pid_t pid = subprocess_impl(command, envp);
+  if (pid == -1) {
+    return -1;
+  }
+  int wstatus = 0;
+  waitpid(pid, &wstatus, 0);
+  int retval = 0;
+  if (WIFEXITED(wstatus)) {
+    retval = WEXITSTATUS(wstatus);
+    if (retval) {
+      LOG(ERROR) << "Command " << command[0]
+                 << " exited with error code: " << retval;
+    }
+  } else if (WIFSIGNALED(wstatus)) {
+    LOG(ERROR) << "Command " << command[0]
+               << " was interrupted by a signal: " << WTERMSIG(wstatus);
+    retval = -1;
+  }
+  return retval;
+}
+
+std::vector<const char*> ToCharPointers(
+    const std::vector<std::string>& vect) {
+  std::vector<const char*> ret = {};
+  for (const auto& str : vect) {
+    ret.push_back(str.c_str());
+  }
+  ret.push_back(NULL);
+  return ret;
+}
+}  // namespace
+namespace cvd {
+pid_t subprocess(const std::vector<std::string>& command,
+                 const std::vector<std::string>& env) {
+  auto cmd = ToCharPointers(command);
+  auto envp = ToCharPointers(env);
+
+  return subprocess_impl(cmd.data(), &envp[0]);
+}
+pid_t subprocess(const std::vector<std::string>& command) {
+  auto cmd = ToCharPointers(command);
+
+  return subprocess_impl(cmd.data(), NULL);
+}
+int execute(const std::vector<std::string>& command,
+            const std::vector<std::string>& env) {
+  auto cmd = ToCharPointers(command);
+  auto envp = ToCharPointers(env);
+
+  return execute_impl(cmd.data(), &envp[0]);
+}
+int execute(const std::vector<std::string>& command) {
+  auto cmd = ToCharPointers(command);
+
+  return execute_impl(cmd.data(), NULL);
+}
+}  // namespace cvd
diff --git a/common/libs/utils/subprocess.h b/common/libs/utils/subprocess.h
new file mode 100644
index 0000000..cbb61f1
--- /dev/null
+++ b/common/libs/utils/subprocess.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+#include <vector>
+
+namespace cvd {
+
+// Starts a new child process with the given parameters. Returns the pid of the
+// started process or -1 if failed. The function with no environment arguments
+// launches the subprocess with the same environment as the parent, to have an
+// empty environment just pass an empty vector as second argument.
+pid_t subprocess(const std::vector<std::string>& command,
+                 const std::vector<std::string>& env);
+pid_t subprocess(const std::vector<std::string>& command);
+
+// Same as subprocess, but it waits for the child process before returning.
+// Returns zero if the process completed successfully, non zero otherwise.
+int execute(const std::vector<std::string>& command,
+            const std::vector<std::string>& env);
+int execute(const std::vector<std::string>& command);
+
+}  // namespace cvd
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index 650ccb9..2100e52 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -28,6 +28,7 @@
         "vsoc_lib",
         "libcuttlefish_fs",
         "libcuttlefish_strings",
+        "libcuttlefish_utils",
         "cuttlefish_auto_resources",
         "libicuuc",
         "libbase",
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 8f05e09..e37b98c 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -37,6 +37,7 @@
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/fs/shared_select.h"
 #include "common/libs/strings/str_split.h"
+#include "common/libs/utils/subprocess.h"
 #include "common/vsoc/lib/vsoc_memory.h"
 #include "common/vsoc/shm/screen_layout.h"
 #include "host/commands/launch/pre_launch_initializers.h"
@@ -262,39 +263,6 @@
   KernelLogMonitor& operator=(const KernelLogMonitor&) = delete;
 };
 
-void subprocess(const char* const* command,
-                const char* const* envp,
-                bool wait_for_child = true) {
-  pid_t pid = fork();
-  if (!pid) {
-    int rval;
-    // If envp is NULL, the current process's environment is used as the
-    // environment of the child process. To force an empty emvironment for the
-    // child process pass the address of a pointer to NULL
-    if (envp == NULL) {
-      rval = execv(command[0], const_cast<char* const*>(command));
-    } else {
-      rval = execve(command[0], const_cast<char* const*>(command),
-                    const_cast<char* const*>(envp));
-    }
-    // No need for an if: if exec worked it wouldn't have returned
-    LOG(ERROR) << "exec of " << command[0] << " failed (" << strerror(errno)
-               << ")";
-    exit(rval);
-  }
-  if (pid == -1) {
-    LOG(ERROR) << "fork of " << command[0] << " failed (" << strerror(errno)
-               << ")";
-  }
-  if (pid > 0) {
-    if (wait_for_child) {
-      waitpid(pid, 0, 0);
-    } else {
-      LOG(INFO) << "Started " << command[0] << ", pid: " << pid;
-    }
-  }
-}
-
 bool DirExists(const char* path) {
   struct stat st;
   if (stat(path, &st) == -1)
@@ -320,20 +288,13 @@
   of += image;
   std::string count = "count=";
   count += std::to_string(image_mb);
-  const char* dd_command[]{
-    "/bin/dd", "if=/dev/zero", of.c_str(), "bs=1M", count.c_str(), NULL};
-  subprocess(dd_command, NULL);
-  const char* mkfs_command[]{
-    "/sbin/mkfs", "-t", image_fmt.c_str(), image.c_str(), NULL};
-  const char* envp[]{"PATH=/sbin", NULL};
-  subprocess(mkfs_command, envp);
+  cvd::execute({"/bin/dd", "if=/dev/zero", of, "bs=1M", count});
+  cvd::execute({"/sbin/mkfs", "-t", image_fmt, image}, {"PATH=/sbin"});
 }
 
 void RemoveFile(const std::string& file) {
   LOG(INFO) << "Removing " << file;
-  const char* rm_command[]{
-    "/bin/rm", "-f", file.c_str(), NULL};
-  subprocess(rm_command, NULL);
+  cvd::execute({"/bin/rm", "-f", file});
 }
 
 bool ApplyDataImagePolicy(const char* data_image) {
@@ -380,24 +341,20 @@
   return true;
 }
 
-bool EnsureDirExists(const char* dir) {
-  if (!DirExists(dir)) {
+bool EnsureDirExists(const std::string& dir) {
+  if (!DirExists(dir.c_str())) {
     LOG(INFO) << "Setting up " << dir;
-    if (mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
+    if (mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
       if (errno == EACCES) {
         // TODO(79170615) Don't use sudo once libvirt is replaced
         LOG(WARNING) << "Not enough permission to create " << dir
                      << " retrying with sudo";
-        const char* mkdir_command[]{"/usr/bin/sudo", "/bin/mkdir", "-m",
-                                    "0775",          dir,          NULL};
-        subprocess(mkdir_command, NULL);
+        cvd::execute({"/usr/bin/sudo", "/bin/mkdir", "-m", "0775", dir});
 
         // When created with sudo the owner and group is root.
         std::string user_group = getenv("USER");
         user_group += ":libvirt-qemu";
-        const char* chown_cmd[] = {"/usr/bin/sudo", "/bin/chown",
-                                   user_group.c_str(), dir, NULL};
-        subprocess(chown_cmd, NULL);
+        cvd::execute({"/usr/bin/sudo", "/bin/chown", user_group, dir});
       } else {
         LOG(FATAL) << "Unable to create " << dir << ". Error: " << errno;
         return false;
@@ -443,18 +400,10 @@
 
 void LaunchSocketForwardProxyIfEnabled() {
   if (AdbTunnelEnabled()) {
-    auto guest_port_arg = GetGuestPortArg();
-    auto host_port_arg = GetHostPortArg();
-    auto config_arg = GetConfigFileArg();
-
-    const char* const socket_proxy[] = {
-      FLAGS_socket_forward_proxy_binary.c_str(),
-      guest_port_arg.c_str(),
-      host_port_arg.c_str(),
-      config_arg.c_str(),
-      NULL
-    };
-    subprocess(socket_proxy, nullptr, false);
+    cvd::subprocess({FLAGS_socket_forward_proxy_binary,
+                     GetGuestPortArg(),
+                     GetHostPortArg(),
+                     GetConfigFileArg()});
   }
 }
 
@@ -462,30 +411,16 @@
   if (FLAGS_start_vnc_server) {
     // Launch the vnc server, don't wait for it to complete
     auto port_options = "-port=" + std::to_string(FLAGS_vnc_server_port);
-    auto config_arg = GetConfigFileArg();
-    const char* vnc_command[] = {
-      FLAGS_vnc_server_binary.c_str(),
-      port_options.c_str(),
-      config_arg.c_str(),
-      NULL
-    };
-    subprocess(vnc_command, NULL, false);
+    cvd::subprocess(
+        {FLAGS_vnc_server_binary, port_options, GetConfigFileArg()});
   }
 }
 
 void LaunchWifiRelayIfEnabled() {
   if (FLAGS_start_wifi_relay) {
     // Launch the wifi relay, don't wait for it to complete
-    auto config_arg = GetConfigFileArg();
-    const char* relay_command[] = {
-        "/usr/bin/sudo",
-        "-E",
-        FLAGS_wifi_relay_binary.c_str(),
-        config_arg.c_str(),
-        NULL
-    };
-
-    subprocess(relay_command, NULL /* envp */, false /* wait_for_child */);
+    cvd::subprocess(
+        {"/usr/bin/sudo", "-E", FLAGS_wifi_relay_binary, GetConfigFileArg()});
   }
 }
 bool ResolveInstanceFiles() {
@@ -554,7 +489,7 @@
   auto config = vsoc::CuttlefishConfig::Get();
   // Set this first so that calls to PerInstancePath below are correct
   config->set_instance_dir(FLAGS_instance_dir);
-  if (!EnsureDirExists(FLAGS_instance_dir.c_str())) {
+  if (!EnsureDirExists(FLAGS_instance_dir)) {
     return false;
   }
 
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 1c23f0a..a4bfc66 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -23,6 +23,7 @@
     ],
     shared_libs: [
         "libcuttlefish_fs",
+        "libcuttlefish_utils",
         "cuttlefish_auto_resources",
         "libbase",
         "libicuuc",