use crate::device::MemoryBalloonDevice;
use crate::error::{VZError, VZResult};
use crate::socket::VirtioSocketDevice;
use std::ffi::c_void;
use tokio::sync::oneshot;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i64)]
pub enum VirtualMachineState {
Stopped = 0,
Running = 1,
Paused = 2,
Error = 3,
Starting = 4,
Pausing = 5,
Resuming = 6,
Stopping = 7,
Saving = 8,
Restoring = 9,
}
impl From<i64> for VirtualMachineState {
fn from(value: i64) -> Self {
match value {
0 => Self::Stopped,
1 => Self::Running,
2 => Self::Paused,
3 => Self::Error,
4 => Self::Starting,
5 => Self::Pausing,
6 => Self::Resuming,
7 => Self::Stopping,
8 => Self::Saving,
9 => Self::Restoring,
_ => Self::Error,
}
}
}
pub struct VirtualMachine {
vm_box: *mut c_void,
}
unsafe impl Send for VirtualMachine {}
unsafe impl Sync for VirtualMachine {}
pub(crate) unsafe extern "C" fn state_trampoline(ctx: *mut c_void, err: *mut std::ffi::c_char) {
unsafe {
let sender = Box::from_raw(ctx.cast::<oneshot::Sender<Result<(), String>>>());
let result = if err.is_null() {
Ok(())
} else {
Err(crate::shim_ffi::take_error_string(err))
};
let _ = sender.send(result);
}
}
impl VirtualMachine {
pub(crate) fn from_box(vm_box: *mut c_void) -> Self {
Self { vm_box }
}
pub(crate) fn vm_box(&self) -> *mut c_void {
self.vm_box
}
pub fn state(&self) -> VirtualMachineState {
VirtualMachineState::from(unsafe { crate::shim_ffi::abx_vm_state(self.vm_box) })
}
pub fn can_stop(&self) -> bool {
unsafe { crate::shim_ffi::abx_vm_can_stop(self.vm_box) }
}
fn can_pause(&self) -> bool {
unsafe { crate::shim_ffi::abx_vm_can_pause(self.vm_box) }
}
fn can_resume(&self) -> bool {
unsafe { crate::shim_ffi::abx_vm_can_resume(self.vm_box) }
}
pub async fn start(&self) -> VZResult<()> {
let (tx, rx) = oneshot::channel::<Result<(), String>>();
let ctx: *mut c_void = Box::into_raw(Box::new(tx)).cast();
unsafe {
crate::shim_ffi::abx_vm_start(self.vm_box, ctx, state_trampoline);
}
let result = rx.await.map_err(|_| VZError::Internal {
code: -1,
message: "Start operation cancelled".into(),
})?;
result.map_err(|msg| VZError::Internal {
code: -1,
message: format!("VM start failed: {msg}"),
})
}
pub async fn stop(&self) -> VZResult<()> {
if !self.can_stop() {
return Err(VZError::InvalidState {
expected: "can_stop=true".into(),
actual: format!("state={:?}", self.state()),
});
}
let (tx, rx) = oneshot::channel::<Result<(), String>>();
let ctx: *mut c_void = Box::into_raw(Box::new(tx)).cast();
unsafe {
crate::shim_ffi::abx_vm_stop(self.vm_box, ctx, state_trampoline);
}
let result = rx.await.map_err(|_| VZError::Internal {
code: -1,
message: "Stop operation cancelled".into(),
})?;
result.map_err(|msg| VZError::OperationFailed(format!("VM stop failed: {msg}")))
}
pub async fn pause(&self) -> VZResult<()> {
if !self.can_pause() {
return Err(VZError::InvalidState {
expected: "can_pause=true".into(),
actual: format!("state={:?}", self.state()),
});
}
let (tx, rx) = oneshot::channel::<Result<(), String>>();
let ctx: *mut c_void = Box::into_raw(Box::new(tx)).cast();
unsafe {
crate::shim_ffi::abx_vm_pause(self.vm_box, ctx, state_trampoline);
}
let result = rx.await.map_err(|_| VZError::Internal {
code: -1,
message: "Pause operation cancelled".into(),
})?;
result.map_err(|msg| VZError::OperationFailed(format!("VM pause failed: {msg}")))
}
pub async fn resume(&self) -> VZResult<()> {
if !self.can_resume() {
return Err(VZError::InvalidState {
expected: "can_resume=true".into(),
actual: format!("state={:?}", self.state()),
});
}
let (tx, rx) = oneshot::channel::<Result<(), String>>();
let ctx: *mut c_void = Box::into_raw(Box::new(tx)).cast();
unsafe {
crate::shim_ffi::abx_vm_resume(self.vm_box, ctx, state_trampoline);
}
let result = rx.await.map_err(|_| VZError::Internal {
code: -1,
message: "Resume operation cancelled".into(),
})?;
result.map_err(|msg| VZError::OperationFailed(format!("VM resume failed: {msg}")))
}
pub fn request_stop(&self) -> VZResult<()> {
unsafe {
let mut error: *mut std::ffi::c_char = std::ptr::null_mut();
if crate::shim_ffi::abx_vm_request_stop(self.vm_box, &raw mut error) {
Ok(())
} else {
Err(VZError::OperationFailed(
crate::shim_ffi::take_error_string(error),
))
}
}
}
pub fn socket_devices(&self) -> Vec<VirtioSocketDevice> {
unsafe {
let count = crate::shim_ffi::abx_vm_socket_device_count(self.vm_box);
(0..count)
.filter_map(|i| {
let device_box = crate::shim_ffi::abx_vm_socket_device_at(self.vm_box, i);
(!device_box.is_null()).then(|| VirtioSocketDevice::from_box(device_box))
})
.collect()
}
}
#[must_use]
pub fn memory_balloon_devices(&self) -> Vec<MemoryBalloonDevice> {
unsafe {
let count = crate::shim_ffi::abx_vm_balloon_count(self.vm_box);
(0..count)
.filter_map(|i| {
let balloon_box = crate::shim_ffi::abx_vm_balloon_at(self.vm_box, i);
(!balloon_box.is_null()).then(|| MemoryBalloonDevice::from_box(balloon_box))
})
.collect()
}
}
#[must_use]
pub fn first_balloon_device(&self) -> Option<MemoryBalloonDevice> {
self.memory_balloon_devices().into_iter().next()
}
}
impl Drop for VirtualMachine {
fn drop(&mut self) {
if !self.vm_box.is_null() {
unsafe { crate::shim_ffi::abx_object_release(self.vm_box) };
}
}
}