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