summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2019-11-05 20:33:12 +0100
committerAram Drevekenin <aram@poor.dev>2019-11-05 20:33:12 +0100
commit53d165b2a033615e2cce7ab5cc42ff2bf85e50da (patch)
tree1506e6e227d2d2fe622a73539480ec38bfb61c2d /src
parentdb0f4d109403da54574a6469fe676f167c97e2c6 (diff)
feat(raw): machine friendly output
Diffstat (limited to 'src')
-rw-r--r--src/display/components/table.rs50
-rw-r--r--src/display/mod.rs2
-rw-r--r--src/display/raw_terminal_backend.rs52
-rw-r--r--src/display/ui.rs58
-rw-r--r--src/display/ui_state.rs14
-rw-r--r--src/main.rs141
-rw-r--r--src/network/connection.rs23
-rw-r--r--src/network/sniffer.rs4
-rw-r--r--src/network/utilization.rs4
-rw-r--r--src/os/linux.rs23
-rw-r--r--src/tests/cases/mod.rs2
-rw-r--r--src/tests/cases/raw_mode.rs1115
-rw-r--r--src/tests/cases/snapshots/raw_mode__basic_startup.snap5
-rw-r--r--src/tests/cases/snapshots/raw_mode__bi_directional_traffic.snap8
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_connections_from_remote_address.snap10
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_packets_of_traffic_from_different_connections.snap11
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_packets_of_traffic_from_single_connection.snap8
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_processes_with_multiple_connections.snap17
-rw-r--r--src/tests/cases/snapshots/raw_mode__no_resolve_mode.snap17
-rw-r--r--src/tests/cases/snapshots/raw_mode__one_packet_of_traffic.snap8
-rw-r--r--src/tests/cases/snapshots/raw_mode__one_process_with_multiple_connections.snap11
-rw-r--r--src/tests/cases/snapshots/raw_mode__sustained_traffic_from_multiple_processes.snap17
-rw-r--r--src/tests/cases/snapshots/raw_mode__sustained_traffic_from_multiple_processes_bi_directional.snap17
-rw-r--r--src/tests/cases/snapshots/raw_mode__sustained_traffic_from_one_process.snap11
-rw-r--r--src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap17
-rw-r--r--src/tests/cases/snapshots/ui__basic_startup.snap55
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height-2.snap34
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height.snap34
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height-2.snap34
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height.snap34
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_full_height-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_full_height.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_150_width_full_height-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_150_width_full_height.snap55
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_150_width_under_30_height-2.snap34
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_150_width_under_30_height.snap34
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_ip-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_ip.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections.snap55
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode.snap55
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic.snap55
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event.snap55
-rw-r--r--src/tests/cases/ui.rs1682
-rw-r--r--src/tests/fakes/fake_input.rs2
-rw-r--r--src/tests/mod.rs1373
-rw-r--r--src/tests/snapshots/tests__basic_startup.snap2
-rw-r--r--src/tests/snapshots/tests__bi_directional_traffic.snap2
-rw-r--r--src/tests/snapshots/tests__layout_under_150_width_full_height.snap2
-rw-r--r--src/tests/snapshots/tests__multiple_connections_from_remote_address-2.snap55
-rw-r--r--src/tests/snapshots/tests__multiple_connections_from_remote_address.snap55
-rw-r--r--src/tests/snapshots/tests__multiple_packets_of_traffic_from_different_connections.snap2
-rw-r--r--src/tests/snapshots/tests__multiple_packets_of_traffic_from_single_connection.snap2
-rw-r--r--src/tests/snapshots/tests__multiple_processes_with_multiple_connections.snap2
-rw-r--r--src/tests/snapshots/tests__no_resolve_mode-2.snap55
-rw-r--r--src/tests/snapshots/tests__no_resolve_mode.snap55
-rw-r--r--src/tests/snapshots/tests__one_packet_of_traffic.snap2
-rw-r--r--src/tests/snapshots/tests__one_process_with_multiple_connections.snap2
-rw-r--r--src/tests/snapshots/tests__traffic_with_winch_event.snap2
83 files changed, 5634 insertions, 1490 deletions
diff --git a/src/display/components/table.rs b/src/display/components/table.rs
index 6f22ea5..f59536e 100644
--- a/src/display/components/table.rs
+++ b/src/display/components/table.rs
@@ -7,7 +7,7 @@ use ::tui::terminal::Frame;
use ::tui::widgets::{Block, Borders, Row, Widget};
use crate::display::{Bandwidth, DisplayBandwidth, UIState};
-use crate::network::Connection;
+use crate::network::{display_connection_string, display_ip_or_host};
use ::std::net::Ipv4Addr;
use std::iter::FromIterator;
@@ -28,13 +28,6 @@ fn display_upload_and_download(bandwidth: &impl Bandwidth) -> String {
)
}
-fn display_ip_or_host(ip: Ipv4Addr, ip_to_host: &HashMap<Ipv4Addr, String>) -> String {
- match ip_to_host.get(&ip) {
- Some(host) => host.clone(),
- None => ip.to_string(),
- }
-}
-
fn sort_by_bandwidth<'a, T>(
list: &'a mut Vec<(T, &impl Bandwidth)>,
) -> &'a Vec<(T, &'a impl Bandwidth)> {
@@ -54,19 +47,6 @@ fn sort_by_bandwidth<'a, T>(
list
}
-fn display_connection_string(
- connection: &Connection,
- ip_to_host: &HashMap<Ipv4Addr, String>,
-) -> String {
- format!(
- ":{} => {}:{} ({})",
- connection.local_port,
- display_ip_or_host(connection.remote_socket.ip, ip_to_host),
- connection.remote_socket.port,
- connection.protocol,
- )
-}
-
pub struct Table<'a> {
title: &'a str,
column_names: &'a [&'a str],
@@ -119,29 +99,29 @@ impl<'a> Table<'a> {
rows: processes_rows,
}
}
- pub fn create_remote_ips_table(
+ pub fn create_remote_addresses_table(
state: &UIState,
ip_to_host: &HashMap<Ipv4Addr, String>,
) -> Self {
- let mut remote_ips_list = Vec::from_iter(&state.remote_ips);
- sort_by_bandwidth(&mut remote_ips_list);
- let remote_ips_rows = remote_ips_list
+ let mut remote_addresses_list = Vec::from_iter(&state.remote_addresses);
+ sort_by_bandwidth(&mut remote_addresses_list);
+ let remote_addresses_rows = remote_addresses_list
.iter()
- .map(|(remote_ip, data_for_remote_ip)| {
- let remote_ip = display_ip_or_host(**remote_ip, &ip_to_host);
+ .map(|(remote_address, data_for_remote_address)| {
+ let remote_address = display_ip_or_host(**remote_address, &ip_to_host);
vec![
- remote_ip,
- data_for_remote_ip.connection_count.to_string(),
- display_upload_and_download(*data_for_remote_ip),
+ remote_address,
+ data_for_remote_address.connection_count.to_string(),
+ display_upload_and_download(*data_for_remote_address),
]
})
.collect();
- let remote_ips_title = "Utilization by remote ip";
- let remote_ips_column_names = &["Remote Address", "Connection Count", "Rate Up/Down"];
+ let remote_addresses_title = "Utilization by remote address";
+ let remote_addresses_column_names = &["Remote Address", "Connection Count", "Rate Up/Down"];
Table {
- title: remote_ips_title,
- column_names: remote_ips_column_names,
- rows: remote_ips_rows,
+ title: remote_addresses_title,
+ column_names: remote_addresses_column_names,
+ rows: remote_addresses_rows,
}
}
pub fn render(&self, frame: &mut Frame<impl Backend>, rect: Rect) {
diff --git a/src/display/mod.rs b/src/display/mod.rs
index 8d24159..396b5d9 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -1,7 +1,9 @@
mod components;
mod ui;
mod ui_state;
+mod raw_terminal_backend;
pub use components::*;
pub use ui::*;
pub use ui_state::*;
+pub use raw_terminal_backend::*;
diff --git a/src/display/raw_terminal_backend.rs b/src/display/raw_terminal_backend.rs
new file mode 100644
index 0000000..3ae299a
--- /dev/null
+++ b/src/display/raw_terminal_backend.rs
@@ -0,0 +1,52 @@
+// this is a bit of a hack:
+// the TUI backend used by this app changes stdout to raw byte mode.
+// this is not desired when we do not use it (in our --raw mode),
+// since it makes writing to stdout overly complex
+//
+// so what we do here is provide a fake backend (RawTerminalBackend)
+// that implements the Backend TUI trait, but does nothing
+// this way, we don't need to create the TermionBackend
+// and thus skew our stdout when we don't need it
+use ::std::io;
+use ::tui::backend::Backend;
+use ::tui::buffer::Cell;
+use ::tui::layout::Rect;
+
+pub struct RawTerminalBackend {}
+
+impl Backend for RawTerminalBackend {
+ fn clear(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+
+ fn hide_cursor(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+
+ fn show_cursor(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+
+ fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
+ Ok((0, 0))
+ }
+
+ fn set_cursor(&mut self, _x: u16, _y: u16) -> io::Result<()> {
+ Ok(())
+ }
+
+ fn draw<'a, I>(&mut self, _content: I) -> io::Result<()>
+ where
+ I: Iterator<Item = (u16, u16, &'a Cell)>,
+ {
+ Ok(())
+ }
+
+ fn size(&self) -> io::Result<Rect> {
+ Ok(Rect::new(0, 0, 0, 0))
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
diff --git a/src/display/ui.rs b/src/display/ui.rs
index feacc5e..e23843e 100644
--- a/src/display/ui.rs
+++ b/src/display/ui.rs
@@ -5,10 +5,12 @@ use ::tui::Terminal;
use crate::display::components::{Layout, Table, TotalBandwidth};
use crate::display::UIState;
-use crate::network::{Connection, Utilization};
+use crate::network::{Connection, Utilization, display_connection_string, display_ip_or_host};
use ::std::net::Ipv4Addr;
+use chrono::prelude::*;
+
pub struct Ui<B>
where
B: Backend,
@@ -26,10 +28,52 @@ where
let mut terminal = Terminal::new(terminal_backend).unwrap();
terminal.clear().unwrap();
terminal.hide_cursor().unwrap();
- Ui {
- terminal,
- state: Default::default(),
- ip_to_host: Default::default(),
+ Ui {
+ terminal: terminal,
+ state: Default::default(),
+ ip_to_host: Default::default(),
+ }
+ }
+ pub fn output_text(&mut self, write_to_stdout: &mut Box<dyn FnMut(String) + Send>) {
+ let state = &self.state;
+ 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 {
+ write_to_stdout(
+ format!(
+ "process: <{}> \"{}\" up/down Bps: {}/{} connections: {}",
+ timestamp,
+ process,
+ process_network_data.total_bytes_uploaded,
+ process_network_data.total_bytes_downloaded,
+ process_network_data.connection_count
+ )
+ );
+ }
+ for (connection, connection_network_data) in &state.connections {
+ write_to_stdout(
+ format!(
+ "connection: <{}> {} up/down Bps: {}/{} process: \"{}\"",
+ timestamp,
+ display_connection_string(connection, ip_to_host),
+ connection_network_data.total_bytes_uploaded,
+ connection_network_data.total_bytes_downloaded,
+ connection_network_data.process_name
+ )
+ );
+ }
+ for (remote_address, remote_address_network_data) in &state.remote_addresses {
+ write_to_stdout(
+ format!(
+ "remote_address: <{}> {} up/down Bps: {}/{} connections: {}",
+ timestamp,
+ display_ip_or_host(*remote_address, ip_to_host),
+ remote_address_network_data.total_bytes_uploaded,
+ remote_address_network_data.total_bytes_downloaded,
+ remote_address_network_data.connection_count
+ )
+ );
}
}
pub fn draw(&mut self) {
@@ -40,11 +84,11 @@ where
let size = frame.size();
let connections = Table::create_connections_table(&state, &ip_to_host);
let processes = Table::create_processes_table(&state);
- let remote_ips = Table::create_remote_ips_table(&state, &ip_to_host);
+ let remote_addresses = Table::create_remote_addresses_table(&state, &ip_to_host);
let total_bandwidth = TotalBandwidth { state: &state };
let layout = Layout {
header: total_bandwidth,
- children: vec![processes, connections, remote_ips],
+ children: vec![processes, connections, remote_addresses],
};
layout.render(&mut frame, size);
})
diff --git a/src/display/ui_state.rs b/src/display/ui_state.rs
index ac60933..1bf235a 100644
--- a/src/display/ui_state.rs
+++ b/src/display/ui_state.rs
@@ -43,7 +43,7 @@ impl Bandwidth for NetworkData {
#[derive(Default)]
pub struct UIState {
pub processes: BTreeMap<String, NetworkData>,
- pub remote_ips: BTreeMap<Ipv4Addr, NetworkData>,
+ pub remote_addresses: BTreeMap<Ipv4Addr, NetworkData>,
pub connections: BTreeMap<Connection, ConnectionData>,
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
@@ -55,7 +55,7 @@ impl UIState {
network_utilization: Utilization,
) -> Self {
let mut processes: BTreeMap<String, NetworkData> = BTreeMap::new();
- let mut remote_ips: BTreeMap<Ipv4Addr, NetworkData> = BTreeMap::new();
+ let mut remote_addresses: BTreeMap<Ipv4Addr, NetworkData> = BTreeMap::new();
let mut connections: BTreeMap<Connection, ConnectionData> = BTreeMap::new();
let mut total_bytes_downloaded: u128 = 0;
let mut total_bytes_uploaded: u128 = 0;
@@ -63,7 +63,7 @@ impl UIState {
if let Some(connection_bandwidth_utilization) =
network_utilization.connections.get(&connection)
{
- let data_for_remote_ip = remote_ips.entry(connection.remote_socket.ip).or_default();
+ let data_for_remote_address