Starts a host process to receive logcat output over vsock

The host process is started conditionally on the logcat_mode launcher
flag (when set to 'vsock'). The process accepts connections (one at a
time) on a vsock socket and forwards everything to the logcat file in
the instance directory.

Bug: 124535805
Test: run locally
Change-Id: I2dbff42735a9dc16079e8adb5862ead3c7581de4
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
index 797c1ea..4548588 100644
--- a/host/commands/launch/flags.cc
+++ b/host/commands/launch/flags.cc
@@ -172,7 +172,13 @@
 DEFINE_string(e2e_test_binary,
               vsoc::DefaultHostArtifactsPath("bin/host_region_e2e_test"),
               "Location of the region end to end test binary");
-
+DEFINE_string(logcat_receiver_binary,
+              vsoc::DefaultHostArtifactsPath("bin/logcat_receiver"),
+              "Binary for the logcat server");
+DEFINE_string(logcat_mode, "", "How to send android's log messages from "
+                               "guest to host. One of [serial, vsock]");
+DEFINE_int32(logcat_vsock_port, vsoc::GetPerInstanceDefault(5620),
+             "The port for logcat over vsock");
 namespace {
 
 template<typename S, typename T>
@@ -297,6 +303,10 @@
     tmp_config_obj.add_kernel_cmdline(
         concat("androidboot.hardware=", FLAGS_hardware_name));
   }
+  if (FLAGS_logcat_mode == cvd::kLogcatVsockMode) {
+    tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_logcat_port=",
+                                             FLAGS_logcat_vsock_port));
+  }
   tmp_config_obj.set_hardware_name(FLAGS_hardware_name);
   if (!FLAGS_guest_security.empty()) {
     tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
@@ -344,6 +354,7 @@
   tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
   tmp_config_obj.set_console_path(tmp_config_obj.PerInstancePath("console"));
   tmp_config_obj.set_logcat_path(tmp_config_obj.PerInstancePath("logcat"));
+  tmp_config_obj.set_logcat_receiver_binary(FLAGS_logcat_receiver_binary);
   tmp_config_obj.set_launcher_log_path(tmp_config_obj.PerInstancePath("launcher.log"));
   tmp_config_obj.set_launcher_monitor_socket_path(
       tmp_config_obj.PerInstancePath("launcher_monitor.sock"));
@@ -396,6 +407,9 @@
     tmp_config_obj.disable_usb_adb();
   }
 
+  tmp_config_obj.set_logcat_mode(FLAGS_logcat_mode);
+  tmp_config_obj.set_logcat_vsock_port(FLAGS_logcat_vsock_port);
+
   tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
 
   auto config_file = GetConfigFilePath(tmp_config_obj);
@@ -444,6 +458,8 @@
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("decompress_kernel", "false",
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatSerialMode,
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
 
 void SetDefaultFlagsForCrosvm() {
@@ -481,6 +497,8 @@
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
   SetCommandLineOptionWithMode("start_stream_audio", "false",
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode,
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
 }
 
 bool ParseCommandLineFlags(int* argc, char*** argv) {
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
index 4bf3db9..497b6c4 100644
--- a/host/commands/launch/launch.cc
+++ b/host/commands/launch/launch.cc
@@ -83,6 +83,10 @@
   return vsoc::GetPerInstanceDefault(kFirstHostPort);
 }
 
+bool LogcatReceiverEnabled(const vsoc::CuttlefishConfig& config) {
+  return config.logcat_mode() == cvd::kLogcatVsockMode;
+}
+
 bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config) {
   return AdbModeEnabled(config, kAdbModeUsb);
 }
@@ -141,6 +145,24 @@
   return kernel_log_monitor;
 }
 
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor) {
+  if (!LogcatReceiverEnabled(config)) {
+    return;
+  }
+  auto port = config.logcat_vsock_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+  if (!socket->IsOpen()) {
+    LOG(ERROR) << "Unable to create logcat server socket: "
+               << socket->StrError();
+    std::exit(LauncherExitCodes::kLogcatServerError);
+  }
+  cvd::Command cmd(config.logcat_receiver_binary());
+  cmd.AddParameter("-server_fd=", socket);
+  process_monitor->StartSubprocess(std::move(cmd),
+                                   GetOnSubprocessExitCallback(config));
+}
+
 void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
                               cvd::ProcessMonitor* process_monitor) {
   if (!AdbUsbEnabled(config)) {
diff --git a/host/commands/launch/launch.h b/host/commands/launch/launch.h
index 02f3a38..550ce02 100644
--- a/host/commands/launch/launch.h
+++ b/host/commands/launch/launch.h
@@ -13,6 +13,8 @@
 cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config);
 cvd::Command GetKernelLogMonitorCommand(const vsoc::CuttlefishConfig& config,
                                         cvd::SharedFD* boot_events_pipe);
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor);
 void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
                               cvd::ProcessMonitor* process_monitor);
 void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
