summaryrefslogtreecommitdiffstats
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
parentdb0f4d109403da54574a6469fe676f167c97e2c6 (diff)
feat(raw): machine friendly output
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-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
85 files changed, 5638 insertions, 1490 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7f4168c..09fb45a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1217,6 +1217,7 @@ name = "what"
version = "0.1.0"
dependencies = [
"cargo-insta 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"dns-lookup 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"insta 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1225,6 +1226,7 @@ dependencies = [
"pnet 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pnet_base 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"procfs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index c1f04e2..66facc2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,9 +22,11 @@ structopt = "0.3"
dns-lookup = "1.0.1"
signal-hook = "0.1.10"
failure = "0.1.6"
+chrono = "0.4"
[dev-dependencies]
insta = "0.11.0"
cargo-insta = "0.11.0"
packet-builder = "0.3.0"
pnet_base = "0.22.0"
+regex = "1"
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(&s