use std::os::windows::io::{AsRawHandle, RawHandle};
use std::process::Child;
type Handle = *mut core::ffi::c_void;
const JOB_OBJECT_EXTENDED_LIMIT_INFORMATION: i32 = 9;
const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: u32 = 0x0000_2000;
#[repr(C)]
#[derive(Default)]
struct BasicLimits {
per_process_user_time_limit: i64,
per_job_user_time_limit: i64,
limit_flags: u32,
minimum_working_set_size: usize,
maximum_working_set_size: usize,
active_process_limit: u32,
affinity: usize,
priority_class: u32,
scheduling_class: u32,
}
#[repr(C)]
#[derive(Default)]
struct IoCounters {
read_operation_count: u64,
write_operation_count: u64,
other_operation_count: u64,
read_transfer_count: u64,
write_transfer_count: u64,
other_transfer_count: u64,
}
#[repr(C)]
#[derive(Default)]
struct ExtendedLimits {
basic: BasicLimits,
io: IoCounters,
process_memory_limit: usize,
job_memory_limit: usize,
peak_process_memory_used: usize,
peak_job_memory_used: usize,
}
extern "system" {
fn CreateJobObjectW(attributes: *mut core::ffi::c_void, name: *const u16) -> Handle;
fn SetInformationJobObject(
job: Handle,
class: i32,
information: *const core::ffi::c_void,
length: u32,
) -> i32;
fn AssignProcessToJobObject(job: Handle, process: Handle) -> i32;
fn CloseHandle(handle: Handle) -> i32;
}
pub struct Leash(Handle);
impl Drop for Leash {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
pub fn tie_to_this_process(child: &Child) -> Option<Leash> {
unsafe {
let job = CreateJobObjectW(std::ptr::null_mut(), std::ptr::null());
if job.is_null() {
return None;
}
let mut limits = ExtendedLimits::default();
limits.basic.limit_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let set = SetInformationJobObject(
job,
JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
&limits as *const ExtendedLimits as *const core::ffi::c_void,
std::mem::size_of::<ExtendedLimits>() as u32,
);
let assigned = AssignProcessToJobObject(job, child.as_raw_handle() as RawHandle as Handle);
if set == 0 || assigned == 0 {
CloseHandle(job);
return None;
}
Some(Leash(job))
}
}