use crate::com::HandleMtaLease;
use crate::com_memory::ComAnsiString;
use crate::error::WslcError;
use crate::process::{ProcessStreams, StreamState, WslcProcessHandle, setup_streaming_channels};
use crate::session::WslcSessionHandle;
use std::ffi::{CStr, CString};
use std::sync::{Arc, Mutex};
use wslcsdk_sys::types::{
WSLC_CONTAINER_ID_BUFFER_SIZE, WslcContainer, WslcContainerStartFlags, WslcContainerState,
WslcDeleteContainerFlags, WslcProcess, WslcSignal,
};
use wslcsdk_sys::{
WslcDeleteContainer, WslcGetContainerID, WslcGetContainerInitProcess, WslcGetContainerState,
WslcInspectContainer, WslcOpenContainer, WslcReleaseContainer, WslcReleaseProcess,
WslcSetContainerInitProcessIOCallbacks, WslcStartContainer, WslcStopContainer,
};
#[derive(Debug)]
enum InitProcessSource {
Unfetched,
Borrowed(WslcProcess),
}
#[derive(Debug)]
struct ContainerInner {
raw: WslcContainer,
_session: WslcSessionHandle,
_mta: HandleMtaLease,
init_stream_state: Mutex<Option<Arc<StreamState>>>,
init_process_raw: Mutex<InitProcessSource>,
}
impl Drop for ContainerInner {
fn drop(&mut self) {
let mut slot = self
.init_process_raw
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let InitProcessSource::Borrowed(proc_raw) = *slot
&& !proc_raw.is_null()
{
unsafe {
let _ = WslcReleaseProcess(proc_raw);
}
}
*slot = InitProcessSource::Unfetched;
drop(slot);
if !self.raw.is_null() {
unsafe {
let _ = WslcReleaseContainer(self.raw);
}
self.raw = WslcContainer::NULL;
}
}
}
unsafe impl Send for ContainerInner {}
unsafe impl Sync for ContainerInner {}
#[derive(Clone, Debug)]
pub struct WslcContainerHandle {
inner: Arc<ContainerInner>,
}
impl WslcContainerHandle {
pub(crate) fn from_raw_inner(
raw: WslcContainer,
session: WslcSessionHandle,
init_stream_state: Option<Arc<StreamState>>,
) -> Result<Self, WslcError> {
let mta = match HandleMtaLease::acquire() {
Ok(lease) => lease,
Err(e) => {
unsafe {
let _ = WslcReleaseContainer(raw);
}
return Err(e);
}
};
Ok(Self {
inner: Arc::new(ContainerInner {
raw,
_session: session,
_mta: mta,
init_stream_state: Mutex::new(init_stream_state),
init_process_raw: Mutex::new(InitProcessSource::Unfetched),
}),
})
}
pub fn open(session: &WslcSessionHandle, name_or_id: &str) -> Result<Self, WslcError> {
let _com_guard = crate::com::try_initialize_mta()?;
let c_str = CString::new(name_or_id)
.map_err(|e| WslcError::NulError(format!("名称或 ID 包含非法空字节: {e}")))?;
let mut raw = WslcContainer::NULL;
let mut err_msg: *mut u16 = std::ptr::null_mut();
let hr =
unsafe { WslcOpenContainer(session.as_raw(), c_str.as_ptr(), &mut raw, &mut err_msg) };
unsafe {
WslcError::check(hr, err_msg)?;
}
if raw.is_null() {
return Err(WslcError::InvalidHandle);
}
Self::from_raw_inner(raw, session.clone(), None)
}
pub fn session(&self) -> WslcSessionHandle {
self.inner._session.clone()
}
pub fn as_raw(&self) -> WslcContainer {
self.inner.raw
}
pub fn id(&self) -> Result<String, WslcError> {
let mut buffer = [0i8; WSLC_CONTAINER_ID_BUFFER_SIZE];
let hr = unsafe { WslcGetContainerID(self.inner.raw, buffer.as_mut_ptr()) };
WslcError::check_hr(hr, "获取容器 ID 失败")?;
let c_str = unsafe { CStr::from_ptr(buffer.as_ptr()) };
c_str
.to_str()
.map(|s| s.to_string())
.map_err(|e| WslcError::Utf8Error(e.to_string()))
}
pub fn state(&self) -> Result<WslcContainerState, WslcError> {
let mut state = WslcContainerState::Invalid;
let hr = unsafe { WslcGetContainerState(self.inner.raw, &mut state) };
WslcError::check_hr(hr, "获取容器状态失败")?;
Ok(state)
}
pub fn start(&self, attach: bool) -> Result<(), WslcError> {
let flags: WslcContainerStartFlags = if attach {
wslcsdk_sys::types::WSLC_CONTAINER_START_FLAG_ATTACH
} else {
wslcsdk_sys::types::WSLC_CONTAINER_START_FLAG_NONE
};
let mut err_msg: *mut u16 = std::ptr::null_mut();
let hr = unsafe { WslcStartContainer(self.inner.raw, flags, &mut err_msg) };
unsafe { WslcError::check(hr, err_msg) }?;
log::info!("WSLC 容器启动成功,attach 模式: {attach}");
Ok(())
}
pub fn stop(&self, signal: WslcSignal, timeout_seconds: u32) -> Result<(), WslcError> {
let mut err_msg: *mut u16 = std::ptr::null_mut();
let hr =
unsafe { WslcStopContainer(self.inner.raw, signal, timeout_seconds, &mut err_msg) };
unsafe { WslcError::check(hr, err_msg) }?;
log::info!("WSLC 容器停止成功,信号: {signal:?},超时: {timeout_seconds} 秒");
Ok(())
}
pub fn delete(&self, force: bool) -> Result<(), WslcError> {
let flags: WslcDeleteContainerFlags = if force {
wslcsdk_sys::types::WSLC_DELETE_CONTAINER_FLAG_FORCE
} else {
wslcsdk_sys::types::WSLC_DELETE_CONTAINER_FLAG_NONE
};
let mut err_msg: *mut u16 = std::ptr::null_mut();
let hr = unsafe { WslcDeleteContainer(self.inner.raw, flags, &mut err_msg) };
unsafe { WslcError::check(hr, err_msg) }?;
log::info!("WSLC 容器删除成功,强制标志: {force}");
Ok(())
}
pub fn inspect(&self) -> Result<serde_json::Value, WslcError> {
let mut inspect_ptr: *mut i8 = std::ptr::null_mut();
let hr = unsafe { WslcInspectContainer(self.inner.raw, &mut inspect_ptr) };
if hr < 0 || inspect_ptr.is_null() {
return Err(WslcError::from_hresult(hr, "检查容器元数据失败"));
}
let json = unsafe { ComAnsiString::from_raw(inspect_ptr) }
.expect("前序判定已确保 hr 成功时指针非空");
let text = json.as_str().map_err(|e| {
WslcError::JsonError(format!(
"容器检查数据非合法 UTF-8(官方返回的字节序列含非法编码): {e}"
))
})?;
serde_json::from_str(text).map_err(|e| WslcError::JsonError(e.to_string()))
}
pub fn get_init_process(&self) -> Result<WslcProcessHandle, WslcError> {
let raw_process = {
let mut slot = self
.inner
.init_process_raw
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
match &*slot {
InitProcessSource::Borrowed(raw) => *raw,
InitProcessSource::Unfetched => {
let mut raw = WslcProcess::NULL;
let hr = unsafe { WslcGetContainerInitProcess(self.inner.raw, &mut raw) };
if hr < 0 || raw.is_null() {
return Err(WslcError::from_hresult(hr, "获取容器主进程句柄失败"));
}
*slot = InitProcessSource::Borrowed(raw);
raw
}
}
};
let stream_state = self
.inner
.init_stream_state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
Ok(WslcProcessHandle::from_borrowed(
raw_process,
self.clone(),
stream_state,
))
}
pub fn with_init_process_io_callbacks(
&self,
capacity: usize,
) -> Result<ProcessStreams, WslcError> {
if self.inner.raw.is_null() {
return Err(WslcError::InvalidHandle);
}
let (callbacks, stream_id, state, streams) = setup_streaming_channels(capacity);
let hr = unsafe {
WslcSetContainerInitProcessIOCallbacks(
self.inner.raw,
&callbacks,
stream_id as *mut core::ffi::c_void,
)
};
WslcError::check_hr(hr, "设置主进程 IO 回调失败")?;
*self
.inner
.init_stream_state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(state);
Ok(streams)
}
}