Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  procctl.rs   Sprache: unbekannt

 
//! 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)]

#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use core::mem::MaybeUninit;
use core::ptr;

use bitflags::bitflags;

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

//
// 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, (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())
}

//
// 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://man.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://man.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
///  - [FreeBSD `procctl(PROC_TRACE_CTL,...)`]
///
/// [FreeBSD `procctl(PROC_TRACE_CTL,...)`]: https://man.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
///  - [FreeBSD `procctl(PROC_TRACE_STATUS,...)`]
///
/// [FreeBSD `procctl(PROC_TRACE_STATUS,...)`]: https://man.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 = Pid::from_raw(pid as RawPid).ok_or(io::Errno::RANGE)?;
            Ok(TracingStatus::BeingTraced(pid))
        }
    }
}

//
// PROC_REAP_*
//

const PROC_REAP_ACQUIRE: c_int = 2;
const PROC_REAP_RELEASE: c_int = 3;

/// Acquire or release the reaper status of the calling process.
///
/// # References
///  - [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`]
///
/// [FreeBSD: `procctl(PROC_REAP_ACQUIRE/RELEASE,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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! {
    /// `REAPER_STATUS_*`.
    #[repr(transparent)]
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
    pub struct ReaperStatusFlags: c_uint {
        /// The process has acquired reaper status.
        const OWNED = 1;
        /// The process is the root of the reaper tree (pid 1).
        const REALINIT = 2;

        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
        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],
}

/// Reaper status as returned by [`get_reaper_status`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ReaperStatus {
    /// The flags.
    pub flags: ReaperStatusFlags,
    /// The number of children of the reaper among the descendants.
    pub children: usize,
    /// The total number of descendants of the reaper(s), not counting
    /// descendants of the reaper in the subtree.
    pub descendants: usize,
    /// The pid of the reaper for the specified process id.
    pub reaper: Pid,
    /// The pid of one reaper child if there are any descendants.
    pub pid: Option<Pid>,
}

/// Get information about the reaper of the specified process (or the process
/// itself if it is a reaper).
///
/// # References
///  - [FreeBSD: `procctl(PROC_REAP_STATUS,...)`]
///
/// [FreeBSD: `procctl(PROC_REAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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)?)
        },
    })
}

const PROC_REAP_GETPIDS: c_int = 5;

bitflags! {
    /// `REAPER_PIDINFO_*`.
    #[repr(transparent)]
    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
    pub struct PidInfoFlags: c_uint {
        /// This structure was filled by the kernel.
        const VALID = 1;
        /// The pid field identifies a direct child of the reaper.
        const CHILD = 2;
        /// The reported process is itself a reaper. Descendants of a
        /// subordinate reaper are not reported.
        const REAPER = 4;
        /// The reported process is in the zombie state.
        const ZOMBIE = 8;
        /// The reported process is stopped by
        /// [`Signal::Stop`]/[`Signal::Tstp`].
        const STOPPED = 16;
        /// The reported process is in the process of exiting.
        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,
}

/// A child process of a reaper.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PidInfo {
    /// The flags of the process.
    pub flags: PidInfoFlags,
    /// The pid of the process.
    pub pid: Pid,
    /// The pid of the child of the reaper which is the (grand-..)parent of the
    /// process.
    pub subtree: Pid,
}

/// Get the list of descendants of the specified reaper process.
///
/// # References
///  - [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`]
///
/// [FreeBSD: `procctl(PROC_REAP_GETPIDS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[cfg(feature = "alloc")]
pub fn get_reaper_pids(process: ProcSelector) -> io::Result<Vec<PidInfo>> {
    // Sadly no better way to guarantee that we get all the results than to
    // allocate ~8MB of memory..
    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! {
    /// `REAPER_KILL_*`.
    #[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],
}

/// Reaper status as returned by [`get_reaper_status`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct KillResult {
    /// The number of processes that were signalled.
    pub killed: usize,
    /// The pid of the first process that wasn't successfully signalled.
    pub first_failed: Option<Pid>,
}

/// Deliver a signal to some subset of
///
/// # References
///  - [FreeBSD: `procctl(PROC_REAP_KILL,...)`]
///
/// [FreeBSD: `procctl(PROC_REAP_KILL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
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 c_int,
        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),
    })
}

//
// PROC_TRAPCAP_STATUS/PROC_TRAPCAP_CTL
//

const PROC_TRAPCAP_CTL: c_int = 9;

const PROC_TRAPCAP_CTL_ENABLE: i32 = 1;
const PROC_TRAPCAP_CTL_DISABLE: i32 = 2;

/// `PROC_TRAPCAP_CTL_*`.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum TrapCapBehavior {
    /// Disable the [`Signal::Trap`] signal delivery on capability mode access
    /// violations.
    Disable = PROC_TRAPCAP_CTL_DISABLE,
    /// Enable the [`Signal::Trap`] signal delivery on capability mode access
    /// violations.
    Enable = PROC_TRAPCAP_CTL_ENABLE,
}

/// Set the current value of the capability mode violation trapping behavior.
/// If this behavior is enabled, the kernel would deliver a [`Signal::Trap`]
/// signal on any return from a system call that would result in a
/// [`io::Errno::NOTCAPABLE`]` or [`io::Errno::CAPMODE`] error.
///
/// This behavior is inherited by the children of the process and is kept
/// across `execve` calls.
///
/// # References
///  - [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`]
///
/// [FreeBSD: `procctl(PROC_TRAPCAP_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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;

/// Get the current value of the capability mode violation trapping behavior.
///
/// # References
///  - [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`]
///
/// [FreeBSD: `procctl(PROC_TRAPCAP_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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),
    }
}

//
// PROC_NO_NEW_PRIVS_STATUS/PROC_NO_NEW_PRIVS_CTL
//

const PROC_NO_NEW_PRIVS_CTL: c_int = 19;

const PROC_NO_NEW_PRIVS_ENABLE: c_int = 1;

/// Enable the `no_new_privs` mode that ignores SUID and SGID bits on `execve`
/// in the specified process and its future descendants.
///
/// This is similar to `set_no_new_privs` on Linux, with the exception that on
/// FreeBSD there is no argument `no_new_privs` argument as it's only possible
/// to enable this mode and there's no going back.
///
/// # References
///  - [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`]
///  - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`]
///
/// [Linux: `prctl(PR_SET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_CTL,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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;

/// Check the `no_new_privs` mode of the specified process.
///
/// # References
///  - [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`]
///  - [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`]
///
/// [Linux: `prctl(PR_GET_NO_NEW_PRIVS,...)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
/// [FreeBSD: `procctl(PROC_NO_NEW_PRIVS_STATUS,...)`]: https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2
#[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)
}

[ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge