Allow multiple subscribers to boot events from kernel logs

Bug: 131184297
Bug: 131864854
Test: boot locally
Change-Id: If009906baca7aded07ac92b545be6c7fbdca0fb4
Merged-In: If009906baca7aded07ac92b545be6c7fbdca0fb4
diff --git a/host/commands/kernel_log_monitor/Android.bp b/host/commands/kernel_log_monitor/Android.bp
index a7197e5..7bc29e0 100644
--- a/host/commands/kernel_log_monitor/Android.bp
+++ b/host/commands/kernel_log_monitor/Android.bp
@@ -24,6 +24,7 @@
     ],
     shared_libs: [
         "libcuttlefish_fs",
+        "libcuttlefish_strings",
         "libcuttlefish_utils",
         "cuttlefish_auto_resources",
         "libbase",
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.h b/host/commands/kernel_log_monitor/kernel_log_server.h
index 80a6cfa..0cc8920 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.h
+++ b/host/commands/kernel_log_monitor/kernel_log_server.h
@@ -23,7 +23,6 @@
 
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/fs/shared_select.h"
-//#include "host/libs/monitor/kernel_log_client.h"
 
 namespace monitor {
 
diff --git a/host/commands/kernel_log_monitor/main.cc b/host/commands/kernel_log_monitor/main.cc
index f87f0ea..a17b1b8 100644
--- a/host/commands/kernel_log_monitor/main.cc
+++ b/host/commands/kernel_log_monitor/main.cc
@@ -15,12 +15,17 @@
  */
 
 #include <signal.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
 #include <common/libs/fs/shared_fd.h>
 #include <common/libs/fs/shared_select.h>
+#include <common/libs/strings/str_split.h>
 #include <host/libs/config/cuttlefish_config.h>
 #include "host/commands/kernel_log_monitor/kernel_log_server.h"
 
@@ -28,14 +33,38 @@
              "A file descriptor representing a (UNIX) socket from which to "
              "read the logs. If -1 is given the socket is created according to "
              "the instance configuration");
-DEFINE_int32(subscriber_fd, -1,
-             "A file descriptor (a pipe) to write boot events to. If -1 is "
-             "given no events will be sent");
+DEFINE_string(subscriber_fds, "",
+             "A comma separated list of file descriptors (most likely pipes) to"
+             " send boot events to.");
+
+std::vector<cvd::SharedFD> SubscribersFromCmdline() {
+  // Validate the parameter
+  std::string fd_list = FLAGS_subscriber_fds;
+  for (auto c: fd_list) {
+    if (c != ',' && (c < '0' || c > '9')) {
+      LOG(ERROR) << "Invalid file descriptor list: " << fd_list;
+      std::exit(1);
+    }
+  }
+
+  auto fds = cvd::StrSplit(FLAGS_subscriber_fds, ',');
+  std::vector<cvd::SharedFD> shared_fds;
+  for (auto& fd_str: fds) {
+    auto fd = std::stoi(fd_str);
+    auto shared_fd = cvd::SharedFD::Dup(fd);
+    close(fd);
+    shared_fds.push_back(shared_fd);
+  }
+
+  return shared_fds;
+}
 
 int main(int argc, char** argv) {
   ::android::base::InitLogging(argv, android::base::StderrLogger);
   google::ParseCommandLineFlags(&argc, &argv, true);
 
+  auto subscriber_fds = SubscribersFromCmdline();
+
   // Disable default handling of SIGPIPE
   struct sigaction new_action {
   }, old_action{};
@@ -66,24 +95,22 @@
   monitor::KernelLogServer klog{server, config->PerInstancePath("kernel.log"),
                                 config->deprecated_boot_completed()};
 
-  if (FLAGS_subscriber_fd >= 0) {
-    auto pipe_fd = cvd::SharedFD::Dup(FLAGS_subscriber_fd);
-    close(FLAGS_subscriber_fd);
-    if (pipe_fd->IsOpen()) {
-      klog.SubscribeToBootEvents([pipe_fd](monitor::BootEvent evt) {
-        int retval = pipe_fd->Write(&evt, sizeof(evt));
+  for (auto subscriber_fd: subscriber_fds) {
+    if (subscriber_fd->IsOpen()) {
+      klog.SubscribeToBootEvents([subscriber_fd](monitor::BootEvent evt) {
+        int retval = subscriber_fd->Write(&evt, sizeof(evt));
         if (retval < 0) {
-          if (pipe_fd->GetErrno() != EPIPE) {
+          if (subscriber_fd->GetErrno() != EPIPE) {
             LOG(ERROR) << "Error while writing to pipe: "
-                       << pipe_fd->StrError();
+                       << subscriber_fd->StrError();
           }
-          pipe_fd->Close();
+          subscriber_fd->Close();
           return monitor::SubscriptionAction::CancelSubscription;
         }
         return monitor::SubscriptionAction::ContinueSubscription;
       });
     } else {
-      LOG(ERROR) << "Subscriber fd isn't valid: " << pipe_fd->StrError();
+      LOG(ERROR) << "Subscriber fd isn't valid: " << subscriber_fd->StrError();
       // Don't return here, we still need to write the logs to a file
     }
   }
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
index dd5ab82..b4de6ae 100644
--- a/host/commands/launch/launch.cc
+++ b/host/commands/launch/launch.cc
@@ -147,7 +147,7 @@
       LOG(ERROR) << "Unable to create boot events pipe: " << strerror(errno);
       std::exit(LauncherExitCodes::kPipeIOError);
     }
-    kernel_log_monitor.AddParameter("-subscriber_fd=", pipe_write_end);
+    kernel_log_monitor.AddParameter("-subscriber_fds=", pipe_write_end);
   }
   return kernel_log_monitor;
 }