Refactor refresh

Moving different mechanisms into different conditionally-compiled
modules. This way it can be extended, e.g. with docker lookups, MacOS
support, etc.
This commit is contained in:
John Doty 2024-08-04 08:14:19 -07:00
parent 75343dbea2
commit c2c57289cf
2 changed files with 73 additions and 46 deletions

View file

@ -1,61 +1,34 @@
use crate::message::PortDesc;
use anyhow::Result; use anyhow::Result;
use log::{error, warn};
#[cfg(not(target_os = "linux"))]
pub fn get_entries() -> Result<Vec<PortDesc>> {
use anyhow::bail;
bail!("Not supported on this operating system");
}
#[cfg(target_os = "linux")]
pub fn get_entries() -> Result<Vec<PortDesc>> {
use procfs::process::FDTarget;
use std::collections::HashMap; use std::collections::HashMap;
let all_procs = procfs::process::all_processes()?; use crate::message::PortDesc;
// build up a map between socket inodes and process stat info. Ignore any #[cfg(target_os = "linux")]
// error we encounter as it probably means we have no access to that mod procfs;
// process or something.
let mut map: HashMap<u64, String> = HashMap::new();
for process in all_procs.flatten() {
if !process.is_alive() {
continue; // Ignore zombies.
}
if let (Ok(fds), Ok(cmd)) = (process.fd(), process.cmdline()) { pub fn get_entries() -> Result<Vec<PortDesc>> {
for fd in fds.flatten() { #[allow(unused)]
if let FDTarget::Socket(inode) = fd.target { let mut attempts = 0;
map.insert(inode, cmd.join(" "));
}
}
}
}
let mut h: HashMap<u16, PortDesc> = HashMap::new(); let mut result: HashMap<u16, PortDesc> = HashMap::new();
// Go through all the listening IPv4 and IPv6 sockets and take the first #[cfg(target_os = "linux")]
// instance of listening on each port *if* the address is loopback or
// unspecified. (TODO: Do we want this restriction really?)
let tcp = procfs::net::tcp()?;
let tcp6 = procfs::net::tcp6()?;
for tcp_entry in tcp.into_iter().chain(tcp6) {
if tcp_entry.state == procfs::net::TcpState::Listen
&& (tcp_entry.local_address.ip().is_loopback()
|| tcp_entry.local_address.ip().is_unspecified())
&& !h.contains_key(&tcp_entry.local_address.port())
{ {
if let Some(cmd) = map.get(&tcp_entry.inode) { attempts += 1;
h.insert( match procfs::get_entries() {
tcp_entry.local_address.port(), Ok(m) => {
PortDesc { for (p, d) in m {
port: tcp_entry.local_address.port(), result.entry(p).or_insert(d);
desc: cmd.clone(),
},
);
} }
} }
Err(e) => error!("Error reading procfs: {e:?}"),
}
} }
Ok(h.into_values().collect()) if attempts == 0 {
warn!("Port scanning is not supported for this server");
}
Ok(result.into_values().collect())
} }

View file

@ -0,0 +1,54 @@
use anyhow::Result;
use procfs::process::FDTarget;
use std::collections::HashMap;
use crate::message::PortDesc;
pub fn get_entries() -> Result<HashMap<u16, PortDesc>> {
let all_procs = procfs::process::all_processes()?;
// build up a map between socket inodes and process stat info. Ignore any
// error we encounter as it probably means we have no access to that
// process or something.
let mut map: HashMap<u64, String> = HashMap::new();
for process in all_procs.flatten() {
if !process.is_alive() {
continue; // Ignore zombies.
}
if let (Ok(fds), Ok(cmd)) = (process.fd(), process.cmdline()) {
for fd in fds.flatten() {
if let FDTarget::Socket(inode) = fd.target {
map.insert(inode, cmd.join(" "));
}
}
}
}
let mut h: HashMap<u16, PortDesc> = HashMap::new();
// Go through all the listening IPv4 and IPv6 sockets and take the first
// instance of listening on each port *if* the address is loopback or
// unspecified. (TODO: Do we want this restriction really?)
let tcp = procfs::net::tcp()?;
let tcp6 = procfs::net::tcp6()?;
for tcp_entry in tcp.into_iter().chain(tcp6) {
if tcp_entry.state == procfs::net::TcpState::Listen
&& (tcp_entry.local_address.ip().is_loopback()
|| tcp_entry.local_address.ip().is_unspecified())
&& !h.contains_key(&tcp_entry.local_address.port())
{
if let Some(cmd) = map.get(&tcp_entry.inode) {
h.insert(
tcp_entry.local_address.port(),
PortDesc {
port: tcp_entry.local_address.port(),
desc: cmd.clone(),
},
);
}
}
}
Ok(h)
}