diff --git a/changelog/2164.added.md b/changelog/2164.added.md new file mode 100644 index 0000000000..e20fb670d9 --- /dev/null +++ b/changelog/2164.added.md @@ -0,0 +1 @@ +Added the `procctl` process feature to `::nix::sys` for FreeBSD. diff --git a/src/sys/mod.rs b/src/sys/mod.rs index bf047b3dda..33e01da786 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -73,6 +73,12 @@ feature! { pub mod prctl; } +#[cfg(target_os = "freebsd")] +feature! { + #![feature = "process"] + pub mod procctl; +} + feature! { #![feature = "pthread"] pub mod pthread; diff --git a/src/sys/procctl.rs b/src/sys/procctl.rs new file mode 100644 index 0000000000..2179ed2fc4 --- /dev/null +++ b/src/sys/procctl.rs @@ -0,0 +1,58 @@ +//! procctl provides programmatic control over processes. +//! +//! Here we act on the current process, so we save a getpid syscall here. +//! +//! For more documentation, please read [procctl(2)](https://man.freebsd.org/cgi/man.cgi?query=procctl&sektion=2) +use crate::errno::Errno; +use crate::sys::signal::Signal; +use crate::Result; + +use libc::c_int; +use std::convert::TryFrom; + +/// Enable/disable tracing on the current process, allowing debugging and core dump generation. +pub fn set_dumpable(attribute: bool) -> Result<()> { + let mut dumpable = match attribute { + true => libc::PROC_TRACE_CTL_ENABLE, + false => libc::PROC_TRACE_CTL_DISABLE + }; + + let res = unsafe { libc::procctl(libc::P_PID, 0, libc::PROC_TRACE_CTL, &mut dumpable as *mut c_int as _) }; + Errno::result(res).map(drop) +} + +/// Get the tracing status of the current process. +pub fn get_dumpable() -> Result { + let mut dumpable: c_int = 0; + + let res = unsafe { libc::procctl(libc::P_PID, 0, libc::PROC_TRACE_STATUS, &mut dumpable as *mut c_int as _) }; + match Errno::result(res) { + Ok(_) => Ok(matches!(dumpable, libc::PROC_TRACE_CTL_ENABLE)), + Err(e) => Err(e), + } +} + +/// Set the delivery of the `signal` when the parent of the calling process exits. +pub fn set_pdeathsig>>(signal: T) -> Result<()> { + let mut sig = match signal.into() { + Some(s) => s as c_int, + None => 0, + }; + + let res = unsafe { libc::procctl(libc::P_PID, 0, libc::PROC_PDEATHSIG_CTL, &mut sig as *mut c_int as _) }; + Errno::result(res).map(drop) +} + +/// Get the current signal id that will be delivered to the parent process when it's exiting. +pub fn get_pdeathsig() -> Result> { + let mut sig: c_int = 0; + + let res = unsafe { libc::procctl(libc::P_PID, 0, libc::PROC_PDEATHSIG_STATUS, &mut sig as *mut c_int as _) }; + match Errno::result(res) { + Ok(_) => Ok(match sig { + 0 => None, + _ => Some(Signal::try_from(sig)?), + }), + Err(e) => Err(e), + } +} diff --git a/test/sys/test_procctl.rs b/test/sys/test_procctl.rs new file mode 100644 index 0000000000..3e360d5641 --- /dev/null +++ b/test/sys/test_procctl.rs @@ -0,0 +1,29 @@ +#[cfg(target_os = "freebsd")] +#[cfg(feature = "process")] +mod test_prctl { + use nix::sys::proctl; + + #[test] + fn test_get_set_dumpable() { + let original = procctl::get_dumpable().unwrap(); + + prctl::set_dumpable(false).unwrap(); + let dumpable = procctl::get_dumpable().unwrap(); + assert!(!dumpable); + + prctl::set_dumpable(original).unwrap(); + } + + #[test] + fn test_get_set_pdeathsig() { + use nix::sys::signal::Signal; + + let original = procctl::get_pdeathsig().unwrap(); + + procctl::set_pdeathsig(Signal::SIGUSR1).unwrap(); + let sig = procctl::get_pdeathsig().unwrap(); + assert_eq!(sig, Some(Signal::SIGUSR1)); + + procctl::set_pdeathsig(original).unwrap(); + } +}