woah calm down blud

This commit is contained in:
lda
2026-04-06 03:01:15 +07:00 Unverified
parent 310b0b462d
commit 0c1e0e222b
4 changed files with 133 additions and 14 deletions
+7 -2
View File
@@ -11,8 +11,9 @@ use axum::Router;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use wakey_core::{ use wakey_core::{
Device, DeviceFilters, DeviceInventory, DeviceQuery, DhcpLease, DhcpLeaseWithState, LeaseQuery, Device, DeviceFilters, DeviceInventory, DeviceQuery, DhcpLease, DhcpLeaseWithState,
NeighborEntry, Presence, Query, QueryInput, Status, WakeResult, WakeTarget, InterfaceSummary, LeaseQuery, NeighborEntry, Presence, Query, QueryInput, Status, WakeResult,
WakeTarget,
}; };
pub type StatusResponse = Status<NeighborEntry>; pub type StatusResponse = Status<NeighborEntry>;
@@ -116,6 +117,10 @@ pub async fn list_interfaces() -> Result<Vec<String>> {
Ok(wakey_linux::devices::devs_sorted().await) Ok(wakey_linux::devices::devs_sorted().await)
} }
pub async fn get_interface_summaries() -> Result<Vec<InterfaceSummary>> {
wakey_linux::devices::list_interface_summaries().await
}
pub async fn get_ips(name: impl AsRef<str>) -> Result<Vec<std::net::IpAddr>> { pub async fn get_ips(name: impl AsRef<str>) -> Result<Vec<std::net::IpAddr>> {
Ok(wakey_linux::devices::get_ips(name.as_ref()) Ok(wakey_linux::devices::get_ips(name.as_ref())
.await? .await?
+60 -10
View File
@@ -2,8 +2,8 @@ use std::net::{IpAddr, SocketAddr};
use chrono::{DateTime, Local, Utc}; use chrono::{DateTime, Local, Utc};
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use comfy_table::{Cell, ContentArrangement, Table, presets::UTF8_FULL}; use comfy_table::{Cell, ContentArrangement, Table, presets};
use wakey_core::{DeviceFilters, DeviceQuery, DhcpLeaseWithState, WakeResult}; use wakey_core::{DeviceFilters, DeviceQuery, DhcpLeaseWithState, InterfaceSummary, WakeResult};
#[derive(Parser)] #[derive(Parser)]
#[command(name = "wakey")] #[command(name = "wakey")]
@@ -19,7 +19,7 @@ enum Command {
Status(StatusArgs), Status(StatusArgs),
Leases(LeasesArgs), Leases(LeasesArgs),
Wake(WakeArgs), Wake(WakeArgs),
Devs, Devs(DevsArgs),
} }
#[derive(Args)] #[derive(Args)]
@@ -62,6 +62,12 @@ struct StatusArgs {
json: bool, json: bool,
} }
#[derive(Args)]
struct DevsArgs {
#[arg(long)]
json: bool,
}
fn status_args_to_query(args: StatusArgs) -> wakey_core::DeviceQuery { fn status_args_to_query(args: StatusArgs) -> wakey_core::DeviceQuery {
if let Some(query) = args.query.as_ref() if let Some(query) = args.query.as_ref()
&& args.name.is_none() && args.name.is_none()
@@ -90,7 +96,7 @@ fn status_args_to_query(args: StatusArgs) -> wakey_core::DeviceQuery {
fn base_table() -> Table { fn base_table() -> Table {
let mut table = Table::new(); let mut table = Table::new();
table table
.load_preset(UTF8_FULL) .load_preset(presets::UTF8_FULL_CONDENSED)
.set_content_arrangement(ContentArrangement::Dynamic); .set_content_arrangement(ContentArrangement::Dynamic);
table table
} }
@@ -143,11 +149,51 @@ fn render_wake_table(result: &WakeResult) -> Table {
table table
} }
fn render_devs_table(devs: &[String]) -> Table { fn render_devs_table(devs: &[InterfaceSummary]) -> Table {
let mut table = base_table(); let mut table = base_table();
table.set_header(["Interface"]); table.set_header([
"Interface",
"State",
"MAC",
"Addresses",
"Broadcasts",
"Scope/Label",
]);
for dev in devs { for dev in devs {
table.add_row([Cell::new(dev)]); let addresses = dev
.addrs
.iter()
.filter_map(|addr| addr.cidr.as_deref())
.collect::<Vec<_>>()
.join("\n");
let broadcasts = dev
.addrs
.iter()
.filter_map(|addr| addr.broadcast)
.map(|addr| addr.to_string())
.collect::<Vec<_>>()
.join("\n");
let scope_label = dev
.addrs
.iter()
.map(|addr| match (&addr.scope, &addr.label) {
(Some(scope), Some(label)) => format!("{scope} ({label})"),
(Some(scope), None) => scope.clone(),
(None, Some(label)) => label.clone(),
(None, None) => String::new(),
})
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join("\n");
table.add_row([
Cell::new(&dev.ifname),
Cell::new(&dev.operstate),
Cell::new(dev.mac.map(|m| m.to_string()).unwrap_or_default()),
Cell::new(addresses),
Cell::new(broadcasts),
Cell::new(scope_label),
]);
} }
table table
} }
@@ -219,9 +265,13 @@ async fn main() -> anyhow::Result<()> {
println!("{}", render_wake_table(&result)); println!("{}", render_wake_table(&result));
} }
} }
Command::Devs => { Command::Devs(args) => {
let devs = wakey::list_interfaces().await?; let devs = wakey::get_interface_summaries().await?;
println!("{}", render_devs_table(&devs)); if args.json {
println!("{}", serde_json::to_string_pretty(&devs)?);
} else {
println!("{}", render_devs_table(&devs));
}
} }
} }
Ok(()) Ok(())
+21
View File
@@ -250,6 +250,27 @@ pub struct DeviceInventory {
pub devices: Vec<Device>, pub devices: Vec<Device>,
} }
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
pub struct InterfaceSummary {
pub ifindex: u32,
pub ifname: String,
pub operstate: String,
#[serde(with = "mac::option_mac")]
pub mac: Option<MacAddr>,
pub addrs: Vec<InterfaceAddr>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
pub struct InterfaceAddr {
pub family: Option<String>,
pub cidr: Option<String>,
pub broadcast: Option<std::net::Ipv4Addr>,
pub scope: Option<String>,
pub label: Option<String>,
}
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Eq)]
pub struct WakeTarget { pub struct WakeTarget {
+45 -2
View File
@@ -2,9 +2,11 @@ use anyhow::{Context, Result};
use futures::future::try_join_all; use futures::future::try_join_all;
use std::collections::HashSet; use std::collections::HashSet;
use std::net::IpAddr; use std::net::IpAddr;
use wakey_core::{DeviceQuery, NeighborEntry, NeighborState, QueryInput, parse}; use wakey_core::{
DeviceQuery, InterfaceAddr, InterfaceSummary, NeighborEntry, NeighborState, QueryInput, parse,
};
use lda_ipjs::subcommands::neighbor; use lda_ipjs::subcommands::{address, neighbor};
pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> { pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> {
Ok(tokio::net::lookup_host((machine_name, 0)) Ok(tokio::net::lookup_host((machine_name, 0))
@@ -159,6 +161,47 @@ pub async fn devs_sorted() -> Vec<String> {
v v
} }
pub async fn list_interface_summaries() -> Result<Vec<InterfaceSummary>> {
#[cfg(unix)]
let rows = address::nl::get(None)
.await
.context("rtnetlink address query failed")?;
#[cfg(not(unix))]
let rows = address::get_with_backend(address::Backend::Json, None)
.await
.context("ip -j address show failed")?;
let mut out: Vec<InterfaceSummary> = rows
.into_iter()
.filter(|row| row.ifname != "lo")
.map(|row| InterfaceSummary {
ifindex: row.ifindex,
ifname: row.ifname,
operstate: row.operstate.as_str().to_ascii_lowercase(),
mac: row.address,
addrs: row
.addr_info
.into_iter()
.map(|info| InterfaceAddr {
family: info.family.map(|family| family.as_str().to_string()),
cidr: info
.cidr
.local
.zip(info.cidr.prefixlen)
.map(|(addr, prefixlen)| format!("{addr}/{prefixlen}")),
broadcast: info.broadcast,
scope: info.scope,
label: info.label,
})
.collect(),
})
.collect();
out.sort_by(|a, b| a.ifname.cmp(&b.ifname));
Ok(out)
}
pub async fn has_dev(name: &str) -> bool { pub async fn has_dev(name: &str) -> bool {
list_devs().await.contains(name) list_devs().await.contains(name)
} }