use std::{
io,
mem::{self, MaybeUninit},
};
use crate::{
helpers::syscall,
libproc::{
bindings,
proc_pid::{ListPIDInfo, PidInfoFlavor},
},
};
use libc::c_void;
pub enum PIDFDInfoFlavor {
VNodeInfo = 1,
VNodePathInfo = 2,
SocketInfo = 3,
PSEMInfo = 4,
PSHMInfo = 5,
PipeInfo = 6,
KQueueInfo = 7,
ATalkInfo = 8,
}
pub struct ListFDs;
impl ListPIDInfo for ListFDs {
type Item = ProcFDInfo;
fn flavor() -> PidInfoFlavor {
PidInfoFlavor::ListFDs
}
}
#[repr(C)]
pub struct ProcFDInfo {
pub proc_fd: i32,
pub proc_fdtype: u32,
}
#[derive(Copy, Clone, Debug)]
pub enum ProcFDType {
ATalk = 0,
VNode = 1,
Socket = 2,
PSHM = 3,
PSEM = 4,
KQueue = 5,
Pipe = 6,
FSEvents = 7,
NetPolicy = 9,
Unknown,
}
impl From<u32> for ProcFDType {
fn from(value: u32) -> ProcFDType {
match value {
0 => ProcFDType::ATalk,
1 => ProcFDType::VNode,
2 => ProcFDType::Socket,
3 => ProcFDType::PSHM,
4 => ProcFDType::PSEM,
5 => ProcFDType::KQueue,
6 => ProcFDType::Pipe,
7 => ProcFDType::FSEvents,
_ => ProcFDType::Unknown,
}
}
}
pub trait PIDFDInfo {
fn flavor() -> PIDFDInfoFlavor;
}
pub fn pidfdinfo<T: PIDFDInfo>(pid: i32, fd: i32) -> Result<T, io::Error> {
let flavor = T::flavor() as i32;
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let buffer_size = mem::size_of::<T>() as i32;
let mut pidinfo = MaybeUninit::<T>::uninit();
#[allow(clippy::pedantic)]
let buffer_ptr = &mut pidinfo as *mut _ as *mut c_void;
syscall::cvt_positive(unsafe {
bindings::proc_pidfdinfo(pid, fd, flavor, buffer_ptr, buffer_size)
})?;
Ok(unsafe { pidinfo.assume_init() })
}
#[cfg(all(test, target_os = "macos"))]
mod test {
use super::pidfdinfo;
use crate::libproc::bsd_info::BSDInfo;
use crate::libproc::file_info::{ListFDs, ProcFDType};
use crate::libproc::net_info::{SocketFDInfo, SocketInfoKind};
use crate::libproc::proc_pid::{listpidinfo, pidinfo};
use std::process;
#[test]
#[allow(clippy::cast_possible_wrap)]
fn pidfdinfo_test() {
let pid = process::id() as i32;
let info = pidinfo::<BSDInfo>(pid, 0).expect("pidinfo() failed");
let fds =
listpidinfo::<ListFDs>(pid, info.pbi_nfiles as usize).expect("listpidinfo() failed");
for fd in fds {
if let ProcFDType::Socket = fd.proc_fdtype.into() {
let socket =
pidfdinfo::<SocketFDInfo>(pid, fd.proc_fd).expect("pidfdinfo() failed");
if let SocketInfoKind::Tcp = socket.psi.soi_kind.into() {
unsafe {
let info = socket.psi.soi_proto.pri_tcp;
assert_eq!(socket.psi.soi_protocol, libc::IPPROTO_TCP);
assert_eq!(info.tcpsi_ini.insi_lport, 65535);
}
}
}
}
}
}