client(show): refactor and add indicator of online-ness
parent
88ae2aba17
commit
5671e3837d
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue