1//! Bindings for the FreeBSD `procctl` system call. 2//! 3//! There are similarities (but also differences) with Linux's `prctl` system call, whose interface 4//! is located in the `prctl.rs` file. 5 6#![allow(unsafe_code)] 7 8use core::mem::MaybeUninit; 9 10use crate::backend::c::{c_int, c_uint, c_void}; 11use crate::backend::process::syscalls; 12use crate::backend::process::types::{RawId, Signal}; 13use crate::io; 14use crate::process::{Pid, RawPid}; 15 16// 17// Helper functions. 18// 19 20/// Subset of `idtype_t` C enum, with only the values allowed by `procctl`. 21#[repr(i32)] 22pub enum IdType { 23 /// Process id. 24 Pid = 0, 25 /// Process group id. 26 Pgid = 2, 27} 28 29/// A process selector for use with the `procctl` interface. 30/// 31/// `None` represents the current process. `Some((IdType::Pid, pid))` represents the process 32/// with pid `pid`. `Some((IdType::Pgid, pgid))` represents the control processes belonging to 33/// the process group with id `pgid`. 34pub type ProcSelector = Option<(IdType, Pid)>; 35fn proc_selector_to_raw(selector: ProcSelector) -> (IdType, RawPid) { 36 match selector { 37 Some((idtype, id)) => (idtype, id.as_raw_nonzero().get()), 38 None => (IdType::Pid, 0), 39 } 40} 41 42#[inline] 43pub(crate) unsafe fn procctl( 44 option: c_int, 45 process: ProcSelector, 46 data: *mut c_void, 47) -> io::Result<()> { 48 let (idtype, id) = proc_selector_to_raw(process); 49 syscalls::procctl(idtype as c_uint, id as RawId, option, data) 50} 51 52#[inline] 53pub(crate) unsafe fn procctl_set<P>( 54 option: c_int, 55 process: ProcSelector, 56 data: &P, 57) -> io::Result<()> { 58 procctl(option, process, (data as *const P as *mut P).cast()) 59} 60 61#[inline] 62pub(crate) unsafe fn procctl_get_optional<P>( 63 option: c_int, 64 process: ProcSelector, 65) -> io::Result<P> { 66 let mut value: MaybeUninit<P> = MaybeUninit::uninit(); 67 procctl(option, process, value.as_mut_ptr().cast())?; 68 Ok(value.assume_init()) 69} 70 71// 72// PROC_PDEATHSIG_STATUS/PROC_PDEATHSIG_CTL 73// 74 75const PROC_PDEATHSIG_STATUS: c_int = 12; 76 77/// Get the current value of the parent process death signal. 78/// 79/// # References 80/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`] 81/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`] 82/// 83/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html 84/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 85#[inline] 86pub fn parent_process_death_signal() -> io::Result<Option<Signal>> { 87 unsafe { procctl_get_optional::<c_int>(PROC_PDEATHSIG_STATUS, None) }.map(Signal::from_raw) 88} 89 90const PROC_PDEATHSIG_CTL: c_int = 11; 91 92/// Set the parent-death signal of the calling process. 93/// 94/// # References 95/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`] 96/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`] 97/// 98/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html 99/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 100#[inline] 101pub fn set_parent_process_death_signal(signal: Option<Signal>) -> io::Result<()> { 102 let signal = signal.map_or(0, |signal| signal as c_int); 103 unsafe { procctl_set::<c_int>(PROC_PDEATHSIG_CTL, None, &signal) } 104} 105 106// 107// PROC_TRACE_CTL 108// 109 110const PROC_TRACE_CTL: c_int = 7; 111 112const PROC_TRACE_CTL_ENABLE: i32 = 1; 113const PROC_TRACE_CTL_DISABLE: i32 = 2; 114const PROC_TRACE_CTL_DISABLE_EXEC: i32 = 3; 115 116/// `PROC_TRACE_CTL_*`. 117#[derive(Copy, Clone, Debug, Eq, PartialEq)] 118#[repr(i32)] 119pub enum DumpableBehavior { 120 /// Not dumpable. 121 NotDumpable = PROC_TRACE_CTL_DISABLE, 122 /// Dumpable. 123 Dumpable = PROC_TRACE_CTL_ENABLE, 124 /// Not dumpable, and this behaviour is preserved across `execve` calls. 125 NotDumpableExecPreserved = PROC_TRACE_CTL_DISABLE_EXEC, 126} 127 128/// Set the state of the `dumpable` attribute for the process indicated by `idtype` and `id`. 129/// This determines whether the process can be traced and whether core dumps are produced for 130/// the process upon delivery of a signal whose default behavior is to produce a core dump. 131/// 132/// This is similar to `set_dumpable_behavior` on Linux, with the exception that on FreeBSD 133/// there is an extra argument `process`. When `process` is set to `None`, the operation is 134/// performed for the current process, like on Linux. 135/// 136/// # References 137/// - [`procctl(PROC_TRACE_CTL,...)`] 138/// 139/// [`procctl(PROC_TRACE_CTL,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 140#[inline] 141pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> { 142 unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) } 143} 144 145// 146// PROC_TRACE_STATUS 147// 148 149const PROC_TRACE_STATUS: c_int = 8; 150 151/// Tracing status as returned by [`trace_status`]. 152#[derive(Copy, Clone, Debug, Eq, PartialEq)] 153pub enum TracingStatus { 154 /// Tracing is disabled for the process. 155 NotTraceble, 156 /// Tracing is not disabled for the process, but not debugger/tracer is attached. 157 Tracable, 158 /// The process is being traced by the process whose pid is stored in the first 159 /// component of this variant. 160 BeingTraced(Pid), 161} 162 163/// Get the tracing status of the process indicated by `idtype` and `id`. 164/// 165/// # References 166/// - [`procctl(PROC_TRACE_STATUS,...)`] 167/// 168/// [`procctl(PROC_TRACE_STATUS,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2 169#[inline] 170pub fn trace_status(process: ProcSelector) -> io::Result<TracingStatus> { 171 let val = unsafe { procctl_get_optional::<c_int>(PROC_TRACE_STATUS, process) }?; 172 match val { 173 -1 => Ok(TracingStatus::NotTraceble), 174 0 => Ok(TracingStatus::Tracable), 175 pid => { 176 let pid = unsafe { Pid::from_raw(pid as RawPid) }.ok_or(io::Errno::RANGE)?; 177 Ok(TracingStatus::BeingTraced(pid)) 178 } 179 } 180} 181