refactor: remove direct Windows metrics from tunnel_manager and update wg.exe fallback in tunnel-helper

Remove direct_windows_metrics, read_windows_metrics_from_show, parse_human_wireguard_bytes, and find_windows_wg functions from tunnel_manager.rs to rely on bundled backend for all metrics queries. Update find_wg_cli in tunnel-helper to return "wg.exe" as fallback when WireGuard installation paths don't exist, removing "wg" from candidate list.
This commit is contained in:
2026-03-18 09:49:20 +01:00
parent ab7275059f
commit eff143d5b3
2 changed files with 5 additions and 163 deletions

View File

@@ -61,13 +61,6 @@ pub fn is_active(app: &AppHandle, profile_path: &Path) -> Result<bool, String> {
}
pub fn metrics(app: &AppHandle, profile_path: &Path) -> Result<TunnelMetrics, String> {
#[cfg(target_os = "windows")]
{
if let Ok(metrics) = direct_windows_metrics(profile_path) {
return Ok(metrics);
}
}
let backend = bundled_backend(app)?;
let mut command = Command::new(backend);
command.arg("metrics").arg("--profile").arg(profile_path);
@@ -93,157 +86,6 @@ pub struct TunnelMetrics {
pub tx_bytes: u64,
}
#[cfg(target_os = "windows")]
fn direct_windows_metrics(profile_path: &Path) -> Result<TunnelMetrics, String> {
let tunnel_name = profile_path
.file_stem()
.and_then(|value| value.to_str())
.ok_or_else(|| "invalid profile filename".to_string())?;
let service_name = format!("WireGuardTunnel${}", tunnel_name);
let mut status_cmd = Command::new("sc");
status_cmd.arg("query").arg(&service_name).creation_flags(CREATE_NO_WINDOW);
let status_output = status_cmd
.output()
.map_err(|err| format!("Unable to query tunnel status: {err}"))?;
if !status_output.status.success() {
return Ok(TunnelMetrics {
active: false,
rx_bytes: 0,
tx_bytes: 0,
});
}
let status_stdout = String::from_utf8_lossy(&status_output.stdout);
if !status_stdout.contains("RUNNING") {
return Ok(TunnelMetrics {
active: false,
rx_bytes: 0,
tx_bytes: 0,
});
}
let wg = find_windows_wg()?;
let mut wg_cmd = Command::new(wg);
wg_cmd
.arg("show")
.arg(tunnel_name)
.arg("dump")
.creation_flags(CREATE_NO_WINDOW);
let wg_output = wg_cmd
.output()
.map_err(|err| format!("Unable to query WireGuard counters: {err}"))?;
if !wg_output.status.success() {
return read_windows_metrics_from_show(tunnel_name);
}
let stdout = String::from_utf8_lossy(&wg_output.stdout);
let mut rx_bytes = 0_u64;
let mut tx_bytes = 0_u64;
for line in stdout.lines().skip(1) {
let fields: Vec<&str> = line.split('\t').collect();
if fields.len() < 7 {
continue;
}
rx_bytes = rx_bytes.saturating_add(fields[5].parse::<u64>().unwrap_or(0));
tx_bytes = tx_bytes.saturating_add(fields[6].parse::<u64>().unwrap_or(0));
}
Ok(TunnelMetrics {
active: true,
rx_bytes,
tx_bytes,
})
}
#[cfg(target_os = "windows")]
fn read_windows_metrics_from_show(tunnel_name: &str) -> Result<TunnelMetrics, String> {
let wg = find_windows_wg()?;
let mut command = Command::new(wg);
command
.arg("show")
.arg(tunnel_name)
.creation_flags(CREATE_NO_WINDOW);
let output = command
.output()
.map_err(|err| format!("Unable to query WireGuard transfer text: {err}"))?;
if !output.status.success() {
return Ok(TunnelMetrics {
active: true,
rx_bytes: 0,
tx_bytes: 0,
});
}
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rx_bytes = 0_u64;
let mut tx_bytes = 0_u64;
for line in stdout.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("transfer:") {
let parts: Vec<&str> = rest.split(',').collect();
if let Some(received) = parts.first() {
rx_bytes = parse_human_wireguard_bytes(received);
}
if let Some(sent) = parts.get(1) {
tx_bytes = parse_human_wireguard_bytes(sent);
}
}
}
Ok(TunnelMetrics {
active: true,
rx_bytes,
tx_bytes,
})
}
#[cfg(target_os = "windows")]
fn parse_human_wireguard_bytes(value: &str) -> u64 {
let cleaned = value
.replace("received", "")
.replace("sent", "")
.trim()
.to_string();
let mut parts = cleaned.split_whitespace();
let amount = parts
.next()
.map(|raw| raw.replace(',', "."))
.and_then(|raw| raw.parse::<f64>().ok())
.unwrap_or(0.0);
let unit = parts.next().unwrap_or("B");
let multiplier = match unit {
"B" => 1.0,
"KiB" => 1024.0,
"MiB" => 1024.0 * 1024.0,
"GiB" => 1024.0 * 1024.0 * 1024.0,
"TiB" => 1024.0 * 1024.0 * 1024.0 * 1024.0,
_ => 1.0,
};
(amount * multiplier) as u64
}
#[cfg(target_os = "windows")]
fn find_windows_wg() -> Result<PathBuf, String> {
let candidates = [
PathBuf::from("wg"),
PathBuf::from(r"C:\Program Files\WireGuard\wg.exe"),
PathBuf::from(r"C:\Program Files (x86)\WireGuard\wg.exe"),
];
candidates
.into_iter()
.find(|path| path.exists())
.ok_or_else(|| "required Windows WireGuard CLI is not available".to_string())
}
fn bundled_backend(app: &AppHandle) -> Result<PathBuf, String> {
let resource_dir = app
.path()