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