This commit is contained in:
lda committed 2026-04-05 02:34:17 +07:00
1 parent 0712e87e6f
commit 7eee1e0bab
9 files changed
+382 -68

No files matched your search

+2 -1
View File
@@ -171,7 +171,8 @@ mod tests {
vec![],
)],
};
let legacy = legacy_status_from_inventory(inventory, Some("pc".into()), DeviceFilters::default());
let legacy =
legacy_status_from_inventory(inventory, Some("pc".into()), DeviceFilters::default());
assert_eq!(legacy.name.as_deref(), Some("pc"));
assert_eq!(legacy.table.len(), 1);
assert_eq!(legacy.table[0].state, NeighborState::Reachable);
+104 -5
View File
@@ -1,7 +1,9 @@
use std::net::{IpAddr, SocketAddr};
use chrono::{DateTime, Local, Utc};
use clap::{Args, Parser, Subcommand};
use wakey_core::{DeviceFilters, DeviceQuery};
use comfy_table::{Cell, ContentArrangement, Table, presets::UTF8_FULL};
use wakey_core::{DeviceFilters, DeviceQuery, DhcpLeaseWithState, WakeResult};
#[derive(Parser)]
#[command(name = "wakey")]
@@ -32,11 +34,15 @@ struct HttpArgs {
struct LeasesArgs {
#[arg(long)]
include_state: bool,
#[arg(long)]
json: bool,
}
#[derive(Args)]
struct WakeArgs {
query: String,
#[arg(long)]
json: bool,
}
#[derive(Args)]
@@ -52,6 +58,8 @@ struct StatusArgs {
nuds: Vec<wakey_core::NeighborState>,
#[arg(long = "mac")]
macs: Vec<macaddr::MacAddr>,
#[arg(long)]
json: bool,
}
fn status_args_to_query(args: StatusArgs) -> wakey_core::DeviceQuery {
@@ -79,6 +87,81 @@ fn status_args_to_query(args: StatusArgs) -> wakey_core::DeviceQuery {
}
}
fn base_table() -> Table {
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.set_content_arrangement(ContentArrangement::Dynamic);
table
}
fn render_status_table(status: &wakey::StatusResponse) -> Table {
let mut table = base_table();
table.set_header(["IP", "MAC", "State", "IF"]);
for row in &status.table {
table.add_row([
Cell::new(row.ip),
Cell::new(row.mac.map(|m| m.to_string()).unwrap_or_default()),
Cell::new(format!("{:?}", row.state).to_lowercase()),
Cell::new(row.dev.clone().unwrap_or_default()),
]);
}
table
}
fn render_leases_table(leases: &[DhcpLeaseWithState]) -> Table {
let mut table = base_table();
table.set_header(["IP", "MAC", "Name", "Expires", "NUD"]);
for lease in leases {
let expires = format_epoch_local(lease.lease_line.expires_epoch);
table.add_row([
Cell::new(lease.lease_line.ip),
Cell::new(lease.lease_line.mac),
Cell::new(lease.lease_line.name.clone().unwrap_or_default()),
Cell::new(expires),
Cell::new(
lease
.nud_state
.map(|s| format!("{:?}", s).to_lowercase())
.unwrap_or_default(),
),
]);
}
table
}
fn render_wake_table(result: &WakeResult) -> Table {
let mut table = base_table();
table.set_header(["IP", "MAC", "Status"]);
for row in &result.result {
table.add_row([
Cell::new(row.target.ip.map(|ip| ip.to_string()).unwrap_or_default()),
Cell::new(row.target.mac.map(|m| m.to_string()).unwrap_or_default()),
Cell::new(format!("{:?}", row.status).to_lowercase()),
]);
}
table
}
fn render_devs_table(devs: &[String]) -> Table {
let mut table = base_table();
table.set_header(["Interface"]);
for dev in devs {
table.add_row([Cell::new(dev)]);
}
table
}
fn format_epoch_local(epoch: u64) -> String {
match DateTime::<Utc>::from_timestamp(epoch as i64, 0) {
Some(dt) => dt
.with_timezone(&Local)
.format("%Y-%m-%d %H:%M:%S")
.to_string(),
None => epoch.to_string(),
}
}
#[cfg(not(target_os = "linux"))]
fn main() -> anyhow::Result<()> {
Err(anyhow::anyhow!(
@@ -96,6 +179,7 @@ async fn main() -> anyhow::Result<()> {
wakey::serve_http_from_current_exe(addr).await?;
}
Command::Status(args) => {
let as_json = args.json;
let query = status_args_to_query(args);
let status = if query.name.is_some()
&& query.filter.ips.is_empty()
@@ -107,22 +191,37 @@ async fn main() -> anyhow::Result<()> {
} else {
wakey::get_status(query).await?
};
println!("{}", serde_json::to_string_pretty(&status)?);
if as_json {
println!("{}", serde_json::to_string_pretty(&status)?);
} else {
if let Some(name) = &status.name {
println!("name: {name}");
}
println!("{}", render_status_table(&status));
}
}
Command::Leases(args) => {
let leases = wakey::get_leases(wakey_core::LeaseQuery {
include_state: args.include_state,
})
.await?;
println!("{}", serde_json::to_string_pretty(&leases)?);
if args.json {
println!("{}", serde_json::to_string_pretty(&leases)?);
} else {
println!("{}", render_leases_table(&leases));
}
}
Command::Wake(args) => {
let result = wakey::wake_from_query(args.query).await?;
println!("{}", serde_json::to_string_pretty(&result)?);
if args.json {
println!("{}", serde_json::to_string_pretty(&result)?);
} else {
println!("{}", render_wake_table(&result));
}
}
Command::Devs => {
let devs = wakey::list_interfaces().await?;
println!("{}", serde_json::to_string_pretty(&devs)?);
println!("{}", render_devs_table(&devs));
}
}
Ok(())
+1 -2
View File
@@ -5,9 +5,8 @@
// [0xbc, 0x09, 0x1b, 0xec, 0x65, 0xd0],
// ];
/// is it time to lookup host lda.lan for this...
// is it time to lookup host lda.lan for this...
// pub static LDA_MACS_2: LazyLock<[MacAddr; 2]> = LazyLock::new(|| LDA_MACS.map(MacAddr::from));
pub mod wake;
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
// this is so bad
+48
View File
@@ -0,0 +1,48 @@
pub mod dev {
pub use wakey_linux::devices::devs_sorted;
}
pub mod leases {
pub use wakey_linux::dhcp::enrich_leases_with_nud_state;
}
pub mod parser {
pub use wakey_core::QueryInput as QueryType;
pub async fn parse_query(q: String) -> QueryType {
wakey_linux::devices::classify_query(q).await
}
}
pub use leases::*;
pub use macs::*;
pub mod macs {
use anyhow::Result;
use std::net::IpAddr;
use crate::arpparse::{IpNeighLine, NUDState};
pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> {
wakey_linux::devices::get_ips(machine_name).await
}
pub async fn get_macs(
machine_names: &[impl AsRef<str>],
ips: &[IpAddr],
devs: &[impl AsRef<str>],
state: &[NUDState],
macs: &[macaddr::MacAddr],
) -> Result<Vec<IpNeighLine>> {
wakey_linux::devices::get_neighbors(machine_names, ips, devs, state, macs).await
}
pub async fn get_mac(
ip: Option<IpAddr>,
dev: Option<&str>,
state: &[NUDState],
) -> Result<Vec<IpNeighLine>> {
let ips: Vec<IpAddr> = ip.into_iter().collect();
let devs: Vec<&str> = dev.into_iter().collect();
get_macs(&[] as &[&str], &ips, &devs, state, &[]).await
}
}
-28
View File
@@ -1,28 +0,0 @@
use anyhow::Result;
use std::net::IpAddr;
use crate::arpparse::{IpNeighLine, NUDState};
pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> {
wakey_linux::devices::get_ips(machine_name).await
}
pub async fn get_macs(
machine_names: &[impl AsRef<str>],
ips: &[IpAddr],
devs: &[impl AsRef<str>],
state: &[NUDState],
macs: &[macaddr::MacAddr],
) -> Result<Vec<IpNeighLine>> {
wakey_linux::devices::get_neighbors(machine_names, ips, devs, state, macs).await
}
pub async fn get_mac(
ip: Option<IpAddr>,
dev: Option<&str>,
state: &[NUDState],
) -> Result<Vec<IpNeighLine>> {
let ips: Vec<IpAddr> = ip.into_iter().collect();
let devs: Vec<&str> = dev.into_iter().collect();
get_macs(&[] as &[&str], &ips, &devs, state, &[]).await
}
-17
View File
@@ -1,17 +0,0 @@
pub mod dev {
pub use wakey_linux::devices::devs_sorted;
}
pub mod leases {
pub use wakey_linux::dhcp::enrich_leases_with_nud_state;
}
pub mod macs;
pub mod parser {
pub use wakey_core::QueryInput as QueryType;
pub async fn parse_query(q: String) -> QueryType {
wakey_linux::devices::classify_query(q).await
}
}
pub use leases::*;
pub use macs::*;
-6
View File
@@ -1,6 +0,0 @@
pub async fn wake_one(
sock: &tokio::net::UdpSocket,
t: wakey_linux::wake::CompleteWakeTarget,
) -> wakey_core::WakeTargetResult {
wakey_linux::wake::wake_one(sock, t).await
}