use std::collections::HashMap;
use std::io;
use nix::sys::stat::FileStat;
use super::net::derive_peer_processes;
use super::net::get_sockprotoname;
use super::net::has_loopback_tcp_peers;
use super::net::parse_socket_info;
use super::net::query_socket_details;
use super::net::unix_peer_process;
use super::net::Socket;
use super::ProcHandle;
use crate::model::fd::FDTarget;
use crate::model::fdinfo::FdInfo;
use crate::model::net::NetEntry;
pub struct FileDescriptor {
pub fd: u64,
pub target: FDTarget,
pub stat: Option<FileStat>,
pub fdinfo: FdInfo,
pub socket: Option<Socket>,
pub sockprotoname: Option<String>,
}
impl ProcHandle {
pub fn file_descriptors(&self) -> io::Result<Vec<FileDescriptor>> {
let fds = self.fds()?;
let sockets = parse_socket_info(&*self.source);
let tcp_peers = if !self.is_core && has_loopback_tcp_peers(&sockets) {
derive_peer_processes(self.pid(), &sockets)
} else {
HashMap::new()
};
let mut result = Vec::with_capacity(fds.len());
for fd_num in fds {
match self.gather_fd(fd_num, &sockets, &tcp_peers) {
Ok(fd_desc) => result.push(fd_desc),
Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
eprintln!("fd {fd_num}: permission denied");
}
Err(e) => {
eprintln!("fd {fd_num}: {e}");
}
}
}
Ok(result)
}
#[allow(clippy::unnecessary_cast)]
fn gather_fd(
&self,
fd_num: u64,
sockets: &HashMap<u64, NetEntry>,
tcp_peers: &HashMap<u64, (u64, String)>,
) -> io::Result<FileDescriptor> {
let link_path = self.fd_path(fd_num)?;
let link_text = link_path.to_string_lossy();
let info = self.fdinfo(fd_num)?;
let stat_result = if !self.is_core {
nix::sys::stat::stat(format!("/proc/{}/fd/{}", self.pid(), fd_num).as_str()).ok()
} else {
None
};
let target = if let Some(ref st) = stat_result {
FDTarget::from_stat(st.st_mode, st.st_ino as u64, &link_text)
} else {
FDTarget::from_link(&link_text)
};
let mut socket = None;
let mut sockprotoname = None;
if matches!(target, FDTarget::Socket(_)) {
let inode = target.inode();
if let Some(inode) = inode {
if let Some(sock_info) = sockets.get(&inode) {
let details = query_socket_details(self.pid(), fd_num);
let peer = tcp_peers.get(&inode).cloned().or_else(|| {
if matches!(sock_info, NetEntry::Unix(_)) && !self.is_core {
unix_peer_process(self.pid(), fd_num)
} else {
None
}
});
socket = Some(Socket::from_parts(sock_info.clone(), details, peer));
}
}
if socket.is_none() && !self.is_core {
sockprotoname = get_sockprotoname(self.pid(), fd_num);
}
}
Ok(FileDescriptor {
fd: fd_num,
target,
stat: stat_result,
fdinfo: info,
socket,
sockprotoname,
})
}
}