#![allow(unsafe_code)]
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use core::mem::MaybeUninit;
use core::num::NonZeroI32;
use core::ptr;
use bitflags::bitflags;
use crate::backend::process::syscalls;
use crate::backend::process::types::RawId;
use crate::ffi::{c_int, c_uint, c_void};
use crate::io;
use crate::process::{Pid, RawPid};
use crate::signal::Signal;
use crate::utils::{as_mut_ptr, as_ptr};
#[repr(i32)]
pub enum IdType {
Pid = 0,
Pgid = 2,
}
pub type ProcSelector = Option<(IdType, Pid)>;
fn proc_selector_to_raw(selector: ProcSelector) -> (IdType, RawPid) {
match selector {
Some((idtype, id)) => (idtype, id.as_raw_nonzero().get()),
None => (IdType::Pid, 0),
}
}
#[inline]
pub(crate) unsafe fn procctl(
option: c_int,
process: ProcSelector,
data: *mut c_void,
) -> io::Result<()> {
let (idtype, id) = proc_selector_to_raw(process);
syscalls::procctl(idtype as c_uint, id as RawId, option, data)
}
#[inline]
pub(crate) unsafe fn procctl_set<P>(
option: c_int,
process: ProcSelector,
data: &P,
) -> io::Result<()> {
procctl(option, process, (as_ptr(data) as *mut P).cast())
}
#[inline]
pub(crate) unsafe fn procctl_get_optional<P>(
option: c_int,
process: ProcSelector,
) -> io::Result<P> {
let mut value: MaybeUninit<P> = MaybeUninit::uninit();
procctl(option, process, value.as_mut_ptr().cast())?;
Ok(value.assume_init())
}
const PROC_PDEATHSIG_STATUS: c_int = 12;
#[inline]
pub fn parent_process_death_signal() -> io::Result<Option<Signal>> {
let raw = unsafe { procctl_get_optional::<c_int>(PROC_PDEATHSIG_STATUS, None) }?;
if let Some(non_zero) = NonZeroI32::new(raw) {
Ok(Some(unsafe {
Signal::from_raw_nonzero_unchecked(non_zero)
}))
} else {
Ok(None)
}
}
const PROC_PDEATHSIG_CTL: c_int = 11;
#[inline]
pub fn set_parent_process_death_signal(signal: Option<Signal>) -> io::Result<()> {
let signal = signal.map_or(0, |signal| signal.as_raw());
unsafe { procctl_set::<c_int>(PROC_PDEATHSIG_CTL, None, &signal) }
}
const PROC_TRACE_CTL: c_int = 7;
const PROC_TRACE_CTL_ENABLE: i32 = 1;
const PROC_TRACE_CTL_DISABLE: i32 = 2;
const PROC_TRACE_CTL_DISABLE_EXEC: i32 = 3;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum DumpableBehavior {
NotDumpable = PROC_TRACE_CTL_DISABLE,
Dumpable = PROC_TRACE_CTL_ENABLE,
NotDumpableExecPreserved = PROC_TRACE_CTL_DISABLE_EXEC,
}
#[inline]
pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> {
unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) }
}
const PROC_TRACE_STATUS: c_int = 8;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TracingStatus {
NotTraceble,
Tracable,
BeingTraced(Pid),
}
#[inline]
pub fn trace_status(process: ProcSelector) -> io::Result<TracingStatus> {
let val = unsafe { procctl_get_optional::<c_int>(PROC_TRACE_STATUS, process) }?;
match val {
-1 => Ok(TracingStatus::NotTraceble),
0 => Ok(TracingStatus::Tracable),
pid => {
let pid = Pid::from_raw(pid as RawPid).ok_or(io::Errno::RANGE)?;
Ok(TracingStatus::BeingTraced(pid))
}
}
}
const PROC_REAP_ACQUIRE: c_int = 2;
const PROC_REAP_RELEASE: c_int = 3;
#[inline]
pub fn set_reaper_status(reaper: bool) -> io::Result<()> {
unsafe {
procctl(
if reaper {
PROC_REAP_ACQUIRE
} else {
PROC_REAP_RELEASE
},
None,
ptr::null_mut(),
)
}
}
const PROC_REAP_STATUS: c_int = 4;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ReaperStatusFlags: c_uint {
const OWNED = 1;
const REALINIT = 2;
const _ = !0;
}
}
#[repr(C)]
struct procctl_reaper_status {
rs_flags: c_uint,
rs_children: c_uint,
rs_descendants: c_uint,
rs_reaper: RawPid,
rs_pid: RawPid,
rs_pad0: [c_uint; 15],
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ReaperStatus {
pub flags: ReaperStatusFlags,
pub children: usize,
pub descendants: usize,
pub reaper: Pid,
pub pid: Option<Pid>,
}
#[inline]
pub fn get_reaper_status(process: ProcSelector) -> io::Result<ReaperStatus> {
let raw = unsafe { procctl_get_optional::<procctl_reaper_status>(PROC_REAP_STATUS, process) }?;
Ok(ReaperStatus {
flags: ReaperStatusFlags::from_bits_retain(raw.rs_flags),
children: raw.rs_children as _,
descendants: raw.rs_descendants as _,
reaper: Pid::from_raw(raw.rs_reaper).ok_or(io::Errno::RANGE)?,
pid: if raw.rs_pid == -1 {
None
} else {
Some(Pid::from_raw(raw.rs_pid).ok_or(io::Errno::RANGE)?)
},
})
}
#[cfg(feature = "alloc")]
const PROC_REAP_GETPIDS: c_int = 5;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct PidInfoFlags: c_uint {
const VALID = 1;
const CHILD = 2;
const REAPER = 4;
const ZOMBIE = 8;
const STOPPED = 16;
const EXITING = 32;
}
}
#[repr(C)]
#[derive(Default, Clone)]
struct procctl_reaper_pidinfo {
pi_pid: RawPid,
pi_subtree: RawPid,
pi_flags: c_uint,
pi_pad0: [c_uint; 15],
}
#[repr(C)]
struct procctl_reaper_pids {
rp_count: c_uint,
rp_pad0: [c_uint; 15],
rp_pids: *mut procctl_reaper_pidinfo,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PidInfo {
pub flags: PidInfoFlags,
pub pid: Pid,
pub subtree: Pid,
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn get_reaper_pids(process: ProcSelector) -> io::Result<Vec<PidInfo>> {
const PID_MAX: usize = 99999;
let mut pids: Vec<procctl_reaper_pidinfo> = vec![Default::default(); PID_MAX];
let mut pinfo = procctl_reaper_pids {
rp_count: PID_MAX as _,
rp_pad0: [0; 15],
rp_pids: pids.as_mut_slice().as_mut_ptr(),
};
unsafe { procctl(PROC_REAP_GETPIDS, process, as_mut_ptr(&mut pinfo).cast())? };
let mut result = Vec::new();
for raw in pids.into_iter() {
let flags = PidInfoFlags::from_bits_retain(raw.pi_flags);
if !flags.contains(PidInfoFlags::VALID) {
break;
}
result.push(PidInfo {
flags,
subtree: Pid::from_raw(raw.pi_subtree).ok_or(io::Errno::RANGE)?,
pid: Pid::from_raw(raw.pi_pid).ok_or(io::Errno::RANGE)?,
});
}
Ok(result)
}
const PROC_REAP_KILL: c_int = 6;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
struct KillFlags: c_uint {
const CHILDREN = 1;
const SUBTREE = 2;
}
}
#[repr(C)]
struct procctl_reaper_kill {
rk_sig: c_int,
rk_flags: c_uint,
rk_subtree: RawPid,
rk_killed: c_uint,
rk_fpid: RawPid,
rk_pad0: [c_uint; 15],
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct KillResult {
pub killed: usize,
pub first_failed: Option<Pid>,
}
pub fn reaper_kill(
process: ProcSelector,
signal: Signal,
direct_children: bool,
subtree: Option<Pid>,
) -> io::Result<KillResult> {
let mut flags = KillFlags::empty();
flags.set(KillFlags::CHILDREN, direct_children);
flags.set(KillFlags::SUBTREE, subtree.is_some());
let mut req = procctl_reaper_kill {
rk_sig: signal.as_raw(),
rk_flags: flags.bits(),
rk_subtree: subtree.map(|p| p.as_raw_nonzero().into()).unwrap_or(0),
rk_killed: 0,
rk_fpid: 0,
rk_pad0: [0; 15],
};
unsafe { procctl(PROC_REAP_KILL, process, as_mut_ptr(&mut req).cast())? };
Ok(KillResult {
killed: req.rk_killed as _,
first_failed: Pid::from_raw(req.rk_fpid),
})
}
const PROC_TRAPCAP_CTL: c_int = 9;
const PROC_TRAPCAP_CTL_ENABLE: i32 = 1;
const PROC_TRAPCAP_CTL_DISABLE: i32 = 2;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum TrapCapBehavior {
Disable = PROC_TRAPCAP_CTL_DISABLE,
Enable = PROC_TRAPCAP_CTL_ENABLE,
}
#[inline]
pub fn set_trap_cap_behavior(process: ProcSelector, config: TrapCapBehavior) -> io::Result<()> {
let config = config as c_int;
unsafe { procctl_set::<c_int>(PROC_TRAPCAP_CTL, process, &config) }
}
const PROC_TRAPCAP_STATUS: c_int = 10;
#[inline]
pub fn trap_cap_behavior(process: ProcSelector) -> io::Result<TrapCapBehavior> {
let val = unsafe { procctl_get_optional::<c_int>(PROC_TRAPCAP_STATUS, process) }?;
match val {
PROC_TRAPCAP_CTL_DISABLE => Ok(TrapCapBehavior::Disable),
PROC_TRAPCAP_CTL_ENABLE => Ok(TrapCapBehavior::Enable),
_ => Err(io::Errno::RANGE),
}
}
const PROC_NO_NEW_PRIVS_CTL: c_int = 19;
const PROC_NO_NEW_PRIVS_ENABLE: c_int = 1;
#[inline]
pub fn set_no_new_privs(process: ProcSelector) -> io::Result<()> {
unsafe { procctl_set::<c_int>(PROC_NO_NEW_PRIVS_CTL, process, &PROC_NO_NEW_PRIVS_ENABLE) }
}
const PROC_NO_NEW_PRIVS_STATUS: c_int = 20;
#[inline]
pub fn no_new_privs(process: ProcSelector) -> io::Result<bool> {
unsafe { procctl_get_optional::<c_int>(PROC_NO_NEW_PRIVS_STATUS, process) }
.map(|x| x == PROC_NO_NEW_PRIVS_ENABLE)
}