Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature, add PID column to processes table #165

Closed
wants to merge 15 commits into from
29 changes: 24 additions & 5 deletions src/display/components/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ fn display_upload_and_download(bandwidth: &impl Bandwidth, total: bool) -> Strin
pub enum ColumnCount {
Two,
Three,
Four,
}

impl ColumnCount {
pub fn as_u16(&self) -> u16 {
match &self {
ColumnCount::Two => 2,
ColumnCount::Three => 3,
ColumnCount::Four => 4,
}
}
}
Expand Down Expand Up @@ -120,16 +122,21 @@ impl<'a> Table<'a> {
let processes_rows = state
.processes
.iter()
.map(|(process_name, data_for_process)| {
.map(|(proc_info, data_for_process)| {
vec![
(*process_name).to_string(),
(*proc_info.procname).to_string(),
if proc_info.pid > 0 {
proc_info.pid.to_string()
} else {
"".to_string()
},
data_for_process.connection_count.to_string(),
display_upload_and_download(data_for_process, state.cumulative_mode),
]
})
.collect();
let processes_title = "Utilization by process name";
let processes_column_names = &["Process", "Connections", "Up / Down"];
let processes_column_names = &["Process", "PID", "Connections", "Rate Up / Down"];
let mut breakpoints = BTreeMap::new();
breakpoints.insert(
0,
Expand All @@ -148,8 +155,8 @@ impl<'a> Table<'a> {
breakpoints.insert(
100,
ColumnData {
column_count: ColumnCount::Three,
column_widths: vec![40, 12, 23],
column_count: ColumnCount::Four,
column_widths: vec![40, 12, 12, 23],
},
);
breakpoints.insert(
Expand Down Expand Up @@ -248,6 +255,12 @@ impl<'a> Table<'a> {
self.column_names[1],
self.column_names[2],
],
ColumnCount::Four => vec![
self.column_names[0],
self.column_names[1],
self.column_names[2],
self.column_names[3],
],
};

let rows = self.rows.iter().map(|row| match column_count {
Expand All @@ -260,6 +273,12 @@ impl<'a> Table<'a> {
truncate_middle(&row[1], widths[1]),
truncate_middle(&row[2], widths[2]),
],
ColumnCount::Four => vec![
truncate_middle(&row[0], widths[0]),
truncate_middle(&row[1], widths[1]),
truncate_middle(&row[2], widths[2]),
truncate_middle(&row[3], widths[3]),
],
});

let table_rows = rows.map(|row| Row::StyledData(row.into_iter(), Style::default()));
Expand Down
7 changes: 4 additions & 3 deletions src/display/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ::tui::Terminal;
use crate::display::components::{HelpText, Layout, Table, TotalBandwidth};
use crate::display::UIState;
use crate::network::{display_connection_string, display_ip_or_host, LocalSocket, Utilization};
use crate::os::ProcessInfo;

use ::std::net::IpAddr;

Expand Down Expand Up @@ -44,11 +45,11 @@ where
let ip_to_host = &self.ip_to_host;
let local_time: DateTime<Local> = Local::now();
let timestamp = local_time.timestamp();
for (process, process_network_data) in &state.processes {
for (proc_info, process_network_data) in &state.processes {
write_to_stdout(format!(
"process: <{}> \"{}\" up/down Bps: {}/{} connections: {}",
timestamp,
process,
proc_info.procname,
process_network_data.total_bytes_uploaded,
process_network_data.total_bytes_downloaded,
process_network_data.connection_count
Expand Down Expand Up @@ -129,7 +130,7 @@ where
}
pub fn update_state(
&mut self,
connections_to_procs: HashMap<LocalSocket, String>,
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
utilization: Utilization,
ip_to_host: HashMap<IpAddr, String>,
) {
Expand Down
57 changes: 31 additions & 26 deletions src/display/ui_state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::os::ProcessInfo;
use ::std::cmp;
use ::std::collections::{HashMap, HashSet, VecDeque};
use ::std::hash::Hash;
use ::std::iter::FromIterator;
use ::std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use ::std::net::{IpAddr, Ipv4Addr};
use core::hash::Hash;
use std::iter::FromIterator;

use crate::network::{Connection, LocalSocket, Utilization};

Expand Down Expand Up @@ -67,48 +68,44 @@ impl Bandwidth for ConnectionData {
}

pub struct UtilizationData {
connections_to_procs: HashMap<LocalSocket, String>,
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
network_utilization: Utilization,
}

#[derive(Default)]
pub struct UIState {
pub processes: Vec<(String, NetworkData)>,
pub processes: Vec<(ProcessInfo, NetworkData)>,
pub remote_addresses: Vec<(IpAddr, NetworkData)>,
pub connections: Vec<(Connection, ConnectionData)>,
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
pub cumulative_mode: bool,
utilization_data: VecDeque<UtilizationData>,
processes_map: HashMap<String, NetworkData>,
processes_map: HashMap<ProcessInfo, NetworkData>,
remote_addresses_map: HashMap<IpAddr, NetworkData>,
connections_map: HashMap<Connection, ConnectionData>,
}

impl UIState {
fn get_proc_name<'a>(
connections_to_procs: &'a HashMap<LocalSocket, String>,
fn get_proc_info<'a>(
connections_to_procs: &'a HashMap<LocalSocket, ProcessInfo>,
local_socket: &LocalSocket,
) -> Option<&'a String> {
if let Some(process_name) = connections_to_procs.get(local_socket) {
Some(process_name)
} else if let Some(process_name) = connections_to_procs.get(&LocalSocket {
) -> Option<&'a ProcessInfo> {
if let Some(proc_info) = connections_to_procs.get(local_socket) {
Some(&proc_info)
} else if let Some(proc_info) = connections_to_procs.get(&LocalSocket {
ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
port: local_socket.port,
protocol: local_socket.protocol,
}) {
Some(process_name)
Some(&proc_info)
} else {
connections_to_procs.get(&LocalSocket {
ip: IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
port: local_socket.port,
protocol: local_socket.protocol,
})
None
}
}
pub fn update(
&mut self,
connections_to_procs: HashMap<LocalSocket, String>,
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
network_utilization: Utilization,
) {
self.utilization_data.push_back(UtilizationData {
Expand All @@ -118,7 +115,7 @@ impl UIState {
if self.utilization_data.len() > RECALL_LENGTH {
self.utilization_data.pop_front();
}
let mut processes: HashMap<String, NetworkData> = HashMap::new();
let mut processes: HashMap<ProcessInfo, NetworkData> = HashMap::new();
let mut remote_addresses: HashMap<IpAddr, NetworkData> = HashMap::new();
let mut connections: HashMap<Connection, ConnectionData> = HashMap::new();
let mut total_bytes_downloaded: u128 = 0;
Expand All @@ -131,7 +128,7 @@ impl UIState {

for (connection, connection_info) in &network_utilization.connections {
let connection_previously_seen = !seen_connections.insert(connection);
let connection_data = connections.entry(connection.clone()).or_default();
let connection_data = connections.entry(*connection).or_default();
let data_for_remote_address = remote_addresses
.entry(connection.remote_socket.ip)
.or_default();
Expand All @@ -148,15 +145,23 @@ impl UIState {
total_bytes_downloaded += connection_info.total_bytes_downloaded;
total_bytes_uploaded += connection_info.total_bytes_uploaded;

let data_for_process = if let Some(process_name) =
UIState::get_proc_name(&connections_to_procs, &connection.local_socket)
let data_for_process = if let Some(proc_info) =
UIState::get_proc_info(&connections_to_procs, &connection.local_socket)
{
connection_data.process_name = process_name.clone();
processes.entry(process_name.clone()).or_default()
connection_data.process_name = proc_info.procname.clone();
processes
.entry(ProcessInfo {
procname: proc_info.procname.clone(),
pid: proc_info.pid,
})
.or_default()
} else {
connection_data.process_name = String::from("<UNKNOWN>");
processes
.entry(connection_data.process_name.clone())
.entry(ProcessInfo {
procname: connection_data.process_name.clone(),
pid: 0,
})
.or_default()
};

Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use network::{
Connection, LocalSocket, Sniffer, Utilization,
};
use os::OnSigWinch;
use os::ProcessInfo;

use ::pnet_bandwhich_fork::datalink::{DataLinkReceiver, NetworkInterface};
use ::std::collections::HashMap;
Expand Down Expand Up @@ -100,7 +101,7 @@ fn try_main() -> Result<(), failure::Error> {
}

pub struct OpenSockets {
sockets_to_procs: HashMap<LocalSocket, String>,
sockets_to_procs: HashMap<LocalSocket, ProcessInfo>,
connections: Vec<Connection>,
}

Expand Down
2 changes: 1 addition & 1 deletion src/network/dns/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl Client {
// Remove ips that are already being resolved
let ips = ips
.into_iter()
.filter(|ip| self.pending.lock().unwrap().insert(ip.clone()))
.filter(|ip| self.pending.lock().unwrap().insert(*ip))
.collect::<Vec<_>>();

if !ips.is_empty() {
Expand Down
30 changes: 24 additions & 6 deletions src/os/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ::std::collections::HashMap;
use ::procfs::process::FDTarget;

use crate::network::{Connection, Protocol};
use crate::os::shared::ProcessInfo;
use crate::OpenSockets;

pub(crate) fn get_open_sockets() -> OpenSockets {
Expand All @@ -13,10 +14,15 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
if let Ok(all_procs) = procfs::process::all_processes() {
for process in all_procs {
if let Ok(fds) = process.fd() {
let procname = process.stat.comm;
for fd in fds {
if let FDTarget::Socket(inode) = fd.target {
inode_to_procname.insert(inode, procname.clone());
inode_to_procname.insert(
inode,
ProcessInfo {
procname: process.stat.comm.clone(),
pid: process.stat.pid,
},
);
}
}
}
Expand All @@ -30,11 +36,17 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
for entry in tcp.into_iter() {
let local_port = entry.local_address.port();
let local_ip = entry.local_address.ip();
if let (connection, Some(procname)) = (
if let (connection, Some(proc_info)) = (
Connection::new(entry.remote_address, local_ip, local_port, Protocol::Tcp),
inode_to_procname.get(&entry.inode),
) {
open_sockets.insert(connection.local_socket, procname.clone());
open_sockets.insert(
connection.local_socket,
ProcessInfo {
procname: proc_info.procname.clone(),
pid: proc_info.pid,
},
);
connections.push(connection);
};
}
Expand All @@ -47,11 +59,17 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
for entry in udp.into_iter() {
let local_port = entry.local_address.port();
let local_ip = entry.local_address.ip();
if let (connection, Some(procname)) = (
if let (connection, Some(proc_info)) = (
Connection::new(entry.remote_address, local_ip, local_port, Protocol::Udp),
inode_to_procname.get(&entry.inode),
) {
open_sockets.insert(connection.local_socket, procname.clone());
open_sockets.insert(
connection.local_socket,
ProcessInfo {
procname: proc_info.procname.clone(),
pid: proc_info.pid,
},
);
connections.push(connection);
};
}
Expand Down
10 changes: 9 additions & 1 deletion src/os/lsof.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ::std::collections::HashMap;

use crate::network::Connection;
use crate::os::shared::ProcessInfo;
use crate::OpenSockets;

use super::lsof_utils;
Expand All @@ -13,6 +14,7 @@ struct RawConnection {
remote_port: String,
protocol: String,
process_name: String,
pid: i32,
}

pub(crate) fn get_open_sockets() -> OpenSockets {
Expand All @@ -31,7 +33,13 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
let socket_addr = SocketAddr::new(remote_ip, remote_port);
let connection = Connection::new(socket_addr, local_ip, local_port, protocol);

open_sockets.insert(connection.local_socket, raw_connection.process_name.clone());
open_sockets.insert(
connection.local_socket,
ProcessInfo {
procname: raw_connection.process_name.clone(),
pid: raw_connection.get_pid(),
},
);
connections_vec.push(connection);
}

Expand Down
9 changes: 8 additions & 1 deletion src/os/lsof_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct RawConnection {
remote_port: String,
protocol: String,
pub process_name: String,
process_id: String,
}

lazy_static! {
Expand All @@ -38,8 +39,8 @@ impl RawConnection {
return None;
}
let process_name = columns[0].replace("\\x20", " ");
let process_id = columns[1].to_owned();
// Unneeded
// let pid = columns[1];
// let username = columns[2];
// let fd = columns[3];

Expand Down Expand Up @@ -72,6 +73,7 @@ impl RawConnection {
remote_port,
protocol,
process_name,
process_id,
};
Some(connection)
} else if let Some(caps) = LISTEN_REGEX.captures(connection_str) {
Expand All @@ -95,6 +97,7 @@ impl RawConnection {
remote_port,
protocol,
process_name,
process_id,
};
Some(connection)
} else {
Expand All @@ -121,6 +124,10 @@ impl RawConnection {
pub fn get_local_port(&self) -> u16 {
self.local_port.parse::<u16>().unwrap()
}

pub fn get_pid(&self) -> i32 {
self.process_id.parse::<i32>().unwrap()
}
}

pub fn get_connections() -> RawConnections {
Expand Down
Loading