summaryrefslogtreecommitdiffstats
path: root/vendor/rustix/src/process/procctl.rs
blob: ff842513fab55f8ca0474f1125e7df81499f74dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Bindings for the FreeBSD `procctl` system call.
//!
//! There are similarities (but also differences) with Linux's `prctl` system call, whose interface
//! is located in the `prctl.rs` file.

#![allow(unsafe_code)]

use core::mem::MaybeUninit;

use crate::backend::c::{c_int, c_uint, c_void};
use crate::backend::process::syscalls;
use crate::backend::process::types::{RawId, Signal};
use crate::io;
use crate::process::{Pid, RawPid};

//
// Helper functions.
//

/// Subset of `idtype_t` C enum, with only the values allowed by `procctl`.
#[repr(i32)]
pub enum IdType {
    /// Process id.
    Pid = 0,
    /// Process group id.
    Pgid = 2,
}

/// A process selector for use with the `procctl` interface.
///
/// `None` represents the current process. `Some((IdType::Pid, pid))` represents the process
/// with pid `pid`. `Some((IdType::Pgid, pgid))` represents the control processes belonging to
/// the process group with id `pgid`.
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, (data as *const P 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())
}

//
// PROC_PDEATHSIG_STATUS/PROC_PDEATHSIG_CTL
//

const PROC_PDEATHSIG_STATUS: c_int = 12;

/// Get the current value of the parent process death signal.
///
/// # References
/// - [Linux: `prctl(PR_GET_PDEATHSIG,...)`]
/// - [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]
///
/// [Linux: `prctl(PR_GET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
/// [FreeBSD: `procctl(PROC_PDEATHSIG_STATUS,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[inline]
pub fn parent_process_death_signal() -> io::Result<Option<Signal>> {
    unsafe { procctl_get_optional::<c_int>(PROC_PDEATHSIG_STATUS, None) }.map(Signal::from_raw)
}

const PROC_PDEATHSIG_CTL: c_int = 11;

/// Set the parent-death signal of the calling process.
///
/// # References
/// - [Linux: `prctl(PR_SET_PDEATHSIG,...)`]
/// - [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]
///
/// [Linux: `prctl(PR_SET_PDEATHSIG,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
/// [FreeBSD: `procctl(PROC_PDEATHSIG_CTL,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[inline]
pub fn set_parent_process_death_signal(signal: Option<Signal>) -> io::Result<()> {
    let signal = signal.map_or(0, |signal| signal as c_int);
    unsafe { procctl_set::<c_int>(PROC_PDEATHSIG_CTL, None, &signal) }
}

//
// PROC_TRACE_CTL
//

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;

/// `PROC_TRACE_CTL_*`.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum DumpableBehavior {
    /// Not dumpable.
    NotDumpable = PROC_TRACE_CTL_DISABLE,
    /// Dumpable.
    Dumpable = PROC_TRACE_CTL_ENABLE,
    /// Not dumpable, and this behaviour is preserved across `execve` calls.
    NotDumpableExecPreserved = PROC_TRACE_CTL_DISABLE_EXEC,
}

/// Set the state of the `dumpable` attribute for the process indicated by `idtype` and `id`.
/// This determines whether the process can be traced and whether core dumps are produced for
/// the process upon delivery of a signal whose default behavior is to produce a core dump.
///
/// This is similar to `set_dumpable_behavior` on Linux, with the exception that on FreeBSD
/// there is an extra argument `process`. When `process` is set to `None`, the operation is
/// performed for the current process, like on Linux.
///
/// # References
/// - [`procctl(PROC_TRACE_CTL,...)`]
///
/// [`procctl(PROC_TRACE_CTL,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[inline]
pub fn set_dumpable_behavior(process: ProcSelector, config: DumpableBehavior) -> io::Result<()> {
    unsafe { procctl(PROC_TRACE_CTL, process, config as usize as *mut _) }
}

//
// PROC_TRACE_STATUS
//

const PROC_TRACE_STATUS: c_int = 8;

/// Tracing status as returned by [`trace_status`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TracingStatus {
    /// Tracing is disabled for the process.
    NotTraceble,
    /// Tracing is not disabled for the process, but not debugger/tracer is attached.
    Tracable,
    /// The process is being traced by the process whose pid is stored in the first
    /// component of this variant.
    BeingTraced(Pid),
}

/// Get the tracing status of the process indicated by `idtype` and `id`.
///
/// # References
/// - [`procctl(PROC_TRACE_STATUS,...)`]
///
/// [`procctl(PROC_TRACE_STATUS,...)`]: https://www.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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 = unsafe { Pid::from_raw(pid as RawPid) }.ok_or(io::Errno::RANGE)?;
            Ok(TracingStatus::BeingTraced(pid))
        }
    }
}