status: deduplicate the healthy and degraded paths, keep the System block contiguous
print_status and print_degraded_sections carried four byte-identical blocks - host firewall (25 lines), anti-torrent (24), last actions (9) and recent events (10) - plus a Conntrack block that was the same logic assembled differently and a Traffic desired block of four printlns. Roughly 80 lines of copy-paste with no test covering either copy, so a format change applied to one path would have silently diverged from the other. The divergence would only ever be visible while the container is down, which is the worst moment to discover the diagnostic output is wrong. Every shared section is now one function called from both paths: print_traffic_desired_section, print_host_firewall_section, print_antitorrent_section, print_conntrack_section, print_last_actions_section, print_recent_events_section. The file already had this shape for print_swap_status and print_reboot_section; the pattern is now followed consistently. The Docker/Amnezia port path block keeps two variants because they genuinely differ - real values against unknown - but the shared FORWARD ownership note is a single constant. The runtime state paths were also duplicated across modules: status.rs re-declared the literals that daemon.rs already owns. daemon::LAST_STATE_FILE and daemon::EVENTS_FILE are now pub and reused, and the recent-events window is a named constant instead of a bare 10. Ordering fix in the same function: print_network_tuning_status ran between the service line and the container lines, so container/container_id/pid/netns/interface appeared under the "Network tuning:" header at the same two-space indent as its fields. Container identity is now printed inside the discovery match, before any other section opens, in both the healthy and degraded paths. Field names are unchanged; only the order of two blocks moved. IMPLEMENTATION_SPEC section 15 documented the old order and is updated. print_degraded_sections returned anyhow::Result<()> while never failing; it returns unit now. status.rs: 645 -> 602 lines. cargo test --workspace: 110 + 4 passed. clippy -D warnings clean.
This commit is contained in:
@@ -451,14 +451,14 @@ Status обязан показывать наличие таблицы/chain/п
|
|||||||
```text
|
```text
|
||||||
System:
|
System:
|
||||||
service: active|inactive|unknown
|
service: active|inactive|unknown
|
||||||
Network tuning:
|
|
||||||
enabled, apply_on_daemon_start, tcp_bbr_mode, conf_path, modules_load_path,
|
|
||||||
значения управляемых ключей, bbr_available, bbr_active, drift[, reason][, warning]
|
|
||||||
container: <name>
|
container: <name>
|
||||||
container_id: <id>
|
container_id: <id>
|
||||||
pid: <pid>
|
pid: <pid>
|
||||||
netns: /proc/<pid>/ns/net
|
netns: /proc/<pid>/ns/net
|
||||||
interface: <iface>
|
interface: <iface>
|
||||||
|
Network tuning:
|
||||||
|
enabled, apply_on_daemon_start, tcp_bbr_mode, conf_path, modules_load_path,
|
||||||
|
значения управляемых ключей, bbr_available, bbr_active, drift[, reason][, warning]
|
||||||
Peers:
|
Peers:
|
||||||
total: N
|
total: N
|
||||||
- <peer ip>
|
- <peer ip>
|
||||||
@@ -493,9 +493,11 @@ Reboot scheduler:
|
|||||||
enabled, window_start_utc, window_end_utc, service, planned
|
enabled, window_start_utc, window_end_utc, service, planned
|
||||||
```
|
```
|
||||||
|
|
||||||
Блок `Network tuning` печатается между строкой `service` и строками `container`/`pid`/`netns`/`interface`, хотя последние логически относятся к `System`. Это текущее фактическое поведение; порядок зафиксирован здесь, чтобы автоматические проверки опирались на реальность.
|
Блок `System` обязан оставаться неразрывным: строки `container`/`container_id`/`pid`/`netns`/`interface` имеют тот же отступ, что и поля любой другой секции, поэтому открытие постороннего блока между ними и заголовком `System` делает вывод неоднозначным при чтении.
|
||||||
|
|
||||||
Если контейнер обнаружить не удалось, `status` не завершается ошибкой, а переходит в degraded-режим: секции `Peers` и `WireGuard clients` печатают `unavailable`, `Traffic actual` показывает `drift: unknown`, блок `Docker/Amnezia port path` заполняется значениями `unknown`, а блок `Peer traffic counters` не печатается вовсе. Секции host firewall, anti-torrent, swap, conntrack, событий и reboot остаются на месте.
|
Если контейнер обнаружить не удалось, `status` не завершается ошибкой, а переходит в degraded-режим. Строки `container`/`pid`/`netns`/`interface` печатаются со значениями `n/a`, секции `Peers` и `WireGuard clients` печатают `unavailable`, `Traffic actual` показывает `drift: unknown`, блок `Docker/Amnezia port path` заполняется значениями `unknown`, а блок `Peer traffic counters` не печатается вовсе. Секции host firewall, anti-torrent, swap, conntrack, событий и reboot остаются на месте.
|
||||||
|
|
||||||
|
Секции, общие для здорового и degraded путей, рендерятся одними и теми же функциями. Это обязательное требование: раньше они были продублированы, и расхождение форматов проявилось бы ровно в тот момент, когда контейнер лежит, то есть в худший для диагностики момент.
|
||||||
|
|
||||||
Отдельные секции деградируют независимо: ошибка чтения peer state не мешает вывести traffic, firewall и swap.
|
Отдельные секции деградируют независимо: ошибка чтения peer state не мешает вывести traffic, firewall и swap.
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ struct LastActionState {
|
|||||||
last_error: Option<String>,
|
last_error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const LAST_STATE_FILE: &str = "/run/awg2-harness/last_action.state";
|
pub const LAST_STATE_FILE: &str = "/run/awg2-harness/last_action.state";
|
||||||
const EVENTS_FILE: &str = "/run/awg2-harness/events.log";
|
pub const EVENTS_FILE: &str = "/run/awg2-harness/events.log";
|
||||||
const EVENTS_MAX_LINES: usize = 1000;
|
const EVENTS_MAX_LINES: usize = 1000;
|
||||||
const EVENTS_MAX_BYTES: usize = 256 * 1024;
|
const EVENTS_MAX_BYTES: usize = 256 * 1024;
|
||||||
|
|
||||||
|
|||||||
+104
-147
@@ -1,5 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::Config, docker, firewall, netns::build_transport, swap, sysctl_tuning, traffic, wg,
|
config::Config,
|
||||||
|
daemon::{EVENTS_FILE, LAST_STATE_FILE},
|
||||||
|
docker, firewall,
|
||||||
|
netns::build_transport,
|
||||||
|
swap, sysctl_tuning, traffic, wg,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@@ -10,6 +14,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ONLINE_HANDSHAKE_THRESHOLD_SECS: u64 = 180;
|
const ONLINE_HANDSHAKE_THRESHOLD_SECS: u64 = 180;
|
||||||
|
const RECENT_EVENTS_LIMIT: usize = 10;
|
||||||
|
const FORWARD_OWNERSHIP_NOTE: &str =
|
||||||
|
" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD";
|
||||||
|
|
||||||
pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
|
pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
|
||||||
println!("System:");
|
println!("System:");
|
||||||
@@ -22,26 +29,33 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
Err(_) => println!(" service: unknown"),
|
Err(_) => println!(" service: unknown"),
|
||||||
}
|
}
|
||||||
|
|
||||||
print_network_tuning_status(cfg);
|
// The System block stays contiguous: container identity is printed before any other
|
||||||
|
// section opens, otherwise its two-space fields read as belonging to that section.
|
||||||
let target = match docker::discover_target(cfg) {
|
let target = match docker::discover_target(cfg) {
|
||||||
Ok(t) => t,
|
Ok(t) => {
|
||||||
|
println!(" container: {}", t.container_name);
|
||||||
|
println!(" container_id: {}", t.container_id);
|
||||||
|
println!(" pid: {}", t.container_pid);
|
||||||
|
println!(" netns: {}", t.netns_path);
|
||||||
|
println!(" interface: {}", t.interface_name);
|
||||||
|
Some(t)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(" container: not available ({e})");
|
println!(" container: not available ({e})");
|
||||||
println!(" container_id: n/a");
|
println!(" container_id: n/a");
|
||||||
println!(" pid: n/a");
|
println!(" pid: n/a");
|
||||||
println!(" netns: n/a");
|
println!(" netns: n/a");
|
||||||
println!(" interface: n/a");
|
println!(" interface: n/a");
|
||||||
print_degraded_sections(cfg)?;
|
None
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(" container: {}", target.container_name);
|
print_network_tuning_status(cfg);
|
||||||
println!(" container_id: {}", target.container_id);
|
|
||||||
println!(" pid: {}", target.container_pid);
|
let Some(target) = target else {
|
||||||
println!(" netns: {}", target.netns_path);
|
print_degraded_sections(cfg);
|
||||||
println!(" interface: {}", target.interface_name);
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
let docker_path_diag = docker::collect_port_path_diagnostics(
|
let docker_path_diag = docker::collect_port_path_diagnostics(
|
||||||
&target.container_name,
|
&target.container_name,
|
||||||
@@ -101,11 +115,7 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
(_, Err(e)) => println!(" unavailable ({e})"),
|
(_, Err(e)) => println!(" unavailable ({e})"),
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Traffic desired:");
|
print_traffic_desired_section(cfg);
|
||||||
println!(" peer_limit: {}", cfg.traffic.peer_limit);
|
|
||||||
println!(" root_rate: {}", cfg.traffic.root_rate);
|
|
||||||
println!(" ifb_name: {}", cfg.traffic.ifb_name);
|
|
||||||
println!(" class_start_id: {}", cfg.traffic.class_start_id);
|
|
||||||
|
|
||||||
println!("Traffic actual:");
|
println!("Traffic actual:");
|
||||||
match traffic::inspect_drift_summary(cfg) {
|
match traffic::inspect_drift_summary(cfg) {
|
||||||
@@ -170,99 +180,13 @@ pub fn print_status(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
Err(_) => println!(" filter statistics: unavailable"),
|
Err(_) => println!(" filter statistics: unavailable"),
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Host firewall:");
|
print_host_firewall_section(cfg);
|
||||||
println!(" enabled: {}", cfg.host_firewall.enabled);
|
|
||||||
println!(" ssh_port: {}/tcp", cfg.host_firewall.ssh_port);
|
|
||||||
println!(
|
|
||||||
" amnezia_port: {}/{}",
|
|
||||||
cfg.host_firewall.amnezia_port,
|
|
||||||
cfg.host_firewall.amnezia_protocols.join(",")
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" input_policy: {}",
|
|
||||||
cfg.host_firewall
|
|
||||||
.default_input_policy
|
|
||||||
.trim()
|
|
||||||
.to_ascii_lowercase()
|
|
||||||
);
|
|
||||||
if cfg.host_firewall.enabled {
|
|
||||||
match firewall::host_firewall_drift_reason(cfg) {
|
|
||||||
Ok(None) => println!(" drift: no"),
|
|
||||||
Ok(Some(reason)) => {
|
|
||||||
println!(" drift: yes");
|
|
||||||
println!(" reason: {}", reason);
|
|
||||||
}
|
|
||||||
Err(e) => println!(" drift: unknown ({e})"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_docker_amnezia_port_path(&docker_path_diag);
|
print_docker_amnezia_port_path(&docker_path_diag);
|
||||||
|
print_antitorrent_section(cfg);
|
||||||
println!("Anti-torrent:");
|
|
||||||
println!(" enabled: {}", cfg.anti_torrent.enabled);
|
|
||||||
println!(
|
|
||||||
" max_connections_per_peer: {}",
|
|
||||||
cfg.anti_torrent.max_connections_per_peer
|
|
||||||
);
|
|
||||||
|
|
||||||
if cfg.anti_torrent.enabled {
|
|
||||||
match firewall::antitorrent_drift_reason(cfg) {
|
|
||||||
Ok(None) => println!(" drift: no"),
|
|
||||||
Ok(Some(reason)) => {
|
|
||||||
println!(" drift: yes");
|
|
||||||
println!(" reason: {}", reason);
|
|
||||||
}
|
|
||||||
Err(e) => println!(" drift: unknown ({e})"),
|
|
||||||
}
|
|
||||||
match firewall::antitorrent_drop_counter(cfg) {
|
|
||||||
Ok(Some((packets, bytes))) => {
|
|
||||||
println!(" dropped_packets: {}", packets);
|
|
||||||
println!(" dropped_bytes: {}", bytes);
|
|
||||||
}
|
|
||||||
Ok(None) => println!(" dropped_packets: n/a"),
|
|
||||||
Err(e) => println!(" dropped_packets: inspect error ({e})"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_swap_status(cfg);
|
print_swap_status(cfg);
|
||||||
|
print_conntrack_section();
|
||||||
let ct_count = Command::new("sysctl")
|
print_last_actions_section();
|
||||||
.args(["-n", "net.netfilter.nf_conntrack_count"])
|
print_recent_events_section();
|
||||||
.output()
|
|
||||||
.ok()
|
|
||||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
|
||||||
.unwrap_or_else(|| "n/a".to_string());
|
|
||||||
let ct_max = Command::new("sysctl")
|
|
||||||
.args(["-n", "net.netfilter.nf_conntrack_max"])
|
|
||||||
.output()
|
|
||||||
.ok()
|
|
||||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
|
||||||
.unwrap_or_else(|| "n/a".to_string());
|
|
||||||
println!("Conntrack:");
|
|
||||||
println!(" nf_conntrack_count: {}", ct_count);
|
|
||||||
println!(" nf_conntrack_max: {}", ct_max);
|
|
||||||
|
|
||||||
println!("Last actions:");
|
|
||||||
let last = fs::read_to_string("/run/awg2-harness/last_action.state").unwrap_or_default();
|
|
||||||
if last.trim().is_empty() {
|
|
||||||
println!(" no persisted state");
|
|
||||||
} else {
|
|
||||||
for line in last.lines() {
|
|
||||||
println!(" {}", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Recent events:");
|
|
||||||
let events = fs::read_to_string("/run/awg2-harness/events.log").unwrap_or_default();
|
|
||||||
let lines: Vec<&str> = events.lines().collect();
|
|
||||||
if lines.is_empty() {
|
|
||||||
println!(" no recent events");
|
|
||||||
} else {
|
|
||||||
for l in lines.iter().rev().take(10).rev() {
|
|
||||||
println!(" {l}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_reboot_section(cfg);
|
print_reboot_section(cfg);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -279,22 +203,42 @@ fn print_network_tuning_status(cfg: &Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_degraded_sections(cfg: &Config) -> anyhow::Result<()> {
|
/// Sections that stay meaningful when the container was not discovered.
|
||||||
|
///
|
||||||
|
/// Every section shared with the healthy path is rendered by the same helper, so the two
|
||||||
|
/// paths cannot drift apart in format. Only the sections that genuinely depend on the
|
||||||
|
/// container differ, and they say so explicitly.
|
||||||
|
fn print_degraded_sections(cfg: &Config) {
|
||||||
println!("Peers:");
|
println!("Peers:");
|
||||||
println!(" unavailable (container/netns not discovered)");
|
println!(" unavailable (container/netns not discovered)");
|
||||||
|
|
||||||
println!("WireGuard clients:");
|
println!("WireGuard clients:");
|
||||||
println!(" unavailable (container/netns not discovered)");
|
println!(" unavailable (container/netns not discovered)");
|
||||||
|
|
||||||
|
print_traffic_desired_section(cfg);
|
||||||
|
|
||||||
|
println!("Traffic actual:");
|
||||||
|
println!(" drift: unknown (container/netns not discovered)");
|
||||||
|
|
||||||
|
print_host_firewall_section(cfg);
|
||||||
|
print_unknown_docker_amnezia_port_path();
|
||||||
|
print_antitorrent_section(cfg);
|
||||||
|
print_swap_status(cfg);
|
||||||
|
print_conntrack_section();
|
||||||
|
print_last_actions_section();
|
||||||
|
print_recent_events_section();
|
||||||
|
print_reboot_section(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_traffic_desired_section(cfg: &Config) {
|
||||||
println!("Traffic desired:");
|
println!("Traffic desired:");
|
||||||
println!(" peer_limit: {}", cfg.traffic.peer_limit);
|
println!(" peer_limit: {}", cfg.traffic.peer_limit);
|
||||||
println!(" root_rate: {}", cfg.traffic.root_rate);
|
println!(" root_rate: {}", cfg.traffic.root_rate);
|
||||||
println!(" ifb_name: {}", cfg.traffic.ifb_name);
|
println!(" ifb_name: {}", cfg.traffic.ifb_name);
|
||||||
println!(" class_start_id: {}", cfg.traffic.class_start_id);
|
println!(" class_start_id: {}", cfg.traffic.class_start_id);
|
||||||
|
}
|
||||||
|
|
||||||
println!("Traffic actual:");
|
fn print_host_firewall_section(cfg: &Config) {
|
||||||
println!(" drift: unknown (container/netns not discovered)");
|
|
||||||
|
|
||||||
println!("Host firewall:");
|
println!("Host firewall:");
|
||||||
println!(" enabled: {}", cfg.host_firewall.enabled);
|
println!(" enabled: {}", cfg.host_firewall.enabled);
|
||||||
println!(" ssh_port: {}/tcp", cfg.host_firewall.ssh_port);
|
println!(" ssh_port: {}/tcp", cfg.host_firewall.ssh_port);
|
||||||
@@ -320,21 +264,18 @@ fn print_degraded_sections(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
Err(e) => println!(" drift: unknown ({e})"),
|
Err(e) => println!(" drift: unknown ({e})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("Docker/Amnezia port path:");
|
fn print_antitorrent_section(cfg: &Config) {
|
||||||
println!(" docker_userland_proxy: unknown");
|
|
||||||
println!(" published_udp_port: unknown");
|
|
||||||
println!(" host_forward_policy_drop: unknown");
|
|
||||||
println!(" docker_forward_rules_present: unknown");
|
|
||||||
println!(" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD");
|
|
||||||
|
|
||||||
println!("Anti-torrent:");
|
println!("Anti-torrent:");
|
||||||
println!(" enabled: {}", cfg.anti_torrent.enabled);
|
println!(" enabled: {}", cfg.anti_torrent.enabled);
|
||||||
println!(
|
println!(
|
||||||
" max_connections_per_peer: {}",
|
" max_connections_per_peer: {}",
|
||||||
cfg.anti_torrent.max_connections_per_peer
|
cfg.anti_torrent.max_connections_per_peer
|
||||||
);
|
);
|
||||||
if cfg.anti_torrent.enabled {
|
if !cfg.anti_torrent.enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match firewall::antitorrent_drift_reason(cfg) {
|
match firewall::antitorrent_drift_reason(cfg) {
|
||||||
Ok(None) => println!(" drift: no"),
|
Ok(None) => println!(" drift: no"),
|
||||||
Ok(Some(reason)) => {
|
Ok(Some(reason)) => {
|
||||||
@@ -351,49 +292,54 @@ fn print_degraded_sections(cfg: &Config) -> anyhow::Result<()> {
|
|||||||
Ok(None) => println!(" dropped_packets: n/a"),
|
Ok(None) => println!(" dropped_packets: n/a"),
|
||||||
Err(e) => println!(" dropped_packets: inspect error ({e})"),
|
Err(e) => println!(" dropped_packets: inspect error ({e})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_swap_status(cfg);
|
|
||||||
|
|
||||||
|
fn print_conntrack_section() {
|
||||||
println!("Conntrack:");
|
println!("Conntrack:");
|
||||||
let ct_count = Command::new("sysctl")
|
println!(
|
||||||
.args(["-n", "net.netfilter.nf_conntrack_count"])
|
" nf_conntrack_count: {}",
|
||||||
.output()
|
read_sysctl_or_na("net.netfilter.nf_conntrack_count")
|
||||||
.ok()
|
);
|
||||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
println!(
|
||||||
.unwrap_or_else(|| "n/a".to_string());
|
" nf_conntrack_max: {}",
|
||||||
let ct_max = Command::new("sysctl")
|
read_sysctl_or_na("net.netfilter.nf_conntrack_max")
|
||||||
.args(["-n", "net.netfilter.nf_conntrack_max"])
|
);
|
||||||
.output()
|
}
|
||||||
.ok()
|
|
||||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
|
||||||
.unwrap_or_else(|| "n/a".to_string());
|
|
||||||
println!(" nf_conntrack_count: {}", ct_count);
|
|
||||||
println!(" nf_conntrack_max: {}", ct_max);
|
|
||||||
|
|
||||||
|
fn read_sysctl_or_na(key: &str) -> String {
|
||||||
|
Command::new("sysctl")
|
||||||
|
.args(["-n", key])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|o| o.status.success())
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||||
|
.filter(|v| !v.is_empty())
|
||||||
|
.unwrap_or_else(|| "n/a".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_last_actions_section() {
|
||||||
println!("Last actions:");
|
println!("Last actions:");
|
||||||
let last = fs::read_to_string("/run/awg2-harness/last_action.state").unwrap_or_default();
|
let last = fs::read_to_string(LAST_STATE_FILE).unwrap_or_default();
|
||||||
if last.trim().is_empty() {
|
if last.trim().is_empty() {
|
||||||
println!(" no persisted state");
|
println!(" no persisted state");
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
for line in last.lines() {
|
for line in last.lines() {
|
||||||
println!(" {}", line);
|
println!(" {}", line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_recent_events_section() {
|
||||||
println!("Recent events:");
|
println!("Recent events:");
|
||||||
let events = fs::read_to_string("/run/awg2-harness/events.log").unwrap_or_default();
|
let events = fs::read_to_string(EVENTS_FILE).unwrap_or_default();
|
||||||
let lines: Vec<&str> = events.lines().collect();
|
let lines: Vec<&str> = events.lines().collect();
|
||||||
if lines.is_empty() {
|
if lines.is_empty() {
|
||||||
println!(" no recent events");
|
println!(" no recent events");
|
||||||
} else {
|
return;
|
||||||
for l in lines.iter().rev().take(10).rev() {
|
}
|
||||||
|
for l in lines.iter().rev().take(RECENT_EVENTS_LIMIT).rev() {
|
||||||
println!(" {l}");
|
println!(" {l}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
print_reboot_section(cfg);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_reboot_section(cfg: &Config) {
|
fn print_reboot_section(cfg: &Config) {
|
||||||
@@ -487,12 +433,23 @@ fn print_docker_amnezia_port_path(diag: &docker::PortPathDiagnostics) {
|
|||||||
" docker_forward_rules_present: {}",
|
" docker_forward_rules_present: {}",
|
||||||
diag.docker_forward_rules_present.as_str()
|
diag.docker_forward_rules_present.as_str()
|
||||||
);
|
);
|
||||||
println!(" note: FORWARD is Docker-managed; awg2-harness does not modify host FORWARD");
|
println!("{FORWARD_OWNERSHIP_NOTE}");
|
||||||
if let Some(w) = diag.warning() {
|
if let Some(w) = diag.warning() {
|
||||||
println!(" warning: {w}");
|
println!(" warning: {w}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same block as [`print_docker_amnezia_port_path`], but every probe needs the container,
|
||||||
|
/// so all four fields are reported as unknown rather than omitted.
|
||||||
|
fn print_unknown_docker_amnezia_port_path() {
|
||||||
|
println!("Docker/Amnezia port path:");
|
||||||
|
println!(" docker_userland_proxy: unknown");
|
||||||
|
println!(" published_udp_port: unknown");
|
||||||
|
println!(" host_forward_policy_drop: unknown");
|
||||||
|
println!(" docker_forward_rules_present: unknown");
|
||||||
|
println!("{FORWARD_OWNERSHIP_NOTE}");
|
||||||
|
}
|
||||||
|
|
||||||
fn handshake_is_online(epoch: Option<u64>, threshold_secs: u64) -> &'static str {
|
fn handshake_is_online(epoch: Option<u64>, threshold_secs: u64) -> &'static str {
|
||||||
if let Some(ts) = epoch {
|
if let Some(ts) = epoch {
|
||||||
let now = SystemTime::now()
|
let now = SystemTime::now()
|
||||||
|
|||||||
Reference in New Issue
Block a user