From 3593035eb9ec11fa98e459044dbee92ab4c2a399 Mon Sep 17 00:00:00 2001 From: "Sijie.Sun" Date: Mon, 15 Sep 2025 23:33:43 +0800 Subject: [PATCH] fix networksetup timeout on some machine (#1372) --- easytier/src/common/network.rs | 44 ++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/easytier/src/common/network.rs b/easytier/src/common/network.rs index e66e788..ac8061d 100644 --- a/easytier/src/common/network.rs +++ b/easytier/src/common/network.rs @@ -59,18 +59,52 @@ impl InterfaceFilter { } } +// Cache for networksetup command output +#[cfg(target_os = "macos")] +static NETWORKSETUP_CACHE: std::sync::OnceLock> = + std::sync::OnceLock::new(); + #[cfg(any(target_os = "macos", target_os = "freebsd"))] impl InterfaceFilter { #[cfg(target_os = "macos")] - async fn is_interface_physical(&self) -> bool { - let interface_name = &self.iface.name; - let output = tokio::process::Command::new("networksetup") + async fn get_networksetup_output() -> String { + use anyhow::Context; + use std::time::{Duration, Instant}; + let cache = NETWORKSETUP_CACHE.get_or_init(|| Mutex::new((String::new(), Instant::now()))); + let mut cache_guard = cache.lock().await; + + // Check if cache is still valid (less than 1 minute old) + if cache_guard.1.elapsed() < Duration::from_secs(60) && !cache_guard.0.is_empty() { + return cache_guard.0.clone(); + } + + // Cache is expired or empty, fetch new data + let stdout = tokio::process::Command::new("networksetup") .args(["-listallhardwareports"]) .output() .await - .expect("Failed to execute command"); + .with_context(|| "Failed to execute networksetup command") + .and_then(|output| { + std::str::from_utf8(&output.stdout) + .map(|s| s.to_string()) + .with_context(|| "Failed to convert networksetup output to string") + }) + .unwrap_or_else(|e| { + tracing::error!("Failed to execute networksetup command: {:?}", e); + String::new() + }); - let stdout = std::str::from_utf8(&output.stdout).expect("Invalid UTF-8"); + // Update cache + cache_guard.0 = stdout.clone(); + cache_guard.1 = Instant::now(); + + stdout + } + + #[cfg(target_os = "macos")] + async fn is_interface_physical(&self) -> bool { + let interface_name = &self.iface.name; + let stdout = Self::get_networksetup_output().await; let lines: Vec<&str> = stdout.lines().collect();