CI / Format and clippy (push) Successful in 1m13s
CI / Dependency audit (push) Successful in 13s
CI / Tests (Linux) (push) Successful in 1m35s
Release / Create release (push) Successful in 7s
CI / Check desktop app (Linux) (push) Successful in 3m19s
Release / CLI (linux-aarch64) (push) Successful in 1m3s
Release / CLI (linux-x86_64) (push) Successful in 1m28s
Release / Desktop app (linux-aarch64) (push) Successful in 7m16s
Release / Desktop app (linux-x86_64) (push) Successful in 10m17s
Release / Desktop app (windows) (push) Successful in 4m54s
Completes the English-only standardization for code, comments, scripts and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en, untouched). Covers the remaining crates/, apps/gui/ui/ and apps/gui/src-tauri/ comments, .gitea/ workflow and template comments, .githooks/ hooks, and the scripts/ source comments for the files already renamed in a prior commit (check.sh, generate-logo.py, install-hooks.sh). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
120 lines
3.3 KiB
Rust
120 lines
3.3 KiB
Rust
//! Network interface enumeration and 802.11 wireless phy identification.
|
|
//!
|
|
//! Reads directly from `/sys/class/net` on Linux, falling back to `iw` for
|
|
//! detailed interface mode inspection when available.
|
|
|
|
use crate::error::{Result, SabiaError};
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
#[derive(Debug, Clone, serde::Serialize)]
|
|
pub struct Interface {
|
|
pub name: String,
|
|
pub mac: String,
|
|
/// Wireless `wiphy` name (e.g. `"phy0"`), present only on 802.11 interfaces.
|
|
pub phy: Option<String>,
|
|
pub mode: Mode,
|
|
pub up: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
|
|
pub enum Mode {
|
|
Managed,
|
|
Monitor,
|
|
Unknown,
|
|
}
|
|
|
|
fn net_root() -> &'static Path {
|
|
Path::new("/sys/class/net")
|
|
}
|
|
|
|
/// Lists all network interfaces on the system (wireless interfaces have `phy.is_some()`).
|
|
pub fn list_all() -> Result<Vec<Interface>> {
|
|
let mut out = Vec::new();
|
|
for entry in fs::read_dir(net_root())? {
|
|
let entry = entry?;
|
|
let name = entry.file_name().to_string_lossy().into_owned();
|
|
if name == "lo" {
|
|
continue;
|
|
}
|
|
out.push(read_interface(&name)?);
|
|
}
|
|
out.sort_by(|a, b| a.name.cmp(&b.name));
|
|
Ok(out)
|
|
}
|
|
|
|
/// Lists only 802.11 wireless interfaces (`phy80211` present).
|
|
pub fn list_wireless() -> Result<Vec<Interface>> {
|
|
Ok(list_all()?
|
|
.into_iter()
|
|
.filter(|i| i.phy.is_some())
|
|
.collect())
|
|
}
|
|
|
|
pub fn get(name: &str) -> Result<Interface> {
|
|
let iface = read_interface(name)?;
|
|
if iface.phy.is_none() {
|
|
return Err(SabiaError::NotWireless(name.to_string()));
|
|
}
|
|
Ok(iface)
|
|
}
|
|
|
|
fn read_interface(name: &str) -> Result<Interface> {
|
|
let base: PathBuf = net_root().join(name);
|
|
if !base.exists() {
|
|
return Err(SabiaError::InterfaceNotFound(name.to_string()));
|
|
}
|
|
|
|
let mac = fs::read_to_string(base.join("address"))
|
|
.map(|s| s.trim().to_string())
|
|
.unwrap_or_default();
|
|
|
|
let up = fs::read_to_string(base.join("operstate"))
|
|
.map(|s| s.trim() == "up")
|
|
.unwrap_or(false);
|
|
|
|
let phy80211 = base.join("phy80211");
|
|
let phy = if phy80211.is_dir() {
|
|
fs::read_link(&phy80211)
|
|
.ok()
|
|
.and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned()))
|
|
.or_else(|| {
|
|
// On some kernels `phy80211` is a directory rather than a symlink;
|
|
// read the `"phyNN"` subdirectory inside it.
|
|
fs::read_dir(&phy80211)
|
|
.ok()
|
|
.and_then(|mut rd| rd.next())
|
|
.and_then(|e| e.ok())
|
|
.map(|e| e.file_name().to_string_lossy().into_owned())
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mode = if phy.is_some() {
|
|
read_wireless_mode(name)
|
|
} else {
|
|
Mode::Unknown
|
|
};
|
|
|
|
Ok(Interface {
|
|
name: name.to_string(),
|
|
mac,
|
|
phy,
|
|
mode,
|
|
up,
|
|
})
|
|
}
|
|
|
|
/// Reads the interface operating mode (`managed`/`monitor`) via `/sys/class/net/<if>/type`
|
|
/// and `iw dev <if> info` when available.
|
|
///
|
|
///
|
|
fn read_wireless_mode(name: &str) -> Mode {
|
|
match crate::netlink::iw_dev_type(name) {
|
|
Some(Mode::Monitor) => Mode::Monitor,
|
|
Some(Mode::Managed) => Mode::Managed,
|
|
_ => Mode::Unknown,
|
|
}
|
|
}
|