use std::io;
use std::mem::size_of;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
use std::process::Child;
use windows_sys::Win32::System::JobObjects::{
AssignProcessToJobObject, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation,
SetInformationJobObject, TerminateJobObject,
};
#[derive(Debug)]
pub struct WindowsJob {
handle: OwnedHandle,
}
impl WindowsJob {
pub fn new_kill_on_close() -> io::Result<Self> {
let raw_handle = unsafe {
windows_sys::Win32::System::JobObjects::CreateJobObjectW(
std::ptr::null(),
std::ptr::null(),
)
};
if raw_handle.is_null() {
return Err(io::Error::last_os_error());
}
let handle = unsafe { OwnedHandle::from_raw_handle(raw_handle) };
let mut limits = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let information_size = u32::try_from(size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>())
.map_err(|_| io::Error::other("Windows Job Object information size overflowed"))?;
let configured = unsafe {
SetInformationJobObject(
handle.as_raw_handle(),
JobObjectExtendedLimitInformation,
std::ptr::addr_of!(limits).cast(),
information_size,
)
};
if configured == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self { handle })
}
pub fn assign_child(&self, child: &Child) -> io::Result<()> {
let assigned =
unsafe { AssignProcessToJobObject(self.handle.as_raw_handle(), child.as_raw_handle()) };
if assigned == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn terminate(&self, exit_code: u32) -> io::Result<()> {
let terminated = unsafe { TerminateJobObject(self.handle.as_raw_handle(), exit_code) };
if terminated == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}