7 Commits

Author SHA1 Message Date
tmerritt 1106850cba permission issues. putting on hold. 2025-04-25 13:00:01 -04:00
tmerritt 4684b81ea1 adds ability to read hosts.txt if no file is specified on the command line, using defaults if hosts.txt is missing
compiles but doesnt actually ping the targets.  all come back as failure.
2025-04-25 11:49:11 -04:00
tmerritt e1b435bf7b more hosts and better naming for peterborough 2025-04-23 11:08:35 -04:00
tmerritt f57325bc6b Updates dependencies 2025-04-23 10:53:09 -04:00
tmerritt 613f22d31c Removes 'magic numbers' and uses constants for number of seconds in hour and day 2025-04-23 10:52:33 -04:00
tmerritt 002ad36068 Rolls 'duration_to_string' into pp 2025-04-22 16:09:49 -04:00
tmerritt 4ec95af6d0 optimized main loop
Adds colour to the text display
2025-04-22 16:03:30 -04:00
11 changed files with 1167 additions and 233 deletions
Generated
+916 -21
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -10,8 +10,13 @@ ansi_term = "0.12"
clap = { version = "4.5", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
csv = "1.3" csv = "1.3"
[[bin]] # Ratatui
name = "duration_to_string" color-eyre = "0.6.3"
crossterm = "0.28.1"
ratatui = "0.29.0"
# Internal Ping
ping-rs = "0.1"
[[bin]] [[bin]]
name = "pp" name = "pp"
+1
View File
@@ -1,3 +1,4 @@
address,name
10.3.100.1,Belleville Router 10.3.100.1,Belleville Router
10.11.31.3,Belleville VPN 11-31 10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32 10.12.32.1,Belleville VPN 12-32
+1
View File
@@ -1,3 +1,4 @@
address,name
10.3.100.1,Belleville Router 10.3.100.1,Belleville Router
10.11.31.3,Belleville VPN 11-31 10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32 10.12.32.1,Belleville VPN 12-32
+7 -6
View File
@@ -1,14 +1,15 @@
10.3.100.1,Belleville Router 10.3.100.1,Belleville Gateway
10.11.31.3,Belleville VPN 11-31 10.11.31.3,Belleville VPN 11-31
10.12.32.1,Belleville VPN 12-32 10.12.32.1,Belleville VPN 12-32
192.186.110.6,Belleville Cogeco
129.222.197.36,Belleville Starlink
10.2.100.1,Lindsay Gateway
24.143.184.98,Lindsay Cogeco
192.168.1.50,Lindsay Sign
10.11.21.1,Lindsay VPN 11-21 10.11.21.1,Lindsay VPN 11-21
10.12.22.1,Lindsay VPN 11-22 10.12.22.1,Lindsay VPN 11-22
8.8.8.8,Google DNS 8.8.8.8,Google DNS
1.1.1.1,1111 DNS 1.1.1.1,1111 DNS
192.168.0.1,Peterborough Gateway
24.51.235.162,Peterborough Cogeco 24.51.235.162,Peterborough Cogeco
99.214.0.92,Peterborough Rogers 99.214.0.92,Peterborough Rogers
216.234.202.122,Lindsay Starlink
24.143.184.98,Lindsay Cogeco
192.186.110.6,Belleville Cogeco
129.222.197.36,Belleville Starlink
192.168.0.50,Sign
-41
View File
@@ -1,41 +0,0 @@
use std::time::Duration;
pub fn duration_to_string(to_convert: Duration) -> String {
let mut total_seconds = to_convert.as_secs();
let mut working_string = String::new();
if total_seconds > 86400 {
// days
let num_days = total_seconds / 86400;
working_string = format!("{} days", num_days);
total_seconds = total_seconds - (num_days * 86400);
}
if total_seconds > 3600 {
// hours
let num_hours = total_seconds / 3600;
if num_hours > 0 {
working_string = format!("{} {} hours", working_string, num_hours);
total_seconds = total_seconds - (num_hours * 3600);
}
}
if total_seconds > 60 {
let num_minutes = total_seconds / 60;
if num_minutes > 0 {
working_string = format!("{} {} minutes", working_string, num_minutes);
total_seconds = total_seconds - (num_minutes * 60);
}
// minutes
}
working_string = format!("{} {} seconds.", working_string, total_seconds);
working_string
}
fn main() {
println!("1m 12s => {}", duration_to_string(Duration::from_secs(72)));
println!("1h 1m 12s => {}", duration_to_string(Duration::from_secs(3672)));
println!("1d 1h 1m 12s => {}", duration_to_string(Duration::from_secs(90072)));
println!("30d 1h 1m 12s => {}", duration_to_string(Duration::from_secs(2595672)));
}
+86 -23
View File
@@ -14,8 +14,52 @@ use pp::manager::Manager;
use pp::SECONDS_BETWEEN_DISPLAY; use pp::SECONDS_BETWEEN_DISPLAY;
use pp::target_state::TargetState; use pp::target_state::TargetState;
use std::{env, error::Error, ffi::OsString, process}; use std::{env, error::Error, ffi::OsString, process};
use color_eyre::owo_colors::OwoColorize;
use crossterm::style::Stylize;
use log::debug;
const SECONDS_IN_MINUTE: u32 = 60;
const SECONDS_IN_HOUR: u32 = SECONDS_IN_MINUTE * 60;
const SECONDS_IN_DAY: u32 = SECONDS_IN_HOUR * 24;
fn get_default_targets() -> BTreeMap<String, TargetState> { pub fn duration_to_string(to_convert: Duration) -> String {
let mut total_seconds = to_convert.as_secs() as u32;
let mut working_string = String::new();
if total_seconds > 86400 {
// days
let num_days = (total_seconds / SECONDS_IN_DAY) as u32;
working_string = format!("{} days", num_days);
total_seconds = total_seconds - (num_days * SECONDS_IN_DAY);
}
if total_seconds > 3600 {
// hours
let num_hours = (total_seconds / SECONDS_IN_HOUR) as u32;
if num_hours > 0 {
working_string = format!("{} {} hours", working_string, num_hours);
total_seconds = total_seconds - (num_hours * SECONDS_IN_HOUR);
}
}
if total_seconds > 60 {
let num_minutes = (total_seconds / SECONDS_IN_MINUTE) as u32;
if num_minutes > 0 {
working_string = format!("{} {} minutes", working_string, num_minutes);
total_seconds = total_seconds - (num_minutes * SECONDS_IN_MINUTE);
}
// minutes
}
working_string = format!("{} {} seconds", working_string, total_seconds);
working_string
}
struct PPState {}
impl PPState {
pub fn get_default_targets() -> BTreeMap<String, TargetState> {
let mut working = BTreeMap::new(); let mut working = BTreeMap::new();
working.insert("Localhost".to_string(), working.insert("Localhost".to_string(),
TargetState { TargetState {
@@ -72,21 +116,21 @@ fn get_default_targets() -> BTreeMap<String, TargetState> {
working working
} }
fn build_targets_from_file(filename: Option<PathBuf>) -> BTreeMap<String, TargetState> { pub fn build_targets_from_file(filename: Option<PathBuf>) -> BTreeMap<String, TargetState> {
let mut hosts: BTreeMap<String, TargetState> = get_default_targets(); PPState::get_default_targets();
if let Some(file) = filename { if let Some(file) = filename {
hosts = if !&file.exists() { let mut working = BTreeMap::new();
println!("Cant load hosts from {:?}. Using default host list.", file.clone().as_os_str()); if !&file.exists() {
debug!("Cant load hosts from {:?}. Using default host list.", file.clone().as_os_str());
// use // use
get_default_targets() PPState::get_default_targets()
} else { } else {
println!("LOADING HOSTS FROM {:?}", file.to_str()); debug!("LOADING HOSTS FROM {:?}", file.to_str());
let file = File::open(file); let file = File::open(file);
let mut rdr = csv::Reader::from_reader(file.unwrap()); let mut rdr = csv::Reader::from_reader(file.unwrap());
for result in rdr.records() { for result in rdr.records() {
let record = result.unwrap(); let record = result.unwrap();
hosts.insert(record[1].to_string(), working.insert(record[1].to_string(),
TargetState { TargetState {
name: record[1].to_string(), name: record[1].to_string(),
target: Ipv4Addr::from_str(&record[0]).unwrap(), target: Ipv4Addr::from_str(&record[0]).unwrap(),
@@ -95,13 +139,13 @@ fn build_targets_from_file(filename: Option<PathBuf>) -> BTreeMap<String, Target
last_rtt: 0, last_rtt: 0,
}); });
} }
hosts working
}
} else {
PPState::get_default_targets()
} }
} }
hosts
} }
fn ips_from_state(to_read_from: BTreeMap<String, TargetState>) -> Vec<Ipv4Addr> { fn ips_from_state(to_read_from: BTreeMap<String, TargetState>) -> Vec<Ipv4Addr> {
let mut working: Vec<Ipv4Addr> = vec![]; let mut working: Vec<Ipv4Addr> = vec![];
for current in to_read_from { for current in to_read_from {
@@ -124,7 +168,15 @@ fn main() {
let settings = AppSettings::parse(); let settings = AppSettings::parse();
print!("Prep to load targets..."); print!("Prep to load targets...");
let mut targets = build_targets_from_file(settings.ping_host_file); let file_to_check = match settings.ping_host_file {
None => {
PathBuf::from("./hosts.txt")
}
Some(actual) => {
actual
}
};
let mut targets = PPState::build_targets_from_file(Some(file_to_check));
// channel to send requests to ping // channel to send requests to ping
let (ping_response_sender, ping_response_listener) = mpsc::channel::<PingResult>(); let (ping_response_sender, ping_response_listener) = mpsc::channel::<PingResult>();
@@ -135,8 +187,10 @@ fn main() {
let mut display_loop_start = SystemTime::now(); let mut display_loop_start = SystemTime::now();
let mut duration_since_last_loop = SystemTime::now().duration_since(display_loop_start).unwrap(); let mut duration_since_last_loop = SystemTime::now().duration_since(display_loop_start).unwrap();
loop { loop {
let now = SystemTime::now();
if let Ok(response) = ping_response_listener.recv_timeout(Duration::from_millis(100)) { if let Ok(response) = ping_response_listener.recv_timeout(Duration::from_millis(100)) {
for (_, (name, current_state)) in targets.clone().iter().enumerate() { let local_targets = targets.clone();
for (_, (name, current_state)) in local_targets.iter().enumerate() {
if current_state.target == response.target { if current_state.target == response.target {
let last_alive_change = if response.success == current_state.alive { let last_alive_change = if response.success == current_state.alive {
current_state.last_alive_change current_state.last_alive_change
@@ -155,27 +209,36 @@ fn main() {
} }
} }
} }
duration_since_last_loop = SystemTime::now() duration_since_last_loop = now
.duration_since(display_loop_start) .duration_since(display_loop_start)
.expect("unable to figure out how long ago we displayed stuff"); .expect("unable to figure out how long ago we displayed stuff");
if duration_since_last_loop.as_secs() > SECONDS_BETWEEN_DISPLAY as u64 { if duration_since_last_loop.as_secs() > SECONDS_BETWEEN_DISPLAY as u64 {
println!("DISPLAY LOOP"); println!("DISPLAY LOOP");
println!("Host \t\t\t\t\t | Alive \t | RTT \t\t"); println!("Host \t\t\t\t\t | Alive \t | RTT \t\t");
for (name, current_result) in targets.clone() { for (name, current_result) in targets.clone() {
let mut target_string = String::new(); let time_since_last_change = now
let time_since_last_change = SystemTime::now().duration_since(current_result.last_alive_change).unwrap(); .duration_since(current_result.last_alive_change)
target_string = format!("{} ({})", name, current_result.target); .unwrap_or(Duration::from_secs(0));
let mut target_string = format!("{} ({})", name, current_result.target);
while target_string.len() < 34 { while target_string.len() < 34 {
target_string = format!("{} ", target_string); target_string.push(' ');
// target_string = format!("{} ", target_string);
} }
println!("{} \t | {} \t | {}\t | Changed {}s ago", target_string = if current_result.alive {
target_string.green().to_string()
} else {
target_string.red().to_string()
};
println!("{} \t | {} \t | {}\t | Changed {} ago",
target_string, target_string,
current_result.alive, current_result.alive,
current_result.last_rtt, time_since_last_change.as_secs() current_result.last_rtt,
duration_to_string(time_since_last_change)
); );
} }
display_loop_start = SystemTime::now(); display_loop_start = now;
} }
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@ pub mod manager;
pub mod ping_request; pub mod ping_request;
pub mod ping_result; pub mod ping_result;
pub mod target_state; pub mod target_state;
mod tui;
pub const SECONDS_BETWEEN_DISPLAY: u32 = 1; pub const SECONDS_BETWEEN_DISPLAY: u32 = 1;
pub const SECONDS_BETWEEN_PING: u32 = 2; pub const SECONDS_BETWEEN_PING: u32 = 2;
+18 -22
View File
@@ -1,17 +1,15 @@
use std::net::Ipv4Addr; use std::net::{IpAddr, Ipv4Addr};
use std::process::Command; use std::process::Command;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::thread; use std::thread;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use ping_rs::send_ping;
use crate::ping_request::PingRequest; use crate::ping_request::PingRequest;
use crate::ping_result::PingResult; use crate::ping_result::PingResult;
use crate::SECONDS_BETWEEN_PING; use crate::SECONDS_BETWEEN_PING;
pub struct Manager; pub struct Manager;
const WIN_PING_SUCCESS: i32 = 1;
const LIN_PING_SUCCESS: i32 = 0;
impl Manager { impl Manager {
pub fn spawn_manager_thread(targets: Vec<Ipv4Addr>, sender: Sender<PingResult>) { pub fn spawn_manager_thread(targets: Vec<Ipv4Addr>, sender: Sender<PingResult>) {
let local_targets = targets.clone(); let local_targets = targets.clone();
@@ -34,27 +32,25 @@ impl Manager {
let mut result = 0; let mut result = 0;
let mut success = true; let mut success = true;
let start_time = SystemTime::now(); let start_time = SystemTime::now();
#[cfg(any(target_os="windows"))] {
result = Command::new("c:/windows/system32/ping.exe").arg(request.target.to_string()) let target_address = IpAddr::from(request.target);
.arg("-n 1")
.arg(format!("-w {}", crate::SECONDS_BETWEEN_PING)) let options = ping_rs::PingOptions { ttl: 128, dont_fragment: false };
.arg("-4") let data = [1,2,3,4];
.output().unwrap().status.code().unwrap_or(255); let result = send_ping(&target_address, Duration::from_secs(10), &data, Some(&options));
success = result == WIN_PING_SUCCESS as i32;;
}
#[cfg(any(target_os="linux"))] {
result = Command::new("/usr/bin/ping").arg(request.target.to_string())
.arg("-c 1")
.arg("-4")
.arg(format!("-w {}", SECONDS_BETWEEN_PING))
.arg("-W 1")
.output().unwrap().status.code().unwrap_or(255);
success = result == LIN_PING_SUCCESS as i32;
}
let ping_duration = SystemTime::now() let ping_duration = SystemTime::now()
.duration_since(start_time) .duration_since(start_time)
.unwrap_or(Duration::from_secs(600)); .unwrap_or(Duration::from_secs(600));
let success = ping_duration.as_millis() >= SECONDS_BETWEEN_PING as u128 && success;
let success = match &result {
Ok(reply) => {
println!("Good reply -> {}/{}/{}", reply.address, data.len(), reply.rtt);
true
},
Err(e) => { println!("ERROR {:?}", e); false }
};
sender.send(PingResult { sender.send(PingResult {
target: local_request.target, target: local_request.target,
success, success,
+13
View File
@@ -1,5 +1,10 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::time::SystemTime; use std::time::SystemTime;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::{StatefulWidget, Style};
use ratatui::style::Color;
use ratatui::widgets::Widget;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TargetState { pub struct TargetState {
@@ -21,3 +26,11 @@ impl Default for TargetState {
} }
} }
} }
struct TargetStateWidget;
impl StatefulWidget for TargetStateWidget {
type State = TargetStateWidget;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
buf.set_string(area.left(),area.top(),"This is the string set ", Style::default().fg(Color::Red));
}
}
View File