use std::ffi::{c_int, c_uint, c_void};
use std::io;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use crate::error::io_assert;
use crate::ns::NsFd;
#[rustfmt::skip]
mod ioctls {
use std::ffi::c_int;
use crate::ioctl::{io, iowr};
use super::CPidFdInfo;
pub const PIDFS_IOCTL_MAGIC: c_int = 0xFF;
pub const PIDFD_GET_CGROUP_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 1);
pub const PIDFD_GET_IPC_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 2);
pub const PIDFD_GET_MNT_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 3);
pub const PIDFD_GET_NET_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 4);
pub const PIDFD_GET_PID_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 5);
pub const PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 6);
pub const PIDFD_GET_TIME_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 7);
pub const PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 8);
pub const PIDFD_GET_USER_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 9);
pub const PIDFD_GET_UTS_NAMESPACE : c_int = io(PIDFS_IOCTL_MAGIC, 10);
pub const PIDFD_GET_INFO : c_int = iowr::<CPidFdInfo>(PIDFS_IOCTL_MAGIC, 11);
}
#[derive(Debug)]
pub struct PidFd {
fd: OwnedFd,
}
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 {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for PidFd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
macro_rules! ns_fd_getters {
(
$(
$(#[$doc:meta])+
$name:ident($ioctl:expr) -> $nsty:ident;
)+
) => {
$(
$(#[$doc])+
pub fn $name(&self) -> io::Result<NsFd<crate::ns::$nsty>> {
unsafe {
let fd = libc::ioctl(self.as_raw_fd(), $ioctl as u64, 0);
io_assert!(fd >= 0);
Ok(NsFd::from_raw_fd(fd))
}
}
)+
};
}
impl PidFd {
pub fn open(pid: libc::pid_t, flags: PidFdFlags) -> io::Result<Self> {
let fd = unsafe { libc::syscall(libc::SYS_pidfd_open, pid, flags.bits()) };
io_assert!(fd >= 0);
Ok(unsafe { Self::from_raw_fd(i32::try_from(fd).unwrap()) })
}
pub fn this(flags: PidFdFlags) -> io::Result<Self> {
let pid = unsafe { libc::getpid() };
Self::open(pid, flags)
}
pub fn get_fd(&self, fd: RawFd) -> io::Result<OwnedFd> {
unsafe {
let fd = libc::syscall(libc::SYS_pidfd_getfd, self.as_raw_fd(), fd, 0);
io_assert!(fd >= 0);
Ok(OwnedFd::from_raw_fd(i32::try_from(fd).unwrap()))
}
}
pub fn send_signal(&self, signal: c_int) -> io::Result<()> {
let rc = unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
self.as_raw_fd(),
signal,
std::ptr::null::<c_void>(),
0,
)
};
io_assert!(rc == 0);
Ok(())
}
ns_fd_getters! {
cgroup_namespace(ioctls::PIDFD_GET_CGROUP_NAMESPACE) -> CGroup;
ipc_namespace(ioctls::PIDFD_GET_IPC_NAMESPACE) -> Ipc;
mount_namespace(ioctls::PIDFD_GET_MNT_NAMESPACE) -> Mnt;
network_namespace(ioctls::PIDFD_GET_NET_NAMESPACE) -> Net;
pid_namespace(ioctls::PIDFD_GET_PID_NAMESPACE) -> Pid;
pid_namespace_for_children(ioctls::PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE) -> Pid;
time_namespace(ioctls::PIDFD_GET_TIME_NAMESPACE) -> Time;
time_namespace_for_children(ioctls::PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE) -> Time;
user_namespace(ioctls::PIDFD_GET_USER_NAMESPACE) -> User;
uts_namespace(ioctls::PIDFD_GET_UTS_NAMESPACE) -> Uts;
}
pub fn info(&self, flags: GetInfoFlags) -> io::Result<Info> {
unsafe {
let mut info = Info {
raw: CPidFdInfo {
mask: flags.bits(),
..std::mem::zeroed()
},
};
let rc = libc::ioctl(
self.as_raw_fd(),
ioctls::PIDFD_GET_INFO as u64,
&raw mut info.raw,
);
io_assert!(rc == 0);
Ok(info)
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct PidFdFlags: c_uint {
const NONBLOCK = libc::PIDFD_NONBLOCK;
}
}
impl Default for PidFdFlags {
fn default() -> Self {
Self::empty()
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct GetInfoFlags: u64 {
const PID = 0x0000_0001;
const CREDS = 0x0000_0002;
const CGROUP_ID = 0x0000_0004;
const EXIT = 0x0000_0008;
}
}
impl Default for GetInfoFlags {
fn default() -> Self {
const { Self::from_bits(Self::PID.bits() | Self::CREDS.bits() | Self::CGROUP_ID.bits()).unwrap() }
}
}
#[derive(Clone, Debug)]
#[repr(C)]
struct CPidFdInfo {
mask: u64,
cgroupid: u64,
pid: u32,
tgid: u32,
ppid: u32,
ruid: u32,
rgid: u32,
euid: u32,
egid: u32,
suid: u32,
sgid: u32,
fsuid: u32,
fsgid: u32,
exit_code: i32,
}
#[derive(Clone, Debug)]
pub struct Info {
raw: CPidFdInfo,
}
impl Info {
fn maybe<T>(&self, flags: GetInfoFlags, value: T) -> Option<T> {
(self.raw.mask & flags.bits() == flags.bits()).then_some(value)
}
pub fn pid(&self) -> Option<libc::pid_t> {
self.maybe(GetInfoFlags::PID, self.raw.pid as libc::pid_t)
}
pub fn cgroup_id(&self) -> Option<u64> {
self.maybe(GetInfoFlags::CGROUP_ID, self.raw.cgroupid)
}
pub fn thread_group_id(&self) -> Option<u32> {
self.maybe(GetInfoFlags::PID, self.raw.tgid)
}
pub fn parent_pid(&self) -> Option<libc::pid_t> {
self.maybe(GetInfoFlags::PID, self.raw.ppid as libc::pid_t)
}
pub fn exit_code(&self) -> Option<i32> {
self.maybe(GetInfoFlags::EXIT, self.raw.exit_code)
}
pub fn credentials(&self) -> Option<Credentials> {
self.maybe(
GetInfoFlags::CREDS,
Credentials {
ruid: self.raw.ruid as libc::uid_t,
rgid: self.raw.rgid as libc::gid_t,
euid: self.raw.euid as libc::uid_t,
egid: self.raw.egid as libc::gid_t,
suid: self.raw.suid as libc::uid_t,
sgid: self.raw.sgid as libc::gid_t,
fsuid: self.raw.fsuid as libc::uid_t,
fsgid: self.raw.fsgid as libc::gid_t,
},
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Credentials {
pub ruid: libc::uid_t,
pub rgid: libc::gid_t,
pub euid: libc::uid_t,
pub egid: libc::gid_t,
pub suid: libc::uid_t,
pub sgid: libc::gid_t,
pub fsuid: libc::uid_t,
pub fsgid: libc::gid_t,
}