blob: e68e97cbbb637b15b1c60d90bb73d91d93cbb6a8 [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
mod arguments;
mod http;
mod io_adapters;
mod listeners;
mod usb_connector;
mod util;
use std::fmt;
use std::io;
use std::net::TcpListener;
use std::os::raw::c_int;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::os::unix::net::UnixListener;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::time::Duration;
use libchromeos::deprecated::{EventFd, PollContext, PollToken};
use libchromeos::signal::register_signal_handler;
use libchromeos::syslog;
use log::{debug, error, info};
use nix::sys::signal::Signal;
use tiny_http::{ClientConnection, Stream};
use crate::arguments::Args;
use crate::http::handle_request;
use crate::listeners::{Accept, ScopedUnixListener};
use crate::usb_connector::{UnplugDetector, UsbConnector};
#[derive(Debug)]
pub enum Error {
CreateSocket(io::Error),
CreateUsbConnector(usb_connector::Error),
EventFd(io::Error),
ParseArgs(arguments::Error),
PollEvents(nix::Error),
RegisterHandler(nix::Error),
Syslog(syslog::Error),
SysUtil(nix::Error),
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
CreateSocket(err) => write!(f, "Failed to create socket: {}", err),
CreateUsbConnector(err) => write!(f, "Failed to create USB connector: {}", err),
EventFd(err) => write!(f, "Failed to create/duplicate EventFd: {}", err),
ParseArgs(err) => write!(f, "Failed to parse arguments: {}", err),
PollEvents(err) => write!(f, "Failed to poll for events: {}", err),
RegisterHandler(err) => write!(f, "Registering SIGINT handler failed: {}", err),
Syslog(err) => write!(f, "Failed to initalize syslog: {}", err),
SysUtil(err) => write!(f, "Sysutil error: {}", err),
}
}
}
type Result<T> = std::result::Result<T, Error>;
// Set to true if the program should terminate.
static SHUTDOWN: AtomicBool = AtomicBool::new(false);
// Holds a raw EventFD with 'static lifetime that can be used to wake up any
// polling threads.
static SHUTDOWN_FD: AtomicI32 = AtomicI32::new(-1);
extern "C" fn sigint_handler(_: c_int) {
// Check if we've already received one SIGINT. If we have, the program may be misbehaving and
// not terminating, so to be safe we'll forcefully exit.
if SHUTDOWN.load(Ordering::Relaxed) {
std::process::exit(1);
}
SHUTDOWN.store(true, Ordering::Relaxed);
let fd = SHUTDOWN_FD.load(Ordering::Relaxed);
if fd >= 0 {
let buf = &1u64 as *const u64 as *const libc::c_void;
let size = std::mem::size_of::<u64>();
unsafe { libc::write(fd, buf, size) };
}
}
/// Registers a SIGINT handler that, when triggered, will write to `shutdown_fd`
/// to notify any listeners of a pending shutdown.
fn add_sigint_handler(shutdown_fd: EventFd) -> nix::Result<()> {
// Leak our copy of the fd to ensure SHUTDOWN_FD remains valid until ippusb_bridge closes, so
// that we aren't inadvertently writing to an invalid FD in the SIGINT handler. The FD will be
// reclaimed by the OS once our process has stopped.
SHUTDOWN_FD.store(shutdown_fd.into_raw_fd(), Ordering::Relaxed);
// Safe because sigint_handler is an extern "C" function that only performs
// async signal-safe operations.
unsafe { register_signal_handler(Signal::SIGINT, sigint_handler) }
}
struct Daemon {
verbose_log: bool,
num_clients: usize,
shutdown: EventFd,
listener: Box<dyn Accept>,
usb: UsbConnector,
}
// Trivially allows a `RawFd` to be passed as a `&AsRawFd`. Needed because
// `Daemon` contains an `Accept` but needs to pass it to `PollContext` as a
// `&AsRawFd`.
struct WrapFd(RawFd);
impl AsRawFd for WrapFd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Daemon {
fn new(
verbose_log: bool,
shutdown: EventFd,
listener: Box<dyn Accept>,
usb: UsbConnector,
) -> Result<Self> {
Ok(Self {
verbose_log,
num_clients: 0,
shutdown,
listener,
usb,
})
}
fn run(&mut self) -> Result<()> {
#[derive(PollToken)]
enum Token {
Shutdown,
ClientConnection,
}
let listener_fd = WrapFd(self.listener.as_raw_fd());
let poll_ctx: PollContext<Token> = PollContext::build_with(&[
(&self.shutdown, Token::Shutdown),
(&listener_fd, Token::ClientConnection),
])
.map_err(Error::SysUtil)?;
'poll: loop {
let timeout = Duration::new(i64::MAX as u64, 0);
let events = poll_ctx.wait_timeout(timeout).map_err(Error::PollEvents)?;
for event in &events {
match event.token() {
Token::Shutdown => break 'poll,
Token::ClientConnection => match self.listener.accept() {
Ok(stream) => self.handle_connection(stream),
Err(err) => error!("Failed to accept connection: {}", err),
},
}
}
}
Ok(())
}
fn handle_connection(&mut self, stream: Stream) {
let connection = ClientConnection::new(stream);
let mut thread_usb = self.usb.clone();
let verbose = self.verbose_log;
self.num_clients += 1;
let client_num = self.num_clients;
std::thread::spawn(move || {
if verbose {
debug!("Connection {} opened", client_num);
}
let mut num_requests = 0;
for request in connection {
num_requests += 1;
let usb_conn = match thread_usb.get_connection() {
Ok(c) => c,
Err(e) => {
error!("Getting USB connection failed: {}", e);
continue;
}
};
if let Err(e) = handle_request(verbose, usb_conn, request) {
error!("Handling request failed: {}", e);
}
}
if verbose {
debug!(
"Connection {} handled {} requests",
client_num, num_requests
);
}
});
}
}
fn run() -> Result<()> {
syslog::init("ippusb_bridge".to_string(), true).map_err(Error::Syslog)?;
let argv: Vec<String> = std::env::args().collect();
let args = match Args::parse(&argv).map_err(Error::ParseArgs)? {
None => return Ok(()),
Some(args) => args,
};
let shutdown_fd = EventFd::new().map_err(|e| Error::EventFd(e.into()))?;
let sigint_shutdown_fd = shutdown_fd.try_clone().map_err(Error::EventFd)?;
add_sigint_handler(sigint_shutdown_fd).map_err(Error::RegisterHandler)?;
// Safe because the syscall doesn't touch any memory and always succeeds.
unsafe { libc::umask(0o117) };
let listener: Box<dyn Accept> = if let Some(unix_socket_path) = args.unix_socket {
info!("Listening on {}", unix_socket_path.display());
Box::new(ScopedUnixListener(
UnixListener::bind(unix_socket_path).map_err(Error::CreateSocket)?,
))
} else {
let host = "127.0.0.1:60000";
info!("Listening on {}", host);
Box::new(TcpListener::bind(host).map_err(Error::CreateSocket)?)
};
let usb =
UsbConnector::new(args.verbose_log, args.bus_device).map_err(Error::CreateUsbConnector)?;
let unplug_shutdown_fd = shutdown_fd.try_clone().map_err(Error::EventFd)?;
let _unplug = UnplugDetector::new(
usb.device(),
unplug_shutdown_fd,
&SHUTDOWN,
args.upstart_mode,
);
let mut daemon = Daemon::new(args.verbose_log, shutdown_fd, listener, usb)?;
daemon.run()?;
info!("Shutting down.");
Ok(())
}
fn main() {
libchromeos::panic_handler::install_memfd_handler();
// Use run() instead of returning a Result from main() so that we can print
// errors using Display instead of Debug.
if let Err(e) = run() {
error!("{}", e);
}
}