Adds command to send logcat output over vsock

The command takes a port, connects to it on the host's cid and sends
the output from logcat. If the logcat process exits for some reason it
sends a message down the socket and restarts logcat.

Bug: 124535805
Test: run locally
Change-Id: Icd6e97ed480cc9a06135480b1d587c2aca9c6bd4
diff --git a/guest/commands/Android.bp b/guest/commands/Android.bp
index 8264fcc..5c7c3c2 100644
--- a/guest/commands/Android.bp
+++ b/guest/commands/Android.bp
@@ -14,4 +14,5 @@
 // limitations under the License.
 
 subdirs = [
+  "vsock_logcat",
 ]
diff --git a/guest/commands/vsock_logcat/Android.bp b/guest/commands/vsock_logcat/Android.bp
new file mode 100644
index 0000000..52575d7
--- /dev/null
+++ b/guest/commands/vsock_logcat/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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 {
+    name: "vsock_logcat",
+    srcs: [
+        "main.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "liblog",
+    ],
+    static_libs: [
+        "libgflags",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/vsock_logcat/main.cpp b/guest/commands/vsock_logcat/main.cpp
new file mode 100644
index 0000000..d93be80
--- /dev/null
+++ b/guest/commands/vsock_logcat/main.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "vsock_logcat"
+
+#include <sstream>
+
+#include <cutils/properties.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+
+DEFINE_uint32(port, property_get_int32("ro.boot.vsock_logcat_port", 0),
+              "VSOCK port to send logcat output to");
+DEFINE_uint32(cid, 2, "VSOCK CID to send logcat output to");
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  CHECK(FLAGS_port != 0) << "Port flag is required";
+
+  auto log_fd = cvd::SharedFD::VsockClient(FLAGS_cid, FLAGS_port, SOCK_STREAM);
+  if (!log_fd->IsOpen()) {
+    LOG(ERROR) << "Unable to connect to vsock:" << FLAGS_cid << ":"
+               << FLAGS_port << ": " << log_fd->StrError();
+    return 1;
+  }
+
+  cvd::Command logcat_cmd("/system/bin/logcat");
+  logcat_cmd.AddParameter("-b");
+  logcat_cmd.AddParameter("all");
+  logcat_cmd.AddParameter("-v");
+  logcat_cmd.AddParameter("threadtime");
+  logcat_cmd.AddParameter("*:V");
+
+  logcat_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, log_fd);
+
+  while (true) {
+    int wstatus;
+    logcat_cmd.Start().Wait(&wstatus, 0);
+    std::ostringstream exit_msg_builder;
+    exit_msg_builder << std::endl
+                     << "Logcat process exited with wstatus " << wstatus
+                     << ", restarting ..." << std::endl
+                     << std::endl;
+    auto exit_msg = exit_msg_builder.str();
+    size_t written = log_fd->Write(exit_msg.c_str(), exit_msg.size());
+    if (written != exit_msg.size()) {
+      LOG(ERROR) << "Unable to write complete message on socket: "
+                 << log_fd->StrError();
+      return 2;
+    }
+  }
+}