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:
parent
75343dbea2
commit
c2c57289cf
2 changed files with 73 additions and 46 deletions
|
|
@ -1,61 +1,34 @@
|
||||||
use crate::message::PortDesc;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use log::{error, warn};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
use crate::message::PortDesc;
|
||||||
pub fn get_entries() -> Result<Vec<PortDesc>> {
|
|
||||||
use anyhow::bail;
|
|
||||||
bail!("Not supported on this operating system");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
mod procfs;
|
||||||
|
|
||||||
pub fn get_entries() -> Result<Vec<PortDesc>> {
|
pub fn get_entries() -> Result<Vec<PortDesc>> {
|
||||||
use procfs::process::FDTarget;
|
#[allow(unused)]
|
||||||
use std::collections::HashMap;
|
let mut attempts = 0;
|
||||||
|
|
||||||
let all_procs = procfs::process::all_processes()?;
|
let mut result: HashMap<u16, PortDesc> = HashMap::new();
|
||||||
|
|
||||||
// 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
|
|
||||||
// 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) {
|
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())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
src/server/refresh/procfs.rs
Normal file
54
src/server/refresh/procfs.rs
Normal 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)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue