sirenia: Use EventLoop for syslog socket multiplexing.

BUG=b:168933287
TEST=trichechus runs in the manatee initramfs

Change-Id: Ibdcbbc9f7f0e16fb6f4df778cf22669be60e0d7f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2453631
Commit-Queue: Allen Webb <allenwebb@google.com>
Tested-by: Allen Webb <allenwebb@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
diff --git a/sirenia/src/linux/mod.rs b/sirenia/src/linux/mod.rs
index d797629..5fce497 100644
--- a/sirenia/src/linux/mod.rs
+++ b/sirenia/src/linux/mod.rs
@@ -5,3 +5,4 @@
 //! A module for Linux specific functionality like epoll and syslog handling.
 
 pub mod events;
+pub mod syslog;
diff --git a/sirenia/src/linux/syslog.rs b/sirenia/src/linux/syslog.rs
new file mode 100644
index 0000000..bf06e36
--- /dev/null
+++ b/sirenia/src/linux/syslog.rs
@@ -0,0 +1,86 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A syslog server interface for use with EventMultiplexer.
+
+use std::boxed::Box;
+use std::fs::remove_file;
+use std::io::{Error as IoError, Read};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::net::{UnixListener, UnixStream};
+use std::path::Path;
+
+use sys_util::{self, error, handle_eintr};
+
+use super::events::{AddEventSourceMutator, EventSource, Mutator, RemoveFdMutator};
+
+pub const SYSLOG_PATH: &str = "/dev/log";
+
+/// Encapsulates a connection with a a syslog client.
+struct SyslogClient(UnixStream);
+
+impl AsRawFd for SyslogClient {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+/// This currently just makes sure the read buffer doesn't fill up, but will eventually need to be
+/// written out to have a handler for the data is that is read from the stream.
+impl EventSource for SyslogClient {
+    fn on_event(&mut self) -> Result<Option<Box<dyn Mutator>>, String> {
+        let mut buffer = [0; 1024];
+        Ok(if handle_eintr!(self.0.read(&mut buffer)).is_err() {
+            Some(Box::new(RemoveFdMutator(self.0.as_raw_fd())))
+        } else {
+            None
+        })
+    }
+}
+
+/// Encapsulates a unix socket listener for a syslog server that accepts client connections.
+pub struct Syslog(UnixListener);
+
+impl Syslog {
+    /// Return true if there is already a syslog socket open at SYSLOG_PATH.
+    pub fn is_syslog_present() -> bool {
+        Path::new(SYSLOG_PATH).exists()
+    }
+
+    /// Binds a new unix socket listener at the SYSLOG_PATH.
+    pub fn new() -> Result<Self, IoError> {
+        Ok(Syslog(UnixListener::bind(Path::new(SYSLOG_PATH))?))
+    }
+}
+
+/// Cleanup the unix socket by removing SYSLOG_PATH whenever the Syslog is dropped.
+impl Drop for Syslog {
+    fn drop(&mut self) {
+        if let Err(e) = remove_file(SYSLOG_PATH) {
+            eprintln!("Failed to cleanup syslog: {:?}", e);
+        }
+    }
+}
+
+impl AsRawFd for Syslog {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+/// Creates a EventSource that adds any accept connections and returns a Mutator that will add the
+/// client connection to the EventMultiplexer when applied.
+impl EventSource for Syslog {
+    fn on_event(&mut self) -> Result<Option<Box<dyn Mutator>>, String> {
+        Ok(Some(match handle_eintr!(self.0.accept()) {
+            Ok((instance, _)) => Box::new(AddEventSourceMutator(Some(Box::new(SyslogClient(
+                instance,
+            ))))),
+            Err(e) => {
+                error!("syslog socket error: {:?}", e);
+                Box::new(RemoveFdMutator(self.0.as_raw_fd()))
+            }
+        }))
+    }
+}
diff --git a/sirenia/src/trichechus.rs b/sirenia/src/trichechus.rs
index 6534159..5201f9e 100644
--- a/sirenia/src/trichechus.rs
+++ b/sirenia/src/trichechus.rs
@@ -6,10 +6,8 @@
 
 use std::env;
 use std::fmt::{self, Debug, Display};
-use std::fs::remove_file;
-use std::io::{BufRead, BufReader, Error as IoError, Read};
-use std::os::unix::io::{AsRawFd, RawFd};
-use std::os::unix::net::{UnixListener, UnixStream};
+use std::io::{BufRead, BufReader};
+use std::os::unix::io::AsRawFd;
 use std::path::Path;
 use std::result::Result as StdResult;
 use std::string::String;
@@ -18,13 +16,15 @@
 use sirenia::build_info::BUILD_TIMESTAMP;
 use sirenia::cli::initialize_common_arguments;
 use sirenia::communication::{self, get_app_path, read_message, write_message, Request, Response};
+use sirenia::linux::events::EventMultiplexer;
+use sirenia::linux::syslog::Syslog;
 use sirenia::sandbox::{self, Sandbox};
 use sirenia::to_sys_util;
 use sirenia::transport::{
     IPServerTransport, ReadDebugSend, ServerTransport, Transport, TransportType,
     VsockServerTransport, WriteDebugSend,
 };
-use sys_util::{self, error, handle_eintr, info, pipe, syslog};
+use sys_util::{self, error, info, pipe, syslog};
 
 #[derive(Debug)]
 pub enum Error {
@@ -76,54 +76,21 @@
     }
 }
 
-const SYSLOG_PATH: &str = "/dev/log";
-
-struct Syslog(UnixListener);
-
-impl Syslog {
-    fn new() -> StdResult<Self, IoError> {
-        Ok(Syslog(UnixListener::bind(Path::new(SYSLOG_PATH))?))
-    }
-}
-
-impl Drop for Syslog {
-    fn drop(&mut self) {
-        if let Err(e) = remove_file(SYSLOG_PATH) {
-            eprintln!("Failed to cleanup syslog: {:?}", e);
-        }
-    }
-}
-
-impl AsRawFd for Syslog {
-    fn as_raw_fd(&self) -> RawFd {
-        self.0.as_raw_fd()
-    }
-}
-
-fn handle_log_listener(listener: Syslog) {
-    while let Ok((instance, _)) = handle_eintr!(listener.0.accept()) {
-        spawn(move || {
-            handle_log_instance(instance);
-        });
-    }
-}
-
-fn handle_log_instance(mut instance: UnixStream) {
-    let mut buffer = [0; 1024];
-    while handle_eintr!(instance.read(&mut buffer)).is_ok() {}
-}
-
 // TODO: Figure out how to clean up TEEs that are no longer in use
 // TODO: Figure out rate limiting and prevention against DOS attacks
 // TODO: What happens if dugong crashes? How do we want to handle
 fn main() -> Result<()> {
-    let syslog_path = Path::new(SYSLOG_PATH);
-    if !syslog_path.exists() {
+    if !Syslog::is_syslog_present() {
         eprintln!("creating syslog");
         let listener = Syslog::new().unwrap();
-        eprintln!("reading syslog");
         spawn(move || {
-            handle_log_listener(listener);
+            let mut ctx = EventMultiplexer::new().unwrap();
+            ctx.add_event(Box::new(listener)).unwrap();
+            while !ctx.is_empty() {
+                if let Err(e) = ctx.run_once() {
+                    eprintln!("{}", e);
+                };
+            }
         });
     } else {
         eprintln!("syslog exists");