Release v2.1.0: backend test seam, captive-portal detection, JSON status, robustness
Features: - Introduce a Backend trait + System impl so flow/status/watch can be unit tested against a fake; add 11 connect-state-machine tests. - Captive-portal detection: status::connectivity returns Online/Portal/Offline; surfaced in status, JSON, connect notes, and a dedicated watch state. - `status --json` for bars/scripts; `profile add`/`profile remove`; detect now scores by number of in-range markers. Robustness: - Pin LC_ALL=C/LANG=C on child processes for locale-independent parsing. - Atomic config/state writes (temp + rename); 0600 config never world-readable. - Transient PSK file written to $XDG_RUNTIME_DIR when available. Fixes (from prior audit): - Feed Wi-Fi PSK to nmcli via stdin/passwd-file, never argv. - mask() no longer panics on multi-byte passwords. - Connectivity check requires HTTP 204 (no captive-portal false positives). - nmcli NAME,TYPE parsing handles escaped colons. - Strip CIDR suffix from displayed IP; PKGBUILD/Cargo version aligned (2.1.0).
This commit is contained in:
parent
8aceab7857
commit
d3c1e19ba3
17 changed files with 1662 additions and 217 deletions
80
src/backend.rs
Normal file
80
src/backend.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//! The seam between breadcrumbs' decision logic and the outside world.
|
||||
//!
|
||||
//! Every interaction with NetworkManager, Tailscale, connectivity probes,
|
||||
//! notifications and logging goes through the [`Backend`] trait. Production code
|
||||
//! uses [`System`], which delegates to the `nm`/`tailscale`/`status`/`notify`
|
||||
//! modules that shell out. Tests inject a fake so the connect state machine
|
||||
//! (`flow`) and watch classifier can be exercised without touching the host.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::config::{Config, NetworkDef};
|
||||
use crate::nm;
|
||||
use crate::notify::{self, Urgency};
|
||||
use crate::status::{self, Connectivity};
|
||||
use crate::tailscale::{self, TsHealth};
|
||||
|
||||
pub trait Backend {
|
||||
fn wifi_interface(&self) -> Option<String>;
|
||||
fn radio_on(&self);
|
||||
fn rescan(&self, iface: &str, ssids: &[String]);
|
||||
fn visible_ssids(&self, iface: &str) -> HashSet<String>;
|
||||
fn active_ssid(&self, iface: &str) -> Option<String>;
|
||||
fn ipv4(&self, iface: &str) -> Option<String>;
|
||||
fn device_connected(&self, iface: &str) -> bool;
|
||||
fn connect(&self, iface: &str, net: &NetworkDef, wait: u32, dns: &str) -> Result<(), String>;
|
||||
fn tailscale_installed(&self) -> bool;
|
||||
fn ensure_exit_node(&self, node: &str) -> TsHealth;
|
||||
fn tailscale_check(&self, node: &str) -> TsHealth;
|
||||
fn connectivity(&self, cfg: &Config) -> Connectivity;
|
||||
fn notify(&self, summary: &str, body: &str, urgency: Urgency);
|
||||
fn log(&self, line: &str);
|
||||
}
|
||||
|
||||
/// The real backend: every method delegates to the system-facing modules.
|
||||
pub struct System;
|
||||
|
||||
impl Backend for System {
|
||||
fn wifi_interface(&self) -> Option<String> {
|
||||
nm::wifi_interface()
|
||||
}
|
||||
fn radio_on(&self) {
|
||||
nm::radio_on()
|
||||
}
|
||||
fn rescan(&self, iface: &str, ssids: &[String]) {
|
||||
nm::rescan(iface, ssids)
|
||||
}
|
||||
fn visible_ssids(&self, iface: &str) -> HashSet<String> {
|
||||
nm::visible_ssids(iface)
|
||||
}
|
||||
fn active_ssid(&self, iface: &str) -> Option<String> {
|
||||
nm::active_ssid(iface)
|
||||
}
|
||||
fn ipv4(&self, iface: &str) -> Option<String> {
|
||||
status::ipv4(iface)
|
||||
}
|
||||
fn device_connected(&self, iface: &str) -> bool {
|
||||
nm::device_connected(iface)
|
||||
}
|
||||
fn connect(&self, iface: &str, net: &NetworkDef, wait: u32, dns: &str) -> Result<(), String> {
|
||||
nm::connect_verbose(iface, net, wait, dns)
|
||||
}
|
||||
fn tailscale_installed(&self) -> bool {
|
||||
tailscale::installed()
|
||||
}
|
||||
fn ensure_exit_node(&self, node: &str) -> TsHealth {
|
||||
tailscale::ensure_exit_node(node)
|
||||
}
|
||||
fn tailscale_check(&self, node: &str) -> TsHealth {
|
||||
tailscale::check(node)
|
||||
}
|
||||
fn connectivity(&self, cfg: &Config) -> Connectivity {
|
||||
status::connectivity(cfg)
|
||||
}
|
||||
fn notify(&self, summary: &str, body: &str, urgency: Urgency) {
|
||||
notify::notify(summary, body, urgency)
|
||||
}
|
||||
fn log(&self, line: &str) {
|
||||
notify::log(line)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue