diff --git a/src/main.rs b/src/main.rs index d5e318639..42dffee56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ use signal_hook::{ }; use std::env; use std::fs::File; -use std::os::unix::io::FromRawFd; +use std::os::unix::io::{FromRawFd, RawFd}; use std::sync::mpsc::channel; use std::sync::{Arc, Mutex}; use std::thread; @@ -62,6 +62,8 @@ enum Error { ThreadJoin(std::boxed::Box), #[error("VMM thread exited with error: {0}")] VmmThread(#[source] vmm::Error), + #[error("Error parsing --api-socket: {0}")] + ParsingApiSocket(std::num::ParseIntError), #[error("Error parsing --event-monitor: {0}")] ParsingEventMonitor(option_parser::OptionParserError), #[error("Error parsing --event-monitor: path or fd required")] @@ -324,7 +326,7 @@ fn create_app<'a, 'b>( .arg( Arg::with_name("api-socket") .long("api-socket") - .help("HTTP API socket path (UNIX domain socket).") + .help("HTTP API socket (UNIX domain socket): path= or fd=.") .takes_value(true) .min_values(1) .group("vmm-config"), @@ -379,7 +381,7 @@ fn create_app<'a, 'b>( app } -fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &Option) -> Result<(), Error> { +fn start_vmm(cmd_arguments: ArgMatches) -> Result, Error> { let log_level = match cmd_arguments.occurrences_of("v") { 0 => LevelFilter::Warn, 1 => LevelFilter::Info, @@ -402,6 +404,29 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &Option) -> Res .map(|()| log::set_max_level(log_level)) .map_err(Error::LoggerSetup)?; + let (api_socket_path, api_socket_fd) = + if let Some(socket_config) = cmd_arguments.value_of("api-socket") { + let mut parser = OptionParser::new(); + parser.add("path").add("fd"); + parser.parse(socket_config).unwrap_or_default(); + + if let Some(fd) = parser.get("fd") { + ( + None, + Some(fd.parse::().map_err(Error::ParsingApiSocket)?), + ) + } else if let Some(path) = parser.get("path") { + (Some(path), None) + } else { + ( + cmd_arguments.value_of("api-socket").map(|s| s.to_string()), + None, + ) + } + } else { + (None, None) + }; + if let Some(monitor_config) = cmd_arguments.value_of("event-monitor") { let mut parser = OptionParser::new(); parser.add("path").add("fd"); @@ -474,7 +499,8 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &Option) -> Res let hypervisor = hypervisor::new().map_err(Error::CreateHypervisor)?; let vmm_thread = vmm::start_vmm_thread( env!("CARGO_PKG_VERSION").to_string(), - api_socket_path, + &api_socket_path, + api_socket_fd, api_evt.try_clone().unwrap(), http_sender, api_request_receiver, @@ -510,7 +536,9 @@ fn start_vmm(cmd_arguments: ArgMatches, api_socket_path: &Option) -> Res vmm_thread .join() .map_err(Error::ThreadJoin)? - .map_err(Error::VmmThread) + .map_err(Error::VmmThread)?; + + Ok(api_socket_path) } fn main() { @@ -519,16 +547,17 @@ fn main() { let (default_vcpus, default_memory, default_rng) = prepare_default_values(); let cmd_arguments = create_app(&default_vcpus, &default_memory, &default_rng).get_matches(); - let api_socket_path = cmd_arguments.value_of("api-socket").map(|s| s.to_string()); - - let exit_code = if let Err(e) = start_vmm(cmd_arguments, &api_socket_path) { - eprintln!("{}", e); - 1 - } else { - 0 + let exit_code = match start_vmm(cmd_arguments) { + Ok(path) => { + path.map(|s| std::fs::remove_file(s).ok()); + 0 + } + Err(e) => { + eprintln!("{}", e); + 1 + } }; - api_socket_path.map(|s| std::fs::remove_file(s).ok()); std::process::exit(exit_code); } diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index 66b69a28a..f3d637713 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -11,6 +11,7 @@ use micro_http::{Body, HttpServer, MediaType, Method, Request, Response, StatusC use seccomp::{SeccompAction, SeccompFilter}; use serde_json::Error as SerdeError; use std::collections::HashMap; +use std::os::unix::io::RawFd; use std::path::PathBuf; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -253,16 +254,12 @@ fn handle_http_request( response } -pub fn start_http_thread( - path: &str, +fn start_http_thread( + mut server: HttpServer, api_notifier: EventFd, api_sender: Sender, seccomp_action: &SeccompAction, ) -> Result>> { - std::fs::remove_file(path).unwrap_or_default(); - let socket_path = PathBuf::from(path); - let mut server = HttpServer::new(socket_path).map_err(Error::CreateApiServer)?; - // Retrieve seccomp filter for API thread let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::Api).map_err(Error::CreateSeccompFilter)?; @@ -299,3 +296,25 @@ pub fn start_http_thread( }) .map_err(Error::HttpThreadSpawn) } + +pub fn start_http_path_thread( + path: &str, + api_notifier: EventFd, + api_sender: Sender, + seccomp_action: &SeccompAction, +) -> Result>> { + std::fs::remove_file(path).unwrap_or_default(); + let socket_path = PathBuf::from(path); + let server = HttpServer::new(socket_path).map_err(Error::CreateApiServer)?; + start_http_thread(server, api_notifier, api_sender, seccomp_action) +} + +pub fn start_http_fd_thread( + fd: RawFd, + api_notifier: EventFd, + api_sender: Sender, + seccomp_action: &SeccompAction, +) -> Result>> { + let server = HttpServer::new_from_fd(fd).map_err(Error::CreateApiServer)?; + start_http_thread(server, api_notifier, api_sender, seccomp_action) +} diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 7fabf8c49..79dd26d19 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -31,7 +31,8 @@ extern crate vm_device; extern crate vmm_sys_util; -pub use self::http::start_http_thread; +pub use self::http::start_http_fd_thread; +pub use self::http::start_http_path_thread; pub mod http; pub mod http_endpoint; diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index fafab1ed0..8bf46ab72 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -247,9 +247,11 @@ impl Serialize for PciDeviceInfo { } } +#[allow(clippy::too_many_arguments)] pub fn start_vmm_thread( vmm_version: String, http_path: &Option, + http_fd: Option, api_event: EventFd, api_sender: Sender, api_receiver: Receiver, @@ -280,9 +282,11 @@ pub fn start_vmm_thread( }) .map_err(Error::VmmThreadSpawn)?; + // The VMM thread is started, we can start serving HTTP requests if let Some(http_path) = http_path { - // The VMM thread is started, we can start serving HTTP requests - api::start_http_thread(http_path, http_api_event, api_sender, seccomp_action)?; + api::start_http_path_thread(http_path, http_api_event, api_sender, seccomp_action)?; + } else if let Some(http_fd) = http_fd { + api::start_http_fd_thread(http_fd, http_api_event, api_sender, seccomp_action)?; } Ok(thread) }