#![allow(clippy::missing_errors_doc)]
use windows::core::{HRESULT, HSTRING};
use windows::Win32::System::HostComputeSystem::{
HcsCreateComputeSystem, HcsGetComputeSystemProperties, HcsModifyComputeSystem,
HcsOpenComputeSystem, HcsPauseComputeSystem, HcsResumeComputeSystem, HcsSaveComputeSystem,
HcsShutDownComputeSystem, HcsStartComputeSystem, HcsTerminateComputeSystem, HCS_SYSTEM,
};
use crate::error::{HcsError, HcsResult};
use crate::handle::{OwnedSystem, SendHandle};
use crate::operation::run_operation;
const HR_OK: HRESULT = HRESULT(0);
#[inline]
fn to_hresult(result: windows::core::Result<()>) -> HRESULT {
match result {
Ok(()) => HR_OK,
Err(e) => e.code(),
}
}
#[derive(Debug)]
pub struct ComputeSystem {
inner: OwnedSystem,
}
impl ComputeSystem {
pub async fn create(id: &str, configuration_json: &str) -> HcsResult<Self> {
let id_w = HSTRING::from(id);
let cfg_w = HSTRING::from(configuration_json);
let mut handle_slot: Option<SendHandle<HCS_SYSTEM>> = None;
let result = {
let handle_slot = &mut handle_slot;
run_operation(move |op| {
let res = unsafe { HcsCreateComputeSystem(&id_w, &cfg_w, op, None) };
match res {
Ok(sys) => {
*handle_slot = Some(SendHandle(sys));
HR_OK
}
Err(e) => e.code(),
}
})
.await
};
let _json = result?;
let Some(raw) = handle_slot else {
return Err(HcsError::Other {
hresult: 0,
message: "HcsCreateComputeSystem returned success without a handle".to_string(),
});
};
if raw.0.is_invalid() {
return Err(HcsError::Other {
hresult: 0,
message: "HcsCreateComputeSystem returned an invalid handle".to_string(),
});
}
let inner = unsafe { OwnedSystem::from_raw(raw.0) };
Ok(Self { inner })
}
pub fn open(id: &str, requested_access: u32) -> HcsResult<Self> {
let id_w = HSTRING::from(id);
let raw = unsafe { HcsOpenComputeSystem(&id_w, requested_access) }
.map_err(|e| HcsError::from_hresult(e.code(), format!("HcsOpenComputeSystem({id})")))?;
if raw.is_invalid() {
return Err(HcsError::Other {
hresult: 0,
message: "HcsOpenComputeSystem returned an invalid handle".to_string(),
});
}
let inner = unsafe { OwnedSystem::from_raw(raw) };
Ok(Self { inner })
}
#[must_use]
pub fn raw(&self) -> SendHandle<HCS_SYSTEM> {
self.inner.as_raw()
}
pub async fn start(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsStartComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn shutdown(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsShutDownComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn terminate(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsTerminateComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn pause(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsPauseComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn resume(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsResumeComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn save(&self, options_json: &str) -> HcsResult<()> {
let opts_w = HSTRING::from(options_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsSaveComputeSystem(*handle, op, &opts_w) })
})
.await?;
Ok(())
}
pub async fn properties(&self, property_query_json: &str) -> HcsResult<String> {
let query_w = HSTRING::from(property_query_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsGetComputeSystemProperties(*handle, op, &query_w) })
})
.await
}
pub async fn modify(&self, request: &crate::schema::ModifySettingRequest) -> HcsResult<()> {
let json = serde_json::to_string(request)?;
self.modify_raw_json(&json).await
}
pub async fn modify_raw_json(&self, modification_json: &str) -> HcsResult<()> {
let mod_w = HSTRING::from(modification_json);
let handle = self.inner.as_raw();
run_operation(move |op| {
to_hresult(unsafe { HcsModifyComputeSystem(*handle, op, &mod_w, None) })
})
.await?;
Ok(())
}
pub async fn add_vsmb(&self, share: &crate::schema::VirtualSmbShare) -> HcsResult<()> {
let req = crate::schema::ModifySettingRequest {
resource_path: "VirtualMachine/Devices/VirtualSmb/Shares".to_string(),
request_type: crate::schema::ModifyRequestType::Add,
settings: Some(serde_json::to_value(share)?),
guest_request: None,
};
self.modify(&req).await
}
pub async fn add_scsi(
&self,
controller_guid: &str,
lun: usize,
attachment: &crate::schema::ScsiAttachment,
) -> HcsResult<()> {
let req = crate::schema::ModifySettingRequest {
resource_path: format!(
"VirtualMachine/Devices/Scsi/{controller_guid}/Attachments/{lun}"
),
request_type: crate::schema::ModifyRequestType::Add,
settings: Some(serde_json::to_value(attachment)?),
guest_request: None,
};
self.modify(&req).await
}
}