diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/display/components/total_bandwidth.rs | 27 | ||||
-rw-r--r-- | src/display/ui.rs | 7 | ||||
-rw-r--r-- | src/main.rs | 16 | ||||
-rw-r--r-- | src/network/dns/client.rs | 6 | ||||
-rw-r--r-- | src/network/dns/resolver.rs | 13 | ||||
-rw-r--r-- | src/os/shared.rs | 30 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__pause_by_space-2.snap | 55 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__pause_by_space-3.snap | 55 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__pause_by_space-4.snap | 55 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__pause_by_space-5.snap | 55 | ||||
-rw-r--r-- | src/tests/cases/snapshots/ui__pause_by_space.snap | 55 | ||||
-rw-r--r-- | src/tests/cases/test_utils.rs | 17 | ||||
-rw-r--r-- | src/tests/cases/ui.rs | 55 | ||||
-rw-r--r-- | src/tests/fakes/fake_input.rs | 17 |
14 files changed, 413 insertions, 50 deletions
diff --git a/src/display/components/total_bandwidth.rs b/src/display/components/total_bandwidth.rs index cd6a6f2..ba78d2d 100644 --- a/src/display/components/total_bandwidth.rs +++ b/src/display/components/total_bandwidth.rs @@ -8,18 +8,29 @@ use crate::display::{DisplayBandwidth, UIState}; pub struct TotalBandwidth<'a> { pub state: &'a UIState, + pub paused: bool, } impl<'a> TotalBandwidth<'a> { pub fn render(&self, frame: &mut Frame<impl Backend>, rect: Rect) { - let title_text = [Text::styled( - format!( - " Total Rate Up / Down: {} / {}", - DisplayBandwidth(self.state.total_bytes_uploaded as f64), - DisplayBandwidth(self.state.total_bytes_downloaded as f64) - ), - Style::default().fg(Color::Green).modifier(Modifier::BOLD), - )]; + let title_text = { + let paused_str = if self.paused { "[PAUSED]" } else { "" }; + let color = if self.paused { + Color::Yellow + } else { + Color::Green + }; + + [Text::styled( + format!( + " Total Rate Up / Down: {} / {} {}", + DisplayBandwidth(self.state.total_bytes_uploaded as f64), + DisplayBandwidth(self.state.total_bytes_downloaded as f64), + paused_str + ), + Style::default().fg(color).modifier(Modifier::BOLD), + )] + }; Paragraph::new(title_text.iter()) .block(Block::default().borders(Borders::NONE)) .alignment(Alignment::Left) diff --git a/src/display/ui.rs b/src/display/ui.rs index dd8ecfb..49b6721 100644 --- a/src/display/ui.rs +++ b/src/display/ui.rs @@ -74,7 +74,7 @@ where )); } } - pub fn draw(&mut self) { + pub fn draw(&mut self, paused: bool) { let state = &self.state; let ip_to_host = &self.ip_to_host; self.terminal @@ -83,7 +83,10 @@ where let connections = Table::create_connections_table(&state, &ip_to_host); let processes = Table::create_processes_table(&state); let remote_addresses = Table::create_remote_addresses_table(&state, &ip_to_host); - let total_bandwidth = TotalBandwidth { state: &state }; + let total_bandwidth = TotalBandwidth { + state: &state, + paused, + }; let layout = Layout { header: total_bandwidth, children: vec![processes, connections, remote_addresses], diff --git a/src/main.rs b/src/main.rs index ea40de0..30a9b25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,6 +100,7 @@ where B: Backend + Send + 'static, { let running = Arc::new(AtomicBool::new(true)); + let paused = Arc::new(AtomicBool::new(false)); let mut active_threads = vec![]; @@ -121,11 +122,12 @@ where .name("resize_handler".to_string()) .spawn({ let ui = ui.clone(); + let paused = paused.clone(); move || { on_winch({ Box::new(move || { let mut ui = ui.lock().unwrap(); - ui.draw(); + ui.draw(paused.load(Ordering::SeqCst)); }) }); } @@ -138,6 +140,7 @@ where .name("display_handler".to_string()) .spawn({ let running = running.clone(); + let paused = paused.clone(); let network_utilization = network_utilization.clone(); move || { while running.load(Ordering::Acquire) { @@ -160,11 +163,14 @@ where } { let mut ui = ui.lock().unwrap(); - ui.update_state(sockets_to_procs, utilization, ip_to_host); + let paused = paused.load(Ordering::SeqCst); + if !paused { + ui.update_state(sockets_to_procs, utilization, ip_to_host); + } if raw_mode { ui.output_text(&mut write_to_stdout); } else { - ui.draw(); + ui.draw(paused); } } let render_duration = render_start_time.elapsed(); @@ -195,6 +201,10 @@ where display_handler.unpark(); break; } + Event::Key(Key::Char(' ')) => { + paused.fetch_xor(true, Ordering::SeqCst); + display_handler.unpark(); + } _ => (), }; } diff --git a/src/network/dns/client.rs b/src/network/dns/client.rs index 1f3fe17..f9d12ed 100644 --- a/src/network/dns/client.rs +++ b/src/network/dns/client.rs @@ -1,7 +1,6 @@ use crate::network::dns::{resolver::Lookup, IpTable}; use std::{ collections::HashSet, - future::Future, net::Ipv4Addr, sync::{Arc, Mutex}, thread::{Builder, JoinHandle}, @@ -23,14 +22,12 @@ pub struct Client { } impl Client { - pub fn new<R, B>(resolver: R, background: B) -> Result<Self, failure::Error> + pub fn new<R>(resolver: R, mut runtime: Runtime) -> Result<Self, failure::Error> where R: Lookup + Send + Sync + 'static, - B: Future<Output = ()> + Send + 'static, { let cache = Arc::new(Mutex::new(IpTable::new())); let pending = Arc::new(Mutex::new(PendingAddrs::new())); - let mut runtime = Runtime::new()?; let (tx, mut rx) = mpsc::channel::<Vec<Ipv4Addr>>(CHANNEL_SIZE); let handle = Builder::new().name("resolver".into()).spawn({ @@ -39,7 +36,6 @@ impl Client { move || { runtime.block_on(async { let resolver = Arc::new(resolver); - tokio::spawn(background); while let Some(ips) = rx.recv().await { for ip in ips { diff --git a/src/network/dns/resolver.rs b/src/network/dns/resolver.rs index 69f6308..007c485 100644 --- a/src/network/dns/resolver.rs +++ b/src/network/dns/resolver.rs @@ -1,18 +1,19 @@ use async_trait::async_trait; -use std::{future::Future, net::Ipv4Addr}; -use trust_dns_resolver::{error::ResolveErrorKind, AsyncResolver}; +use std::net::Ipv4Addr; +use tokio::runtime::Handle; +use trust_dns_resolver::{error::ResolveErrorKind, TokioAsyncResolver}; #[async_trait] pub trait Lookup { async fn lookup(&self, ip: Ipv4Addr) -> Option<String>; } -pub struct Resolver(AsyncResolver); +pub struct Resolver(TokioAsyncResolver); impl Resolver { - pub fn new() -> Result<(Self, impl Future<Output = ()>), failure::Error> { - let (resolver, background) = AsyncResolver::from_system_conf()?; - Ok((Self(resolver), background)) + pub async fn new(runtime: Handle) -> Result<Self, failure::Error> { + let resolver = TokioAsyncResolver::from_system_conf(runtime).await?; + Ok(Self(resolver)) } } diff --git a/src/os/shared.rs b/src/os/shared.rs index ec6a294..55a8553 100644 --- a/src/os/shared.rs +++ b/src/os/shared.rs @@ -4,6 +4,7 @@ use ::pnet_bandwhich_fork::datalink::{self, Config, NetworkInterface}; use ::std::io::{self, stdin, Write}; use ::termion::event::Event; use ::termion::input::TermRead; +use ::tokio::runtime::Runtime; use ::std::io::ErrorKind; use ::std::time; @@ -110,9 +111,7 @@ pub fn get_input( for iface in network_frames { if let Some(iface_error) = iface.err() { if let ErrorKind::PermissionDenied = iface_error.kind() { - failure::bail!( - "Insufficient permissions to listen on network interface(s). Try running with sudo.", - ) + failure::bail!(eperm_message()) } } } @@ -123,8 +122,9 @@ pub fn get_input( let write_to_stdout = create_write_to_stdout(); let (on_winch, cleanup) = sigwinch(); let dns_client = if resolve { - let (resolver, background) = dns::Resolver::new()?; - let dns_client = dns::Client::new(resolver, background)?; + let mut runtime = Runtime::new()?; + let resolver = runtime.block_on(dns::Resolver::new(runtime.handle().clone()))?; + let dns_client = dns::Client::new(resolver, runtime)?; Some(dns_client) } else { None @@ -141,3 +141,23 @@ pub fn get_input( write_to_stdout, }) } + +#[inline] +#[cfg(target_os = "macos")] +fn eperm_message() -> &'static str { + "Insufficient permissions to listen on network interface(s). Try running with sudo." +} + +#[inline] +#[cfg(target_os = "linux")] +fn eperm_message() -> &'static str { + r#" + Insufficient permissions to listen on network interface(s). You can work around + this issue like this: + + * Try running `bandwhich` with `sudo` + + * Build a `setcap(8)` wrapper for `bandwhich` with the following rules: + `cap_net_raw,cap_net_admin+ep` + "# +} diff --git a/src/tests/cases/snapshots/ui__pause_by_space-2.snap b/src/tests/cases/snapshots/ui__pause_by_space-2.snap new file mode 100644 index 0000000..204efd1 --- /dev/null +++ b/src/tests/cases/snapshots/ui__pause_by_space-2.snap @@ -0,0 +1,55 @@ +--- +source: src/tests/cases/ui.rs +expression: "&terminal_draw_events_mirror[1]" +--- + Total Rate Up / Down: 0Bps / 0Bps [PAUSED] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/cases/snapshots/ui__pause_by_space-3.snap b/src/tests/cases/snapshots/ui__pause_by_space-3.snap new file mode 100644 index 0000000..d710afb --- /dev/null +++ b/src/tests/cases/snapshots/ui__pause_by_space-3.snap @@ -0,0 +1,55 @@ +--- +source: src/tests/cases/ui.rs +expression: "&terminal_draw_events_mirror[2]" +--- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/cases/snapshots/ui__pause_by_space-4.snap b/src/tests/cases/snapshots/ui__pause_by_space-4.snap new file mode 100644 index 0000000..5488196 --- /dev/null +++ b/src/tests/cases/snapshots/ui__pause_by_space-4.snap @@ -0,0 +1,55 @@ +--- +source: src/tests/cases/ui.rs +expression: "&terminal_draw_events_mirror[3]" +--- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/cases/snapshots/ui__pause_by_space-5.snap b/src/tests/cases/snapshots/ui__pause_by_space-5.snap new file mode 100644 index 0000000..1524b66 --- /dev/null +++ b/src/tests/cases/snapshots/ui__pause_by_space-5.snap @@ -0,0 +1,55 @@ +--- +source: src/tests/cases/ui.rs +expression: "&terminal_draw_events_mirror[4]" +--- + Total Rate Up / Down: 0Bps / 0Bps + + + + + + + + + |