sys: add pid module with PidFd type

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-01-15 10:57:11 +01:00
parent 20fe5e1781
commit a01491036c
2 changed files with 176 additions and 0 deletions

View File

@ -4,6 +4,7 @@ use failure::*;
use proxmox_tools as tools;
pub mod magic;
pub mod pid;
pub mod procfs;
/// Get pseudo random data (/dev/urandom)

View File

@ -0,0 +1,175 @@
//! PID file descriptor handling.
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use nix::fcntl::OFlag;
use nix::sys::signal::Signal;
use nix::sys::signalfd::siginfo;
use nix::sys::stat::Mode;
use nix::unistd::Pid;
use nix::NixPath;
use crate::error::{io_err_other, SysResult};
use crate::linux::procfs::{MountInfo, PidStat};
use crate::{c_result, c_str, c_try};
use proxmox_tools::fd::Fd;
/// asm-generic pidfd_open syscall number
#[allow(non_upper_case_globals)]
pub const SYS_pidfd_open: libc::c_long = 434;
/// asm-generic pidfd_send_signal syscall number
#[allow(non_upper_case_globals)]
pub const SYS_pidfd_send_signal: libc::c_long = 424;
unsafe fn pidfd_open(pid: libc::pid_t, flags: libc::c_uint) -> libc::c_long {
libc::syscall(SYS_pidfd_open, pid, flags)
}
unsafe fn pidfd_send_signal(
pidfd: RawFd,
sig: libc::c_int,
info: *mut libc::siginfo_t,
flags: libc::c_uint,
) -> libc::c_long {
libc::syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags)
}
/// File descriptor refernce to a process.
pub struct PidFd {
fd: Fd,
pid: Pid,
}
impl PidFd {
/// Get a pidfd to the current process.
pub fn current() -> io::Result<Self> {
Self::open(Pid::this())
}
/// Open a pidfd for the given process id.
pub fn open(pid: Pid) -> io::Result<Self> {
let fd = unsafe { Fd::from_raw_fd(c_try!(pidfd_open(pid.as_raw(), 0)) as RawFd) };
Ok(Self { fd, pid })
}
/// Send a signal to the process.
pub fn send_signal<S: Into<Option<Signal>>>(
&self,
sig: S,
info: Option<&mut siginfo>,
) -> io::Result<()> {
let sig = match sig.into() {
Some(sig) => sig as libc::c_int,
None => 0,
};
let info = match info {
Some(info) => info as *mut siginfo as *mut libc::siginfo_t,
None => std::ptr::null_mut(),
};
c_result!(unsafe { pidfd_send_signal(self.fd.as_raw_fd(), sig, info, 0) }).map(drop)
}
/// Get the original PID number used to open this pidfd. Note that this may not be the correct
/// pid if the PID namespace was changed (which is currently only possible by forking, which is
/// why this is usually safe under normal circumstances.)
#[inline]
pub const fn pid(&self) -> Pid {
self.pid
}
/// Open a procfs file from. This is equivalent to opening `/proc/<pid>/<file>` using this
/// process actual pid. This also works if the file descriptor has been sent over
pub fn open_file<P: ?Sized + NixPath>(&self, path: &P) -> io::Result<File> {
Fd::openat(
self,
path,
OFlag::O_RDONLY | OFlag::O_CLOEXEC,
Mode::empty(),
)
.map(|fd| unsafe { File::from_raw_fd(fd.into_raw_fd()) })
.into_io_result()
}
/// Convenience helper to read a procfs file into memory.
///
/// This calls `self.open_file()` reads it to the end.
pub fn read_file<P: ?Sized + NixPath>(&self, path: &P) -> io::Result<Vec<u8>> {
use io::Read;
let mut reader = self.open_file(path)?;
let mut out = Vec::new();
reader.read_to_end(&mut out)?;
Ok(out)
}
/// Get the `PidStat` structure for this process. (`/proc/PID/stat`)
pub fn get_stat(&self) -> io::Result<PidStat> {
let data = self.read_file(c_str!("stat"))?;
let data = String::from_utf8(data).map_err(io_err_other)?;
PidStat::parse(&data).map_err(io_err_other)
}
/// Read this process' `/proc/PID/mountinfo` file.
pub fn get_mount_info(&self) -> io::Result<MountInfo> {
MountInfo::parse(&self.read_file(c_str!("mountinfo"))?).map_err(io_err_other)
}
/// Attempt to get a `PidFd` from a raw file descriptor.
///
/// This will attempt to read the pid number via the file descriptor.
pub fn try_from_raw_fd(fd: RawFd) -> io::Result<Self> {
let mut this = Self {
fd: unsafe { Fd::from_raw_fd(fd) },
pid: Pid::from_raw(1),
};
// Simple check first: is it a valid pid file descriptor:
if let Err(err) = this.send_signal(None, None) {
if err.kind() == io::ErrorKind::PermissionDenied {
// valid pidfd, but we probably can't do much with it, proceed anyway...
} else {
// make sure we don't try to close the file descriptor:
let _ = this.fd.into_raw_fd();
// if err.raw_os_error() == Some(libc::EBADF)
// => not a valid pid fd, pass the error through
// if err.raw_os_error() == Some(libc::ENOSYS)
// => kernel too old, things most likely won't work anyway
return Err(err);
}
}
match this.get_stat() {
Ok(stat) => {
this.pid = stat.pid;
Ok(this)
}
Err(err) => {
// make sure we don't close the raw file descriptor:
let _ = this.fd.into_raw_fd();
Err(err)
}
}
}
}
impl AsRawFd for PidFd {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl IntoRawFd for PidFd {
fn into_raw_fd(self) -> RawFd {
self.fd.into_raw_fd()
}
}
impl FromRawFd for PidFd {
/// Panics if the file descriptor is not an actual pid fd (or at least a reference to its proc
/// directory).
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::try_from_raw_fd(fd).unwrap()
}
}