monitoring kit
Diffstat (limited to 'ping/src/ping.rs')
| -rw-r--r-- | ping/src/ping.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/ping/src/ping.rs b/ping/src/ping.rs new file mode 100644 index 0000000..ec9a055 --- /dev/null +++ b/ping/src/ping.rs @@ -0,0 +1,133 @@ +#![feature( + generic_arg_infer, + import_trait_associated_functions, + string_deref_patterns, + let_chains, + iter_array_chunks, + array_chunks, + portable_simd, + iter_chain +)] +use anyhow::*; +use grapher::{truncwrite, Grapher}; +use ping_rs::PingError; +use std::array; +use std::io::Write; +use std::io::{stdout, Read}; +use std::net::{IpAddr, ToSocketAddrs}; +use std::result::Result::Ok as Rok; +use std::thread::sleep; +use std::time::Duration; +use termion::color::*; +use termion::cursor::Hide; +use termion::raw::IntoRawMode; +use termion::screen::IntoAlternateScreen; +use termion::{async_stdin, clear, cursor, style}; +fn ping(x: IpAddr) -> Result<u32> { + match ping_rs::send_ping(&x, Duration::from_secs(2), b"", None) { + Rok(x) => Ok(x.rtt), + Err(PingError::TimedOut | PingError::IoPending) => Ok(!0), + Err(x) => bail!("{x:?}"), + } +} + +fn resolve(host: &str) -> Result<IpAddr> { + (host, 80) + .to_socket_addrs() + .context("resolve")? + .map(|x| x.ip()) + .next() + .ok_or(anyhow!("no resolution")) +} + +fn main() -> Result<()> { + let who = std::env::args().nth(1).ok_or(anyhow!("who to ping?"))?; + let ip = resolve(&who)?; + eprintln!("pinging {ip}"); + let _ = ping(ip)?; + let [mut max, mut sum, mut points, mut dropped] = [0; _]; + let mut min = !0; + + let (tx, rx) = std::sync::mpsc::channel::<u32>(); + let (ftx, frx) = oneshot::channel::<()>(); + std::thread::spawn(move || { + let result = ping(ip).unwrap(); + tx.send(result).unwrap(); + _ = ftx.send(()); + loop { + std::thread::sleep(Duration::from_millis(100u64.saturating_sub(result as _))); + let result = ping(ip).unwrap(); + tx.send(result).unwrap(); + } + }); + frx.recv()?; + + let mut g = Grapher::new()?; + + let mut stdout = stdout().into_raw_mode()?.into_alternate_screen()?; + let mut stdin = async_stdin(); + write!(stdout, "{}{}{}", Hide, clear::All, style::Reset).unwrap(); + 'out: loop { + let mut key = 0; + while stdin.read(array::from_mut(&mut key)).unwrap() != 0 { + match key { + b'q' => break 'out, + _ => (), + } + } + + let r = rx.try_recv(); + if let Rok(r) = r + && r != !0 + { + sum += r; + points += 1; + min = min.min(r); + max = max.max(r); + } else { + dropped += 1; + } + let avg = sum / points; + let mut r = r.unwrap_or(avg); + if r == !0 { + r = avg; + } + + let dat = g.data.iter().copied().filter(|&x| x != 0.0); + let sum = dat + .clone() + .filter(|&x| x != 0.0) + .map(|x| x - avg as f64) + .sum::<f64>(); + let dev = (sum / (dat.count()) as f64).abs(); + + g.push_point(r as f64); + g.draw( + |_| None, + |x| { + match max { + ..90 => x / 100.0, + ..240 => x / 250.0, + ..490 => x / 500.0, + ..990 => x / 1000.0, + _ => x / (max as f64 + 10.0), + } + .min(1.0) + }, + )?; + + write!(g.buffer, "{}{}", White.fg_str(), cursor::Goto(1, 1))?; + truncwrite!( + g.buffer, + " {who} ({ip}) ── last {r}ms ── min {min} avg {avg} max {max} dev ±{dev:.2}ms ── dropped {dropped} /{points}pckts ({:.1}%)", + (dropped as f64 /points as f64)*100.0, + )?; + write!(stdout, "{}", Rgb(40, 185, 119).fg_string())?; + stdout.write_all(&g.buffer)?; + stdout.flush()?; + + sleep(Duration::from_millis(100)); + } + println!("\x1B[?7l"); + Ok(()) +} |