use std::io;
pub fn acquire_subreaper() -> io::Result<()> {
imp::acquire()
}
pub fn release_subreaper() -> io::Result<()> {
imp::release()
}
pub fn is_subreaper() -> io::Result<bool> {
imp::is_acquired()
}
#[cfg(target_os = "linux")]
mod imp {
use std::io;
pub(super) fn acquire() -> io::Result<()> {
set(true)
}
pub(super) fn release() -> io::Result<()> {
set(false)
}
pub(super) fn is_acquired() -> io::Result<bool> {
let mut acquired = 0_i32;
let result = unsafe {
libc::prctl(
libc::PR_GET_CHILD_SUBREAPER,
&raw mut acquired,
0 as libc::c_ulong,
0 as libc::c_ulong,
0 as libc::c_ulong,
)
};
if result == -1 {
Err(io::Error::last_os_error())
} else {
Ok(acquired != 0)
}
}
fn set(acquired: bool) -> io::Result<()> {
let value: libc::c_ulong = acquired.into();
let result = unsafe {
libc::prctl(
libc::PR_SET_CHILD_SUBREAPER,
value,
0 as libc::c_ulong,
0 as libc::c_ulong,
0 as libc::c_ulong,
)
};
if result == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
#[cfg(target_os = "freebsd")]
mod imp {
use std::{io, ptr};
const REAPER_STATUS_OWNED: libc::c_uint = 1;
const REAPER_STATUS_REALINIT: libc::c_uint = 2;
#[repr(C)]
struct ReaperStatus {
flags: libc::c_uint,
children: libc::c_uint,
descendants: libc::c_uint,
reaper: libc::pid_t,
pid: libc::pid_t,
padding: [libc::c_uint; 15],
}
impl ReaperStatus {
const fn zeroed() -> Self {
Self {
flags: 0,
children: 0,
descendants: 0,
reaper: 0,
pid: 0,
padding: [0; 15],
}
}
}
pub(super) fn acquire() -> io::Result<()> {
match command(libc::PROC_REAP_ACQUIRE, ptr::null_mut()) {
Ok(()) => Ok(()),
Err(error) if error.raw_os_error() == Some(libc::EBUSY) => {
if reaper_flags()? & REAPER_STATUS_OWNED != 0 {
Ok(())
} else {
Err(error)
}
}
Err(error) => Err(error),
}
}
pub(super) fn release() -> io::Result<()> {
if !holds_acquired_role(reaper_flags()?) {
return Ok(());
}
match command(libc::PROC_REAP_RELEASE, ptr::null_mut()) {
Ok(()) => Ok(()),
Err(error) => match reaper_flags() {
Ok(flags) if !holds_acquired_role(flags) => Ok(()),
_ => Err(error),
},
}
}
pub(super) fn is_acquired() -> io::Result<bool> {
Ok(holds_acquired_role(reaper_flags()?))
}
const fn holds_acquired_role(flags: libc::c_uint) -> bool {
flags & REAPER_STATUS_OWNED != 0 && flags & REAPER_STATUS_REALINIT == 0
}
fn reaper_flags() -> io::Result<libc::c_uint> {
let mut status = ReaperStatus::zeroed();
command(
libc::PROC_REAP_STATUS,
(&raw mut status).cast::<libc::c_void>(),
)?;
Ok(status.flags)
}
fn command(request: libc::c_int, data: *mut libc::c_void) -> io::Result<()> {
let result = unsafe { libc::procctl(libc::P_PID, 0, request, data) };
if result == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
mod imp {
use std::io;
pub(super) fn acquire() -> io::Result<()> {
Err(unsupported())
}
pub(super) fn release() -> io::Result<()> {
Err(unsupported())
}
pub(super) fn is_acquired() -> io::Result<bool> {
Err(unsupported())
}
fn unsupported() -> io::Error {
io::Error::new(
io::ErrorKind::Unsupported,
"process subreaping is supported only on Linux and FreeBSD",
)
}
}