use super::stream::StreamState;
use crate::com::HandleMtaLease;
use crate::container::WslcContainerHandle;
use crate::error::WslcError;
use std::os::windows::raw::HANDLE;
use std::sync::Arc;
use windows_sys::Win32::Foundation::{WAIT_FAILED, WAIT_OBJECT_0, WAIT_TIMEOUT};
use windows_sys::Win32::System::Threading::WaitForSingleObject;
use wslcsdk_sys::types::{WslcProcess, WslcProcessIOHandle, WslcProcessState, WslcSignal};
use wslcsdk_sys::{
WslcGetProcessExitCode, WslcGetProcessExitEvent, WslcGetProcessIOHandle, WslcGetProcessPid,
WslcGetProcessState, WslcReleaseProcess, WslcSignalProcess,
};
#[derive(Debug)]
pub(crate) enum ProcessOwnership {
Owned {
container: WslcContainerHandle,
},
Borrowed {
container: WslcContainerHandle,
},
}
#[derive(Debug)]
pub(crate) struct ProcessInner {
pub(crate) raw: WslcProcess,
pub(crate) ownership: ProcessOwnership,
pub(crate) _stream_state: Option<Arc<StreamState>>,
_mta: HandleMtaLease,
}
impl Drop for ProcessInner {
fn drop(&mut self) {
if matches!(self.ownership, ProcessOwnership::Owned { .. }) && !self.raw.is_null() {
log::debug!("释放 WSLC 进程句柄");
unsafe {
let _ = WslcReleaseProcess(self.raw);
}
self.raw = WslcProcess::NULL;
}
}
}
unsafe impl Send for ProcessInner {}
unsafe impl Sync for ProcessInner {}
#[derive(Clone, Debug)]
pub struct WslcProcessHandle {
pub(crate) inner: Arc<ProcessInner>,
}
impl WslcProcessHandle {
pub unsafe fn from_raw(raw: WslcProcess, container: WslcContainerHandle) -> Self {
let mta = HandleMtaLease::acquire().unwrap_or_else(|e| {
log::error!("为外部进程句柄获取 MTA 租约失败,析构时可能崩溃: {e}");
HandleMtaLease::degraded()
});
Self::build(raw, ProcessOwnership::Owned { container }, None, mta)
}
pub(crate) fn try_from_raw_with_stream(
raw: WslcProcess,
container: WslcContainerHandle,
stream_state: Option<Arc<StreamState>>,
) -> Result<Self, WslcError> {
unsafe {
HandleMtaLease::wrap_raw(
raw,
|h| {
let _ = WslcReleaseProcess(h);
},
|raw, mta| {
Self::build(
raw,
ProcessOwnership::Owned { container },
stream_state,
mta,
)
},
)
}
}
pub(crate) fn try_from_borrowed(
raw: WslcProcess,
container: WslcContainerHandle,
stream_state: Option<Arc<StreamState>>,
) -> Result<Self, WslcError> {
let mta = HandleMtaLease::acquire()?;
Ok(Self::build(
raw,
ProcessOwnership::Borrowed { container },
stream_state,
mta,
))
}
fn build(
raw: WslcProcess,
ownership: ProcessOwnership,
stream_state: Option<Arc<StreamState>>,
mta: HandleMtaLease,
) -> Self {
Self {
inner: Arc::new(ProcessInner {
raw,
ownership,
_stream_state: stream_state,
_mta: mta,
}),
}
}
pub fn container(&self) -> WslcContainerHandle {
match &self.inner.ownership {
ProcessOwnership::Owned { container } | ProcessOwnership::Borrowed { container } => {
container.clone()
}
}
}
pub fn is_borrowed(&self) -> bool {
matches!(self.inner.ownership, ProcessOwnership::Borrowed { .. })
}
pub fn as_raw(&self) -> WslcProcess {
self.inner.raw
}
pub fn pid(&self) -> Result<u32, WslcError> {
let mut pid = 0u32;
let hr = unsafe { WslcGetProcessPid(self.inner.raw, &mut pid) };
WslcError::check_hr(hr, "获取进程 PID 失败")?;
Ok(pid)
}
pub fn exit_event(&self) -> Result<HANDLE, WslcError> {
let mut event: HANDLE = std::ptr::null_mut();
let hr = unsafe { WslcGetProcessExitEvent(self.inner.raw, &mut event) };
WslcError::check_hr(hr, "获取进程退出事件句柄失败")?;
Ok(event)
}
pub fn state(&self) -> Result<WslcProcessState, WslcError> {
let mut state = WslcProcessState::Unknown;
let hr = unsafe { WslcGetProcessState(self.inner.raw, &mut state) };
WslcError::check_hr(hr, "获取进程状态失败")?;
Ok(state)
}
pub fn exit_code(&self) -> Result<i32, WslcError> {
let mut code = 0i32;
let hr = unsafe { WslcGetProcessExitCode(self.inner.raw, &mut code) };
WslcError::check_hr(hr, "获取进程退出码失败")?;
Ok(code)
}
pub fn signal(&self, sig: WslcSignal) -> Result<(), WslcError> {
let hr = unsafe { WslcSignalProcess(self.inner.raw, sig) };
WslcError::check_hr(hr, "向进程发送信号失败")?;
log::debug!("成功向容器内进程发送 POSIX 信号: {:?}", sig);
Ok(())
}
pub fn io_handle(&self, io: WslcProcessIOHandle) -> Result<HANDLE, WslcError> {
if self.inner._stream_state.is_some() {
return Err(WslcError::InvalidConfiguration(format!(
"进程 {io:?} 已启用流式 IO 回调,官方规定此时无法再获取对应 IO 句柄;\
请改用流式通道消费输出,或在未注册回调的进程上调用本方法"
)));
}
let mut handle: HANDLE = std::ptr::null_mut();
let hr = unsafe { WslcGetProcessIOHandle(self.inner.raw, io, &mut handle) };
if hr < 0 {
Err(WslcError::from_hresult(hr, "获取进程标准流句柄失败"))
} else if handle.is_null() {
Err(WslcError::missing_output("WslcGetProcessIOHandle", hr))
} else {
Ok(handle)
}
}
pub fn wait(&self, timeout_ms: u32) -> Result<Option<i32>, WslcError> {
let event = self.exit_event()?;
let wait_res = unsafe { WaitForSingleObject(event, timeout_ms) };
match wait_res {
WAIT_OBJECT_0 => {
let code = self.exit_code()?;
Ok(Some(code))
}
WAIT_TIMEOUT => Ok(None),
WAIT_FAILED => Err(
unsafe { WslcError::last_win32_error("等待进程退出事件失败") },
),
other => Err(WslcError::Win32(
other,
format!("等待进程退出返回非预期状态: {other}"),
)),
}
}
pub async fn wait_async(&self, timeout_ms: u32) -> Result<Option<i32>, WslcError> {
if self.inner.raw.is_null() {
return Err(WslcError::InvalidHandle);
}
let process = self.clone();
let event = crate::async_ops::run_blocking("获取进程退出事件句柄", move || {
process.exit_event().map(|h| h as usize)
})
.await?;
let is_signaled =
crate::async_ops::wait_win32_event_async(event as HANDLE, timeout_ms).await?;
if !is_signaled {
return Ok(None);
}
let process = self.clone();
let code =
crate::async_ops::run_blocking("获取进程退出码", move || process.exit_code()).await?;
Ok(Some(code))
}
}