Make the launcher exit on boot completed/failed

By default the launcher behaves as usual (blocking until the device is
stopped), but when given the --daemon flag it forks after doing the
necessary preprocessing and launches everything in background, exiting
once it gets the boot completed or failed signal.

Bug: 111307422
Test: local
Change-Id: I6a2f48df70c878d3f1b2bdf671770730e935e7c0
Merged-In: I6a2f48df70c878d3f1b2bdf671770730e935e7c0
(cherry picked from commit 1300f0bc85e0c04727f78c070dfb2eb1c66f35f5)
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 26cbf3f..58b43f0 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -15,6 +15,7 @@
  */
 
 #include <limits.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -82,7 +83,7 @@
 DEFINE_bool(disable_dac_security, false,
             "Disable DAC security in libvirt. For debug only.");
 DEFINE_string(kernel_path, "",
-	      "Path to the kernel. Overrides the one from the boot image");
+              "Path to the kernel. Overrides the one from the boot image");
 DEFINE_string(extra_kernel_command_line, "",
               "Additional flags to put on the kernel command line");
 DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
@@ -160,6 +161,9 @@
 constexpr char kDefaultUuidPrefix[] = "699acfc4-c8c4-11e7-882b-5065f31dc1";
 DEFINE_string(uuid, vsoc::GetPerInstanceDefault(kDefaultUuidPrefix).c_str(),
               "UUID to use for the device. Random if not specified");
+DEFINE_bool(daemon, false,
+            "Run cuttlefish in background, the launcher exits on boot "
+            "completed/failed");
 
 DECLARE_string(config_file);
 
@@ -618,6 +622,44 @@
   env->Write(config_env.c_str(), config_env.size());
   return true;
 }
+
+// Forks and returns the write end of a pipe to the child process. The parent
+// process waits for boot events to come through the pipe and exits accordingly.
+cvd::SharedFD DaemonizeLauncher() {
+  cvd::SharedFD read_end, write_end;
+  if (!cvd::SharedFD::Pipe(&read_end, &write_end)) {
+    LOG(ERROR) << "Unable to create pipe";
+    return cvd::SharedFD(); // a closed FD
+  }
+  auto pid = fork();
+  if (pid) {
+    // Explicitly close here, otherwise we may end up reading forever if the
+    // child process dies.
+    write_end->Close();
+    monitor::BootEvent evt;
+    while(true) {
+      auto bytes_read = read_end->Read(&evt, sizeof(evt));
+      if (bytes_read != sizeof(evt)) {
+        LOG(ERROR) << "Fail to read a complete event, read " << bytes_read
+                   << " bytes only instead of the expected " << sizeof(evt);
+        std::exit(10);
+      }
+      if (evt == monitor::BootEvent::BootCompleted) {
+        std::exit(0);
+      }
+      if (evt == monitor::BootEvent::BootFailed) {
+        std::exit(11);
+      }
+      // Do nothing for the other signals
+    }
+  } else {
+    // The child returns the write end of the pipe
+    // TODO(111321286): Make the child the head of a new process group that will
+    // contain all other host processes.
+    read_end->Close();
+    return write_end;
+  }
+}
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -667,6 +709,38 @@
   LOG(INFO) << "To access the console run: socat file:$(tty),raw,echo=0 "
             << config->console_path();
 
+  KernelLogMonitor kmon(config->kernel_log_socket_name(),
+                        config->PerInstancePath("kernel.log"),
+                        FLAGS_deprecated_boot_completed);
+
+  if (FLAGS_daemon) {
+    // This code will move to its own process eventually so the signal handling
+    // is done here to keep everything that needs to move close together.
+    // Disable default handling of SIGPIPE.
+    struct sigaction new_action{}, old_action{};
+    new_action.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &new_action, &old_action);
+
+    auto pipe_fd = DaemonizeLauncher();
+    if (!pipe_fd->IsOpen()) {
+      return 9;
+    }
+    kmon.SubscribeToBootEvents([pipe_fd](monitor::BootEvent evt) {
+      int retval = pipe_fd->Write(&evt, sizeof(evt));
+      if (retval < 0) {
+        if (pipe_fd->GetErrno() == EPIPE) {
+          pipe_fd->Close();
+        } else {
+          LOG(ERROR) << "Error while writing to pipe: " << pipe_fd->StrError();
+        }
+        return monitor::SubscriptionAction::CancelSubscription;
+      }
+      return monitor::SubscriptionAction::ContinueSubscription;
+    });
+  }
+
+  kmon.Start();
+
   // Start the usb manager
   VirtualUSBManager vadb(config->usb_v1_socket_name(), config->vhci_port(),
                          config->usb_ip_socket_name());
@@ -674,11 +748,6 @@
 
   LaunchIvServer();
 
-  KernelLogMonitor kmon(config->kernel_log_socket_name(),
-                        config->PerInstancePath("kernel.log"),
-                        FLAGS_deprecated_boot_completed);
-  kmon.Start();
-
   // Initialize the regions that require so before the VM starts.
   PreLaunchInitializers::Initialize();
 
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 10fa693..1630f5e 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -98,7 +98,7 @@
     if (status != 0) {
       LOG(FATAL) << "Qemu process exited prematurely";
     } else {
-      LOG(ERROR) << "Qemu process exited normally, it shouldn't happen";
+      LOG(INFO) << "Qemu process exited normally";
     }
   });
   waiting_thread.detach();