#![allow(unsafe_code)]
use std::io;
use std::mem::{size_of, zeroed};
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
use std::os::windows::process::CommandExt;
use std::process::{Child, Command};
use windows_sys::Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
SetInformationJobObject, TerminateJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
};
use windows_sys::Win32::System::Threading::CREATE_NEW_PROCESS_GROUP;
pub(super) struct Scope(OwnedHandle);
impl Scope {
pub(super) fn new() -> io::Result<Self> {
let raw = unsafe { CreateJobObjectW(std::ptr::null(), std::ptr::null()) };
if raw.is_null() {
return Err(io::Error::last_os_error());
}
let job = unsafe { OwnedHandle::from_raw_handle(raw) };
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { zeroed() };
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let result = unsafe {
SetInformationJobObject(
job.as_raw_handle(),
JobObjectExtendedLimitInformation,
std::ptr::addr_of!(info).cast(),
u32::try_from(size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>())
.map_err(io::Error::other)?,
)
};
if result == 0 {
return Err(io::Error::last_os_error());
}
Ok(Self(job))
}
pub(super) fn configure(&self, command: &mut Command) {
command.creation_flags(CREATE_NEW_PROCESS_GROUP);
}
pub(super) fn attach(&mut self, child: &Child) -> io::Result<()> {
let result =
unsafe { AssignProcessToJobObject(self.0.as_raw_handle(), child.as_raw_handle()) };
if result == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub(super) fn terminate(&self, _child: &Child) -> io::Result<()> {
if unsafe { TerminateJobObject(self.0.as_raw_handle(), 1) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}