diff --git a/src/main.rs b/src/main.rs index 1c2eb7d86..798be76a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ extern crate event_monitor; use argh::FromArgs; use libc::EFD_NONBLOCK; -use log::LevelFilter; +use log::{warn, LevelFilter}; use option_parser::OptionParser; use seccompiler::SeccompAction; use signal_hook::consts::SIGSYS; @@ -33,6 +33,8 @@ enum Error { #[cfg(feature = "guest_debug")] #[error("Failed to create Debug EventFd: {0}")] CreateDebugEventFd(#[source] std::io::Error), + #[error("Failed to create exit EventFd: {0}")] + CreateExitEventFd(#[source] std::io::Error), #[error("Failed to open hypervisor interface (is hypervisor interface available?): {0}")] CreateHypervisor(#[source] hypervisor::HypervisorError), #[error("Failed to start the VMM thread: {0}")] @@ -509,6 +511,8 @@ fn start_vmm(toplevel: TopLevel) -> Result, Error> { #[cfg(feature = "guest_debug")] let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?; + let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?; + let vmm_thread = vmm::start_vmm_thread( vmm::VmmVersionInfo::new(env!("BUILD_VERSION"), env!("CARGO_PKG_VERSION")), &api_socket_path, @@ -522,33 +526,46 @@ fn start_vmm(toplevel: TopLevel) -> Result, Error> { debug_evt.try_clone().unwrap(), #[cfg(feature = "guest_debug")] vm_debug_evt.try_clone().unwrap(), + exit_evt.try_clone().unwrap(), &seccomp_action, hypervisor, ) .map_err(Error::StartVmmThread)?; - let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some(); + let r: Result<(), Error> = (|| { + let payload_present = toplevel.kernel.is_some() || toplevel.firmware.is_some(); - if payload_present { - let vm_params = toplevel.to_vm_params(); - let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?; + if payload_present { + let vm_params = toplevel.to_vm_params(); + let vm_config = config::VmConfig::parse(vm_params).map_err(Error::ParsingConfig)?; - // Create and boot the VM based off the VM config we just built. - let sender = api_request_sender.clone(); - vmm::api::vm_create( - api_evt.try_clone().unwrap(), - api_request_sender, - Arc::new(Mutex::new(vm_config)), - ) - .map_err(Error::VmCreate)?; - vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?; - } else if let Some(restore_params) = toplevel.restore { - vmm::api::vm_restore( - api_evt.try_clone().unwrap(), - api_request_sender, - Arc::new(config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?), - ) - .map_err(Error::VmRestore)?; + // Create and boot the VM based off the VM config we just built. + let sender = api_request_sender.clone(); + vmm::api::vm_create( + api_evt.try_clone().unwrap(), + api_request_sender, + Arc::new(Mutex::new(vm_config)), + ) + .map_err(Error::VmCreate)?; + vmm::api::vm_boot(api_evt.try_clone().unwrap(), sender).map_err(Error::VmBoot)?; + } else if let Some(restore_params) = toplevel.restore { + vmm::api::vm_restore( + api_evt.try_clone().unwrap(), + api_request_sender, + Arc::new( + config::RestoreConfig::parse(&restore_params).map_err(Error::ParsingRestore)?, + ), + ) + .map_err(Error::VmRestore)?; + } + + Ok(()) + })(); + + if r.is_err() { + if let Err(e) = exit_evt.write(1) { + warn!("writing to exit EventFd: {e}"); + } } vmm_thread diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 3337ae160..7d8f41aab 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -288,6 +288,7 @@ pub fn start_vmm_thread( #[cfg(feature = "guest_debug")] debug_path: Option, #[cfg(feature = "guest_debug")] debug_event: EventFd, #[cfg(feature = "guest_debug")] vm_debug_event: EventFd, + exit_event: EventFd, seccomp_action: &SeccompAction, hypervisor: Arc, ) -> Result>> { @@ -308,9 +309,8 @@ pub fn start_vmm_thread( .map_err(Error::CreateSeccompFilter)?; let vmm_seccomp_action = seccomp_action.clone(); - let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; let thread = { - let exit_evt = exit_evt.try_clone().map_err(Error::EventFdClone)?; + let exit_event = exit_event.try_clone().map_err(Error::EventFdClone)?; thread::Builder::new() .name("vmm".to_string()) .spawn(move || { @@ -328,7 +328,7 @@ pub fn start_vmm_thread( vm_debug_event, vmm_seccomp_action, hypervisor, - exit_evt, + exit_event, )?; vmm.setup_signal_handler()?; @@ -349,7 +349,7 @@ pub fn start_vmm_thread( http_api_event, api_sender, seccomp_action, - exit_evt, + exit_event, hypervisor_type, )?; } else if let Some(http_fd) = http_fd { @@ -358,7 +358,7 @@ pub fn start_vmm_thread( http_api_event, api_sender, seccomp_action, - exit_evt, + exit_event, hypervisor_type, )?; } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index dfb51ef2d..5723e4915 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -314,10 +314,10 @@ impl VmState { fn valid_transition(self, new_state: VmState) -> Result<()> { match self { VmState::Created => match new_state { - VmState::Created | VmState::Shutdown => { - Err(Error::InvalidStateTransition(self, new_state)) + VmState::Created => Err(Error::InvalidStateTransition(self, new_state)), + VmState::Running | VmState::Paused | VmState::BreakPoint | VmState::Shutdown => { + Ok(()) } - VmState::Running | VmState::Paused | VmState::BreakPoint => Ok(()), }, VmState::Running => match new_state { @@ -2694,7 +2694,7 @@ mod tests { // Check the transitions from Created assert!(state.valid_transition(VmState::Created).is_err()); assert!(state.valid_transition(VmState::Running).is_ok()); - assert!(state.valid_transition(VmState::Shutdown).is_err()); + assert!(state.valid_transition(VmState::Shutdown).is_ok()); assert!(state.valid_transition(VmState::Paused).is_ok()); assert!(state.valid_transition(VmState::BreakPoint).is_ok()); }