extern crate libc;
use std::mem;
use crate::libproc::helpers;
use crate::libproc::proc_pid::{ListPIDInfo, PidInfoFlavor};
#[cfg(target_os = "macos")]
use self::libc::c_void;
#[cfg(target_os = "macos")]
use crate::osx_libproc_bindings::proc_pidfdinfo;
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,
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: Default {
fn flavor() -> PIDFDInfoFlavor;
}
#[cfg(target_os = "macos")]
pub fn pidfdinfo<T: PIDFDInfo>(pid: i32, fd: i32) -> Result<T, String> {
let flavor = T::flavor() as i32;
let buffer_size = mem::size_of::<T>() as i32;
let mut pidinfo = T::default();
let buffer_ptr = &mut pidinfo as *mut _ as *mut c_void;
let ret: i32;
unsafe {
ret = proc_pidfdinfo(pid, fd, flavor, buffer_ptr, buffer_size);
};
if ret <= 0 {
Err(helpers::get_errno_with_message(ret))
} else {
Ok(pidinfo)
}
}
#[cfg(not(target_os = "macos"))]
pub fn pidfdinfo<T: PIDFDInfo>(_pid: i32, _fd: i32) -> Result<T, String> {
unimplemented!()
}
#[cfg(all(test, target_os = "macos"))]
mod test {
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 super::pidfdinfo;
#[test]
fn pidfdinfo_test() {
use std::process;
use std::net::TcpListener;
let pid = process::id() as i32;
let _listener = TcpListener::bind("127.0.0.1:65535");
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 as u32, 65535);
}
}
}
}
}
}