diff --git a/host/commands/launch/launcher_defs.h b/host/commands/launch/launcher_defs.h
index ccf859c..9a78a4e 100644
--- a/host/commands/launch/launcher_defs.h
+++ b/host/commands/launch/launcher_defs.h
@@ -17,6 +17,9 @@
 
 namespace cvd {
 
+constexpr char kLogcatSerialMode[] = "serial";
+constexpr char kLogcatVsockMode[] = "vsock";
+
 enum LauncherExitCodes : int {
   kSuccess = 0,
   kArgumentParsingError = 1,
@@ -36,6 +39,7 @@
   kUsbV1SocketError = 15,
   kE2eTestFailed = 16,
   kKernelDecompressError = 17,
+  kLogcatServerError = 18,
 };
 
 // Actions supported by the launcher server
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 639a364..ff2c7dd 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -424,6 +424,8 @@
   SetUpHandlingOfBootEvents(&process_monitor, boot_events_pipe,
                             boot_state_machine);
 
+  LaunchLogcatReceiverIfEnabled(*config, &process_monitor);
+
   LaunchUsbServerIfEnabled(*config, &process_monitor);
 
   LaunchIvServerIfEnabled(&process_monitor, *config);
diff --git a/host/commands/logcat_receiver/Android.bp b/host/commands/logcat_receiver/Android.bp
new file mode 100644
index 0000000..16f3bf9
--- /dev/null
+++ b/host/commands/logcat_receiver/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2019 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_binary_host {
+    name: "logcat_receiver",
+    srcs: [
+        "main.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcuttlefish_fs",
+        "liblog",
+        "libcuttlefish_utils",
+        "cuttlefish_auto_resources",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libgflags",
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/logcat_receiver/main.cpp b/host/commands/logcat_receiver/main.cpp
new file mode 100644
index 0000000..e9cbad2
--- /dev/null
+++ b/host/commands/logcat_receiver/main.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+DEFINE_int32(
+    server_fd, -1,
+    "File descriptor to an already created vsock server. If negative a new "
+    "server will be created at the port specified on the config file");
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto config = vsoc::CuttlefishConfig::Get();
+
+  auto path = config->logcat_path();
+  auto logcat_file =
+      cvd::SharedFD::Open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
+  CHECK(logcat_file->IsOpen())
+      << "Unable to open logcat file: " << logcat_file->StrError();
+
+  cvd::SharedFD server_fd;
+  if (FLAGS_server_fd < 0) {
+    unsigned int port = config->logcat_vsock_port();
+    server_fd = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+  } else {
+    server_fd = cvd::SharedFD::Dup(FLAGS_server_fd);
+    close(FLAGS_server_fd);
+  }
+
+  CHECK(server_fd->IsOpen()) << "Error creating or inheriting logcat server: "
+                             << server_fd->StrError();
+
+  // Server loop
+  while (true) {
+    auto conn = cvd::SharedFD::Accept(*server_fd);
+
+    while (true) {
+      char buff[1024];
+      auto read = conn->Read(buff, sizeof(buff));
+      if (read <= 0) {
+        // Close here to ensure the other side gets reset if it's still
+        // connected
+        conn->Close();
+        LOG(WARNING) << "Detected close from the other side";
+        break;
+      }
+      auto written = logcat_file->Write(buff, read);
+      CHECK(written == read)
+          << "Error writing to log file: " << logcat_file->StrError()
+          << ". This is unrecoverable.";
+    }
+  }
+  return 0;
+}
\ No newline at end of file
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index b77e03d..3940739 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -152,6 +152,10 @@
 const char* kDataPolicy = "data_policy";
 const char* kBlankDataImageMb = "blank_data_image_mb";
 const char* kBlankDataImageFmt = "blank_data_image_fmt";
+
+const char* kLogcatMode = "logcat_mode";
+const char* kLogcatVsockPort = "logcat_vsock_mode";
+const char* kLogcatReceiverBinary = "logcat_receiver_binary";
 }  // namespace
 
 namespace vsoc {
@@ -760,6 +764,31 @@
 }
 
 
+void CuttlefishConfig::set_logcat_mode(const std::string& mode) {
+  (*dictionary_)[kLogcatMode] = mode;
+}
+
+std::string CuttlefishConfig::logcat_mode() const {
+  return (*dictionary_)[kLogcatMode].asString();
+}
+
+void CuttlefishConfig::set_logcat_vsock_port(int port) {
+  (*dictionary_)[kLogcatVsockPort] = port;
+}
+
+int CuttlefishConfig::logcat_vsock_port() const {
+  return (*dictionary_)[kLogcatVsockPort].asInt();
+}
+
+void CuttlefishConfig::set_logcat_receiver_binary(const std::string& binary) {
+  SetPath(kLogcatReceiverBinary, binary);
+}
+
+std::string CuttlefishConfig::logcat_receiver_binary() const {
+  return (*dictionary_)[kLogcatReceiverBinary].asString();
+}
+
+
 bool CuttlefishConfig::enable_ivserver() const {
   return hardware_name() == "vsoc";
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 451f4a3..933cdca 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -175,6 +175,9 @@
   std::string logcat_path() const;
   void set_logcat_path(const std::string& logcat_path);
 
+  std::string logcat_receiver_binary() const;
+  void set_logcat_receiver_binary(const std::string& binary);
+
   std::string launcher_log_path() const;
   void set_launcher_log_path(const std::string& launcher_log_path);
 
@@ -293,6 +296,12 @@
   void set_blank_data_image_fmt(const std::string& blank_data_image_fmt);
   std::string blank_data_image_fmt() const;
 
+  void set_logcat_mode(const std::string& mode);
+  std::string logcat_mode() const;
+
+  void set_logcat_vsock_port(int port);
+  int logcat_vsock_port() const;
+
   bool enable_ivserver() const;
 
  private:
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index 166c4e7..45224cf 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -184,12 +184,17 @@
     -device "${kernel_console_serial},chardev=charserial0,id=serial0"
     -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
     -device "pci-serial,chardev=charserial1,id=serial1"
-    -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
-    -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
     -chardev "socket,path=${ivshmem_qemu_socket_path:-${default_dir}/ivshmem_socket_qemu},id=ivsocket"
     -device "ivshmem-doorbell,chardev=ivsocket,vectors=${ivshmem_vector_count}"
 )
 
+if [[ "${logcat_mode}" == "serial" ]]; then
+    args+=(
+        -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
+        -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
+    )
+fi
+
 if [[ -n "${gdb_flag}" ]]; then
   args+=(-gdb "${gdb_flag}")
 fi
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 751c68c..33d9d44 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -87,6 +87,7 @@
                       std::to_string(config_->ivshmem_vector_count()));
   LogAndSetEnv("usb_v1_socket_name", config_->usb_v1_socket_name());
   LogAndSetEnv("vsock_guest_cid", std::to_string(config_->vsock_guest_cid()));
+  LogAndSetEnv("logcat_mode", config_->logcat_mode());
 
   cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"));
   return qemu_cmd;