client(show): refactor and add indicator of online-ness

pull/76/head
Jake McGinty 2021-05-10 00:03:00 +09:00
parent 88ae2aba17
commit 5671e3837d
1 changed files with 131 additions and 113 deletions

View File

@ -11,7 +11,7 @@ use std::{
fmt, fmt,
path::{Path, PathBuf}, path::{Path, PathBuf},
thread, thread,
time::Duration, time::{Duration, SystemTime},
}; };
use structopt::StructOpt; use structopt::StructOpt;
use wgctrl::{DeviceConfigBuilder, DeviceInfo, InterfaceName, PeerConfigBuilder, PeerInfo}; use wgctrl::{DeviceConfigBuilder, DeviceInfo, InterfaceName, PeerConfigBuilder, PeerInfo};
@ -23,6 +23,18 @@ use data_store::DataStore;
use shared::{wg, Error}; use shared::{wg, Error};
use util::{human_duration, human_size, Api}; use util::{human_duration, human_size, Api};
struct PeerState<'a> {
peer: &'a Peer,
info: Option<&'a PeerInfo>,
}
macro_rules! println_pad {
($pad:expr, $($arg:tt)*) => {
print!("{:pad$}", "", pad = $pad);
println!($($arg)*);
}
}
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
#[structopt(name = "innernet", about)] #[structopt(name = "innernet", about)]
struct Opts { struct Opts {
@ -714,17 +726,26 @@ fn show(short: bool, tree: bool, interface: Option<Interface>) -> Result<(), Err
let interfaces = let interfaces =
interface.map_or_else(DeviceInfo::enumerate, |interface| Ok(vec![*interface]))?; interface.map_or_else(DeviceInfo::enumerate, |interface| Ok(vec![*interface]))?;
let devices = interfaces.into_iter().filter_map(|name| { let devices = interfaces
DataStore::open(&name) .into_iter()
.and_then(|store| { .filter_map(|name| {
Ok(( DataStore::open(&name)
DeviceInfo::get_by_name(&name).with_str(name.as_str_lossy())?, .and_then(|store| {
store, Ok((
)) DeviceInfo::get_by_name(&name).with_str(name.as_str_lossy())?,
}) store,
.ok() ))
}); })
for (mut device_info, store) in devices { .ok()
})
.collect::<Vec<_>>();
if devices.is_empty() {
println!("No innernet networks currently running.");
return Ok(());
}
for (device_info, store) in devices {
let peers = store.peers(); let peers = store.peers();
let cidrs = store.cidrs(); let cidrs = store.cidrs();
let me = peers let me = peers
@ -732,45 +753,46 @@ fn show(short: bool, tree: bool, interface: Option<Interface>) -> Result<(), Err
.find(|p| p.public_key == device_info.public_key.as_ref().unwrap().to_base64()) .find(|p| p.public_key == device_info.public_key.as_ref().unwrap().to_base64())
.ok_or("missing peer info")?; .ok_or("missing peer info")?;
print_interface(&device_info, me, short)?; let mut peer_states = device_info
// Sort the peers by last handshake time (descending), .peers
// then by IP address (ascending) .iter()
device_info.peers.sort_by_key(|peer| { .map(|info| {
let our_peer = peers let public_key = info.config.public_key.to_base64();
.iter() match peers.iter().find(|p| p.public_key == public_key) {
.find(|p| p.public_key == peer.config.public_key.to_base64()) Some(peer) => Ok(PeerState {
.ok_or("missing peer info") peer,
.unwrap(); info: Some(info),
}),
( None => Err(format!("peer {} isn't an innernet peer.", public_key)),
std::cmp::Reverse(peer.stats.last_handshake_time), }
our_peer.ip, })
) .collect::<Result<Vec<PeerState>, _>>()?;
peer_states.push(PeerState {
peer: me,
info: None,
}); });
print_interface(&device_info, short || tree)?;
peer_states.sort_by_key(|peer| peer.peer.ip);
if tree { if tree {
let cidr_tree = CidrTree::new(cidrs); let cidr_tree = CidrTree::new(cidrs);
print_tree(&cidr_tree, &peers, 1); print_tree(&cidr_tree, &peer_states, 1);
} else { } else {
for peer in device_info.peers { for peer_state in peer_states {
let our_peer = peers print_peer(&peer_state, short, 1);
.iter()
.find(|p| p.public_key == peer.config.public_key.to_base64())
.ok_or("missing peer info")?;
print_peer(our_peer, &peer, short)?;
} }
} }
} }
Ok(()) Ok(())
} }
fn print_tree(cidr: &CidrTree, peers: &[Peer], level: usize) { fn print_tree(cidr: &CidrTree, peers: &[PeerState], level: usize) {
println!( println_pad!(
"{:pad$}{} {}", level * 2,
"", "{} {}",
cidr.cidr.to_string().bold().blue(), cidr.cidr.to_string().bold().blue(),
cidr.name.blue(), cidr.name.blue(),
pad = level * 2
); );
let mut children: Vec<_> = cidr.children().collect(); let mut children: Vec<_> = cidr.children().collect();
@ -779,92 +801,88 @@ fn print_tree(cidr: &CidrTree, peers: &[Peer], level: usize) {
.iter() .iter()
.for_each(|child| print_tree(&child, peers, level + 1)); .for_each(|child| print_tree(&child, peers, level + 1));
for peer in peers.iter().filter(|p| p.cidr_id == cidr.id) { for peer in peers.iter().filter(|p| p.peer.cidr_id == cidr.id) {
print_peer(peer, true, level);
}
}
fn print_interface(device_info: &DeviceInfo, short: bool) -> Result<(), Error> {
if short {
let listen_port_str = device_info
.listen_port
.map(|p| format!("(:{}) ", p))
.unwrap_or_default();
println!( println!(
"{:pad$}| {} {}", "{} {}",
"", device_info.name.to_string().green().bold(),
listen_port_str.dimmed(),
);
} else {
println!(
"{}: {}",
"network".green().bold(),
device_info.name.to_string().green(),
);
if let Some(listen_port) = device_info.listen_port {
println!(" {}: {}", "listening port".bold(), listen_port);
}
}
Ok(())
}
fn print_peer(peer: &PeerState, short: bool, level: usize) {
let pad = level * 2;
let PeerState { peer, info } = peer;
if short {
let last_handshake = info
.and_then(|i| i.stats.last_handshake_time)
.and_then(|t| t.elapsed().ok())
.unwrap_or_else(|| SystemTime::UNIX_EPOCH.elapsed().unwrap());
let online = last_handshake <= Duration::from_secs(150) || info.is_none();
println_pad!(
pad,
"| {} {}: {} ({}{}…)",
if online { "".bold() } else { "".dimmed() },
peer.ip.to_string().yellow().bold(), peer.ip.to_string().yellow().bold(),
peer.name.yellow(), peer.name.yellow(),
pad = level * 2 if info.is_none() { "you, " } else { "" },
); &peer.public_key[..6].dimmed(),
}
}
fn print_interface(device_info: &DeviceInfo, me: &Peer, short: bool) -> Result<(), Error> {
let public_key = device_info
.public_key
.as_ref()
.ok_or("interface has no private key set.")?
.to_base64();
if short {
println!("{}", device_info.name.to_string().green().bold());
println!(
" {} {}: {} ({}...)",
"(you)".bold(),
me.ip.to_string().yellow().bold(),
me.name.yellow(),
public_key[..10].dimmed()
); );
} else { } else {
println!( println_pad!(
"{}: {} ({}...)", pad,
"interface".green().bold(),
device_info.name.to_string().green(),
public_key[..10].yellow()
);
if !short {
if let Some(listen_port) = device_info.listen_port {
println!(" {}: {}", "listening_port".bold(), listen_port);
}
println!(" {}: {}", "ip".bold(), me.ip);
}
}
Ok(())
}
fn print_peer(our_peer: &Peer, peer: &PeerInfo, short: bool) -> Result<(), Error> {
if short {
println!(
" {}: {} ({}...)",
peer.config.allowed_ips[0]
.address
.to_string()
.yellow()
.bold(),
our_peer.name.yellow(),
&our_peer.public_key[..10].dimmed()
);
} else {
println!(
"{}: {} ({}...)", "{}: {} ({}...)",
"peer".yellow().bold(), "peer".yellow().bold(),
our_peer.name.yellow(), peer.name.yellow(),
&our_peer.public_key[..10].yellow() &peer.public_key[..10].yellow(),
); );
println!(" {}: {}", "ip".bold(), our_peer.ip); println_pad!(pad, " {}: {}", "ip".bold(), peer.ip);
if let Some(ref endpoint) = our_peer.endpoint { if let Some(ref endpoint) = peer.endpoint {
println!(" {}: {}", "endpoint".bold(), endpoint); println_pad!(pad, " {}: {}", "endpoint".bold(), endpoint);
} }
if let Some(last_handshake) = peer.stats.last_handshake_time { if let Some(info) = info {
let duration = last_handshake.elapsed()?; if let Some(last_handshake) = info.stats.last_handshake_time {
println!( let duration = last_handshake.elapsed().expect("horrible clock problem");
" {}: {}", println_pad!(
"last handshake".bold(), pad,
human_duration(duration), " {}: {}",
); "last handshake".bold(),
} human_duration(duration),
if peer.stats.tx_bytes > 0 || peer.stats.rx_bytes > 0 { );
println!( }
" {}: {} received, {} sent", if info.stats.tx_bytes > 0 || info.stats.rx_bytes > 0 {
"transfer".bold(), println_pad!(
human_size(peer.stats.rx_bytes), pad,
human_size(peer.stats.tx_bytes), " {}: {} received, {} sent",
); "transfer".bold(),
human_size(info.stats.rx_bytes),
human_size(info.stats.tx_bytes),
);
}
} }
} }
Ok(())
} }
fn main() { fn main() {