idk
This commit is contained in:
Generated
+1
@@ -1312,6 +1312,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
name = "wakey"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"color-eyre",
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2024"
|
||||
publish = ["gitea"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
axum = { version = "0.8.4", features = ["macros"] }
|
||||
axum-extra = { version = "0.10.1", features = ["query"] }
|
||||
color-eyre = "0.6.5"
|
||||
|
||||
@@ -19,17 +19,14 @@
|
||||
|
||||
// prefix seems to be a cidr. both 6 and 4 works. idfk dog
|
||||
|
||||
use std::{io, process::Output};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use super::AddrOutput;
|
||||
|
||||
pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
||||
let mut cmd = tokio::process::Command::new("ip");
|
||||
cmd.args(["-j", "address", "show"]);
|
||||
|
||||
if let Some(d) = dev {
|
||||
cmd.args(["dev", d]);
|
||||
}
|
||||
|
||||
let output = cmd.output().await?;
|
||||
let output = _get(dev).await.context("Can not run command")?;
|
||||
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(String::from_utf8_lossy(&output.stderr).into_owned());
|
||||
@@ -37,3 +34,13 @@ pub async fn get(dev: Option<&str>) -> anyhow::Result<Vec<AddrOutput>> {
|
||||
|
||||
Ok(serde_json::from_slice(&output.stdout)?)
|
||||
}
|
||||
pub async fn _get(dev: Option<&str>) -> io::Result<Output> {
|
||||
let mut cmd = tokio::process::Command::new("ip");
|
||||
cmd.args(["-j", "address", "show"]);
|
||||
|
||||
if let Some(d) = dev {
|
||||
cmd.args(["dev", d]);
|
||||
}
|
||||
|
||||
cmd.output().await
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! idk what to put here
|
||||
|
||||
use std::net::IpAddr;
|
||||
use std::{io, net::IpAddr, process::Output};
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::{Context, bail};
|
||||
|
||||
use super::{NUDState, NeighborItem};
|
||||
|
||||
@@ -19,6 +19,16 @@ pub async fn get(
|
||||
dev: Option<&str>,
|
||||
nud: &[NUDState],
|
||||
) -> anyhow::Result<Vec<NeighborItem>> {
|
||||
let output = _get(ip, dev, nud).await.context("Can not run command")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(String::from_utf8_lossy(&output.stderr).into_owned())
|
||||
} else {
|
||||
Ok(serde_json::from_slice(&output.stdout)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn _get(ip: Option<IpAddr>, dev: Option<&str>, nud: &[NUDState]) -> io::Result<Output> {
|
||||
let mut cmd = tokio::process::Command::new("ip");
|
||||
cmd.args(["-j", "neigh", "show"]);
|
||||
|
||||
@@ -34,11 +44,5 @@ pub async fn get(
|
||||
cmd.arg(nud.to_string());
|
||||
}
|
||||
|
||||
let output = cmd.output().await?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!(String::from_utf8_lossy(&output.stderr).into_owned())
|
||||
} else {
|
||||
Ok(serde_json::from_slice(&output.stdout)?)
|
||||
}
|
||||
cmd.output().await
|
||||
}
|
||||
|
||||
+52
-2
@@ -15,5 +15,55 @@ impl<'de> Deserialize<'de> for NUDState {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use crate::utils::parse::mac::{des_opm, ser_opm};
|
||||
use crate::arpparse::IpNeighLine;
|
||||
|
||||
use lda_ipjs::subcommands::neighbor::{self as ipjs_neigh, NeighborItem};
|
||||
|
||||
impl From<ipjs_neigh::NUDState> for NUDState {
|
||||
fn from(value: ipjs_neigh::NUDState) -> Self {
|
||||
match value {
|
||||
ipjs_neigh::NUDState::Permanent => NUDState::Permanent,
|
||||
ipjs_neigh::NUDState::Noarp => NUDState::Noarp,
|
||||
ipjs_neigh::NUDState::Reachable => NUDState::Reachable,
|
||||
ipjs_neigh::NUDState::Stale => NUDState::Stale,
|
||||
ipjs_neigh::NUDState::None => NUDState::None,
|
||||
ipjs_neigh::NUDState::Incomplete => NUDState::Incomplete,
|
||||
ipjs_neigh::NUDState::Delay => NUDState::Delay,
|
||||
ipjs_neigh::NUDState::Probe => NUDState::Probe,
|
||||
ipjs_neigh::NUDState::Failed => NUDState::Failed,
|
||||
ipjs_neigh::NUDState::Other(_) => NUDState::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NUDState> for ipjs_neigh::NUDState {
|
||||
fn from(value: NUDState) -> Self {
|
||||
match value {
|
||||
NUDState::Permanent => ipjs_neigh::NUDState::Permanent,
|
||||
NUDState::Noarp => ipjs_neigh::NUDState::Noarp,
|
||||
NUDState::Reachable => ipjs_neigh::NUDState::Reachable,
|
||||
NUDState::Stale => ipjs_neigh::NUDState::Stale,
|
||||
NUDState::None => ipjs_neigh::NUDState::None,
|
||||
NUDState::Incomplete => ipjs_neigh::NUDState::Incomplete,
|
||||
NUDState::Delay => ipjs_neigh::NUDState::Delay,
|
||||
NUDState::Probe => ipjs_neigh::NUDState::Probe,
|
||||
NUDState::Failed => ipjs_neigh::NUDState::Failed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NeighborItem> for IpNeighLine {
|
||||
fn from(item: NeighborItem) -> Self {
|
||||
IpNeighLine {
|
||||
ip: item.ip,
|
||||
dev: Some(item.dev),
|
||||
mac: item.mac,
|
||||
state: item
|
||||
.state
|
||||
.first()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.unwrap_or(NUDState::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -10,7 +10,7 @@
|
||||
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
|
||||
use impls::ser_opm;
|
||||
use crate::utils::parse::mac::ser_opm;
|
||||
use macaddr::MacAddr;
|
||||
use serde_with::skip_serializing_none;
|
||||
use strum::{Display, EnumString};
|
||||
@@ -41,7 +41,9 @@ pub struct IpNeighLine {
|
||||
|
||||
// NUDState custom Deserialize now lives in arpparse/impl.rs; use serde_with OneOrMany for Vec
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, EnumString, Display, Clone, Copy, Hash, serde::Serialize, Default)]
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, EnumString, Display, Clone, Copy, Hash, serde::Serialize, Default,
|
||||
)]
|
||||
#[strum(serialize_all = "UPPERCASE", ascii_case_insensitive)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NUDState {
|
||||
@@ -64,7 +66,6 @@ pub enum NUDState {
|
||||
/// address is not changed by this command.
|
||||
Stale,
|
||||
|
||||
|
||||
/// the neighbour entry has not (yet) been
|
||||
/// validated/resolved.
|
||||
Incomplete,
|
||||
@@ -88,7 +89,7 @@ pub enum NUDState {
|
||||
|
||||
impl NUDState {
|
||||
/// Argument form expected by `ip neigh ... nud <state>` (lowercase)
|
||||
pub const fn as_ip_neigh_arg(self) -> &'static str {
|
||||
pub const fn _as_ip_neigh_arg(self) -> &'static str {
|
||||
match self {
|
||||
NUDState::Permanent => "permanent",
|
||||
NUDState::Reachable => "reachable",
|
||||
@@ -182,7 +183,7 @@ impl IpNeighLine {
|
||||
Self { state, ..self }
|
||||
}
|
||||
*/
|
||||
pub fn with_dev(dev: impl Into<String>) -> impl FnMut(Self) -> Self {
|
||||
pub fn _with_dev(dev: impl Into<String>) -> impl FnMut(Self) -> Self {
|
||||
let dev = dev.into();
|
||||
move |self_| Self {
|
||||
dev: Some(dev.clone()),
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
pub(crate) async fn exec_command<S: AsRef<std::ffi::OsStr>>(
|
||||
cmd: S,
|
||||
args: impl IntoIterator<Item = S>,
|
||||
) -> io::Result<std::process::Output> {
|
||||
let mut u = tokio::process::Command::new(cmd);
|
||||
u.args(args);
|
||||
u.output().await
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Failure to resolve a host name to IPs.
|
||||
DnsResolve { name: String, source: io::Error },
|
||||
/// External command failed (e.g., ip neigh)
|
||||
CommandFailed {
|
||||
cmd: &'static str,
|
||||
args: Vec<String>,
|
||||
status: Option<i32>,
|
||||
stderr: String,
|
||||
},
|
||||
/// Generic IO error fallback
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::DnsResolve { name, source } => {
|
||||
write!(f, "DNS resolve failed for {name}: {source}")
|
||||
}
|
||||
Error::CommandFailed {
|
||||
cmd,
|
||||
args,
|
||||
status,
|
||||
stderr,
|
||||
} => {
|
||||
let code = status
|
||||
.map(|c| c.to_string())
|
||||
.unwrap_or_else(|| "signal".into());
|
||||
write!(f, "{cmd} {args:?} failed (status: {code}): {stderr}",)
|
||||
}
|
||||
Error::Io(e) => write!(f, "IO error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::DnsResolve { source, .. } => Some(source),
|
||||
Error::Io(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@
|
||||
// pub static LDA_MACS_2: LazyLock<[MacAddr; 2]> = LazyLock::new(|| LDA_MACS.map(MacAddr::from));
|
||||
pub mod wake;
|
||||
|
||||
pub mod cmd;
|
||||
pub mod error;
|
||||
/// generic so you can do "123.45.67.89:22" or "lda.lan:22" as an input
|
||||
// this is so bad
|
||||
pub mod ping;
|
||||
|
||||
+32
-49
@@ -1,20 +1,15 @@
|
||||
use macaddr::MacAddr;
|
||||
|
||||
use crate::arpparse::{self, IpNeighLine, NUDState};
|
||||
use crate::utils::{
|
||||
cmd::exec_command,
|
||||
error::{self, Result},
|
||||
};
|
||||
use crate::arpparse::{IpNeighLine, NUDState};
|
||||
use anyhow::{Context, Result};
|
||||
use lda_ipjs::subcommands::neighbor as ipjs_neigh;
|
||||
use std::collections::HashSet;
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub async fn get_ips(machine_name: &str) -> Result<impl Iterator<Item = IpAddr>> {
|
||||
Ok(tokio::net::lookup_host((machine_name, 0))
|
||||
.await
|
||||
.map_err(|e| error::Error::DnsResolve {
|
||||
name: machine_name.to_string(),
|
||||
source: e,
|
||||
})?
|
||||
.with_context(|| format!("DNS resolve failed for {machine_name}"))?
|
||||
.map(|c| c.ip()))
|
||||
}
|
||||
|
||||
@@ -45,15 +40,12 @@ pub async fn get_macs(
|
||||
macs: &[MacAddr],
|
||||
) -> Result<Vec<IpNeighLine>> {
|
||||
let mut ip_set: HashSet<IpAddr> = ips.iter().map(|ip| ip.to_canonical()).collect();
|
||||
let ip_m: HashSet<IpAddr> = futures::future::try_join_all(
|
||||
machine_names
|
||||
.iter()
|
||||
.map(|c| get_ips(c.as_ref())),
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let ip_m: HashSet<IpAddr> =
|
||||
futures::future::try_join_all(machine_names.iter().map(|c| get_ips(c.as_ref())))
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let ip_all = if ip_set.is_empty() && ip_m.is_empty() {
|
||||
None
|
||||
} else if ip_set.is_empty() {
|
||||
@@ -114,37 +106,28 @@ pub async fn get_mac(
|
||||
dev: Option<&str>,
|
||||
state: &[NUDState],
|
||||
) -> Result<Vec<IpNeighLine>> {
|
||||
let mut args: Vec<String> = vec!["neigh".into(), "show".into()];
|
||||
if let Some(ip) = ip {
|
||||
args.push("to".into());
|
||||
args.push(ip.to_string());
|
||||
}
|
||||
if let Some(d) = dev {
|
||||
args.push("dev".into());
|
||||
args.push(d.to_string());
|
||||
}
|
||||
for nud in state {
|
||||
args.push("nud".into());
|
||||
args.push(nud.as_ip_neigh_arg().into());
|
||||
}
|
||||
let cmd = "ip";
|
||||
let out = exec_command(cmd, args.iter().map(String::as_str).collect::<Vec<_>>()).await?;
|
||||
if !out.status.success() {
|
||||
return Err(error::Error::CommandFailed {
|
||||
cmd,
|
||||
args,
|
||||
status: out.status.code(),
|
||||
stderr: String::from_utf8_lossy(&out.stderr).into(),
|
||||
});
|
||||
}
|
||||
let lines = String::from_utf8_lossy(&out.stdout);
|
||||
let parsed = lines.lines().flat_map(arpparse::parse_ip_neigh_line);
|
||||
let rows: Vec<IpNeighLine> = if let Some(d) = dev {
|
||||
parsed.map(IpNeighLine::with_dev(d)).collect()
|
||||
} else {
|
||||
parsed.collect()
|
||||
};
|
||||
Ok(rows)
|
||||
let ipjs_states: Vec<ipjs_neigh::NUDState> = state.iter().copied().map(Into::into).collect();
|
||||
|
||||
let items = ipjs_neigh::json::get(ip, dev, &ipjs_states)
|
||||
.await
|
||||
.context("Calling ip -j neigh failed")?;
|
||||
|
||||
let lines = items
|
||||
.into_iter()
|
||||
.map(|item| IpNeighLine {
|
||||
ip: item.ip,
|
||||
dev: Some(item.dev),
|
||||
mac: item.mac,
|
||||
state: item
|
||||
.state
|
||||
.first()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.unwrap_or(NUDState::None),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(lines)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user