// Copyright 2018 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. /// Runs a [9P] server. /// /// [9P]: http://man.cat-v.org/plan_9/5/0intro extern crate getopts; extern crate libc; extern crate p9; #[macro_use] extern crate sys_util; mod vsock; use std::fmt; use std::io::{self, BufReader, BufWriter}; use std::net; use std::num::ParseIntError; use std::os::raw::c_uint; use std::result; use std::str::FromStr; use std::string; use std::sync::Arc; use std::thread; use sys_util::syslog; use vsock::*; const DEFAULT_BUFFER_SIZE: usize = 8192; // Address family identifiers. const VSOCK: &'static str = "vsock:"; const UNIX: &'static str = "unix:"; // Usage for this program. const USAGE: &'static str = "9s [options] {vsock:|unix:|:}"; enum ListenAddress { Net(net::SocketAddr), Unix(String), Vsock(c_uint), } #[derive(Debug)] enum ParseAddressError { MissingUnixPath, MissingVsockPort, Net(net::AddrParseError), Unix(string::ParseError), Vsock(ParseIntError), } impl fmt::Display for ParseAddressError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &ParseAddressError::MissingUnixPath => write!(f, "missing unix path"), &ParseAddressError::MissingVsockPort => write!(f, "missing vsock port number"), &ParseAddressError::Net(ref e) => e.fmt(f), &ParseAddressError::Unix(ref e) => write!(f, "invalid unix path: {}", e), &ParseAddressError::Vsock(ref e) => write!(f, "invalid vsock port number: {}", e), } } } impl FromStr for ListenAddress { type Err = ParseAddressError; fn from_str(s: &str) -> result::Result { if s.starts_with(VSOCK) { if s.len() > VSOCK.len() { Ok(ListenAddress::Vsock( s[VSOCK.len()..].parse().map_err(ParseAddressError::Vsock)?, )) } else { Err(ParseAddressError::MissingVsockPort) } } else if s.starts_with(UNIX) { if s.len() > UNIX.len() { Ok(ListenAddress::Unix( s[UNIX.len()..].parse().map_err(ParseAddressError::Unix)?, )) } else { Err(ParseAddressError::MissingUnixPath) } } else { Ok(ListenAddress::Net( s.parse().map_err(ParseAddressError::Net)?, )) } } } #[derive(Debug)] enum Error { Address(ParseAddressError), Argument(getopts::Fail), Cid(ParseIntError), IO(io::Error), MissingAcceptCid, Syslog(syslog::Error), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Error::Address(ref e) => e.fmt(f), &Error::Argument(ref e) => e.fmt(f), &Error::Cid(ref e) => write!(f, "invalid cid value: {}", e), &Error::IO(ref e) => e.fmt(f), &Error::MissingAcceptCid => write!(f, "`accept_cid` is required for vsock servers"), &Error::Syslog(ref e) => write!(f, "failed to initialize syslog: {:?}", e), } } } type Result = result::Result; fn handle_client( root: Arc, mut reader: R, mut writer: W, ) -> io::Result<()> { let mut server = p9::Server::new(&*root); loop { server.handle_message(&mut reader, &mut writer)?; } } fn run_vsock_server(root: Arc, port: c_uint, accept_cid: c_uint) -> io::Result<()> { let listener = VsockListener::bind(port)?; loop { let (stream, peer) = listener.accept()?; if accept_cid != peer.cid { warn!("ignoring connection from {}:{}", peer.cid, peer.port); continue; } info!("accepted connection from {}:{}", peer.cid, peer.port); let reader = BufReader::with_capacity(DEFAULT_BUFFER_SIZE, stream.try_clone()?); let writer = BufWriter::with_capacity(DEFAULT_BUFFER_SIZE, stream); let server_root = root.clone(); thread::spawn(move || { if let Err(e) = handle_client(server_root, reader, writer) { error!( "error while handling client {}:{}: {}", peer.cid, peer.port, e ); } }); } } fn main() -> Result<()> { let mut opts = getopts::Options::new(); opts.optopt( "", "accept_cid", "only accept connections from this vsock context id", "CID", ); opts.optopt( "r", "root", "root directory for clients (default is \"/\")", "PATH", ); opts.optflag("h", "help", "print this help menu"); let matches = opts .parse(std::env::args_os().skip(1)) .map_err(Error::Argument)?; if matches.opt_present("h") || matches.free.len() == 0 { print!("{}", opts.usage(USAGE)); return Ok(()); } syslog::init().map_err(Error::Syslog)?; let root: Arc = Arc::from(matches.opt_str("r").unwrap_or_else(|| "/".into())); // We already checked that |matches.free| has at least one item. match matches.free[0] .parse::() .map_err(Error::Address)? { ListenAddress::Vsock(port) => { let accept_cid = if let Some(cid) = matches.opt_str("accept_cid") { cid.parse::().map_err(Error::Cid) } else { Err(Error::MissingAcceptCid) }?; run_vsock_server(root, port, accept_cid).map_err(Error::IO)?; } ListenAddress::Net(_) => { error!("Network server unimplemented"); } ListenAddress::Unix(_) => { error!("Unix server unimplemented"); } } Ok(()) }