diff --git a/easytier-contrib/easytier-ffi/src/lib.rs b/easytier-contrib/easytier-ffi/src/lib.rs index 1c2e5ab..e63b97f 100644 --- a/easytier-contrib/easytier-ffi/src/lib.rs +++ b/easytier-contrib/easytier-ffi/src/lib.rs @@ -202,7 +202,7 @@ pub unsafe extern "C" fn collect_network_infos( std::slice::from_raw_parts_mut(infos, max_length) }; - let collected_infos = match INSTANCE_MANAGER.collect_network_infos() { + let collected_infos = match INSTANCE_MANAGER.collect_network_infos_sync() { Ok(infos) => infos, Err(e) => { set_error_msg(&format!("failed to collect network infos: {}", e)); diff --git a/easytier-contrib/easytier-uptime/src/health_checker.rs b/easytier-contrib/easytier-uptime/src/health_checker.rs index e4e5244..1d44344 100644 --- a/easytier-contrib/easytier-uptime/src/health_checker.rs +++ b/easytier-contrib/easytier-uptime/src/health_checker.rs @@ -497,7 +497,7 @@ impl HealthChecker { instance_mgr: Arc, // return version, response time on healthy, conn_count ) -> anyhow::Result<(String, u64, u32)> { - let Some(instance) = instance_mgr.get_network_info(&inst_id) else { + let Some(instance) = instance_mgr.get_network_info(&inst_id).await else { anyhow::bail!("healthy check node is not started"); }; diff --git a/easytier-gui/src-tauri/src/lib.rs b/easytier-gui/src-tauri/src/lib.rs index 2cb646e..1c7eb46 100644 --- a/easytier-gui/src-tauri/src/lib.rs +++ b/easytier-gui/src-tauri/src/lib.rs @@ -93,9 +93,10 @@ fn retain_network_instance(instance_ids: Vec) -> Result<(), String> { } #[tauri::command] -fn collect_network_infos() -> Result, String> { +async fn collect_network_infos() -> Result, String> { let infos = INSTANCE_MANAGER .collect_network_infos() + .await .map_err(|e| e.to_string())?; let mut ret = BTreeMap::new(); diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 41ed6d0..a7985cf 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -1228,7 +1228,7 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> { tokio::select! { _ = manager.wait() => { - let infos = manager.collect_network_infos()?; + let infos = manager.collect_network_infos().await?; let errs = infos .into_values() .filter_map(|info| info.error_msg) diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index dc7c8be..37247c0 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -33,7 +33,8 @@ use crate::peers::peer_manager::{PeerManager, RouteAlgoType}; use crate::peers::rpc_service::PeerManagerRpcService; use crate::peers::{create_packet_recv_chan, recv_packet_from_chan, PacketRecvChanReceiver}; use crate::proto::api::config::{ - ConfigPatchAction, ConfigRpc, PatchConfigRequest, PatchConfigResponse, PortForwardPatch, + ConfigPatchAction, ConfigRpc, GetConfigRequest, GetConfigResponse, PatchConfigRequest, + PatchConfigResponse, PortForwardPatch, }; use crate::proto::api::instance::{ GetPrometheusStatsRequest, GetPrometheusStatsResponse, GetStatsRequest, GetStatsResponse, @@ -42,6 +43,7 @@ use crate::proto::api::instance::{ MappedListenerManageRpc, MetricSnapshot, PortForwardManageRpc, StatsRpc, VpnPortalInfo, VpnPortalRpc, }; +use crate::proto::api::manage::NetworkConfig; use crate::proto::common::{PortForwardConfigPb, TunnelInfo}; use crate::proto::rpc_impl::standalone::RpcServerHook; use crate::proto::rpc_types; @@ -1194,6 +1196,7 @@ impl Instance { #[derive(Clone)] pub struct ConfigRpcService { patcher: InstanceConfigPatcher, + global_ctx: Weak, } #[async_trait::async_trait] @@ -1212,10 +1215,23 @@ impl Instance { self.patcher.apply_patch(patch).await?; Ok(PatchConfigResponse::default()) } + + async fn get_config( + &self, + _: Self::Controller, + _request: GetConfigRequest, + ) -> crate::proto::rpc_types::error::Result { + let global_ctx = weak_upgrade(&self.global_ctx)?; + let config = NetworkConfig::new_from_config(&global_ctx.config)?; + Ok(GetConfigResponse { + config: Some(config), + }) + } } ConfigRpcService { patcher: self.get_config_patcher(), + global_ctx: Arc::downgrade(&self.global_ctx), } } diff --git a/easytier/src/instance_manager.rs b/easytier/src/instance_manager.rs index cc86761..d2dc7a7 100644 --- a/easytier/src/instance_manager.rs +++ b/easytier/src/instance_manager.rs @@ -123,22 +123,33 @@ impl NetworkInstanceManager { Ok(self.list_network_instance_ids()) } - pub fn collect_network_infos( + pub async fn collect_network_infos( &self, ) -> Result, anyhow::Error> { let mut ret = BTreeMap::new(); for instance in self.instance_map.iter() { - if let Some(info) = instance.get_running_info() { + if let Ok(info) = instance.get_running_info().await { ret.insert(*instance.key(), info); } } Ok(ret) } - pub fn get_network_info(&self, instance_id: &uuid::Uuid) -> Option { + pub fn collect_network_infos_sync( + &self, + ) -> Result, anyhow::Error> { + tokio::runtime::Runtime::new()?.block_on(self.collect_network_infos()) + } + + pub async fn get_network_info( + &self, + instance_id: &uuid::Uuid, + ) -> Option { self.instance_map - .get(instance_id) - .and_then(|instance| instance.value().get_running_info()) + .get(instance_id)? + .get_running_info() + .await + .ok() } pub fn list_network_instance_ids(&self) -> Vec { diff --git a/easytier/src/launcher.rs b/easytier/src/launcher.rs index 60ecbb1..0d300b1 100644 --- a/easytier/src/launcher.rs +++ b/easytier/src/launcher.rs @@ -1,6 +1,6 @@ use crate::common::config::PortForwardConfig; -use crate::proto::api::manage; -use crate::proto::peer_rpc::RouteForeignNetworkSummary; +use crate::proto::api::{self, manage}; +use crate::proto::rpc_types::controller::BaseController; use crate::rpc_service::InstanceRpcService; use crate::{ common::{ @@ -10,11 +10,9 @@ use crate::{ }, constants::EASYTIER_VERSION, global_ctx::{EventBusSubscriber, GlobalCtxEvent}, - stun::StunInfoCollectorTrait, }, instance::instance::Instance, - peers::rpc_service::PeerManagerRpcService, - proto::api::instance::{list_peer_route_pair, PeerInfo, Route}, + proto::api::instance::list_peer_route_pair, }; use anyhow::Context; use chrono::{DateTime, Local}; @@ -37,12 +35,7 @@ pub struct Event { struct EasyTierData { events: RwLock>, - my_node_info: RwLock, - routes: RwLock>, - peers: RwLock>, - foreign_network_summary: RwLock, tun_fd: Arc>>, - tun_dev_name: RwLock, event_subscriber: RwLock>, instance_stop_notifier: Arc, } @@ -53,12 +46,7 @@ impl Default for EasyTierData { Self { event_subscriber: RwLock::new(tx), events: RwLock::new(VecDeque::new()), - my_node_info: RwLock::new(MyNodeInfo::default()), - routes: RwLock::new(Vec::new()), - peers: RwLock::new(Vec::new()), - foreign_network_summary: RwLock::new(RouteForeignNetworkSummary::default()), tun_fd: Arc::new(RwLock::new(None)), - tun_dev_name: RwLock::new(String::new()), instance_stop_notifier: Arc::new(tokio::sync::Notify::new()), } } @@ -70,14 +58,12 @@ pub struct EasyTierLauncher { thread_handle: Option>, api_service: ArcMutApiService, running_cfg: String, - fetch_node_info: bool, - error_msg: Arc>>, data: Arc, } impl EasyTierLauncher { - pub fn new(fetch_node_info: bool) -> Self { + pub fn new() -> Self { let instance_alive = Arc::new(AtomicBool::new(false)); Self { instance_alive, @@ -85,8 +71,6 @@ impl EasyTierLauncher { api_service: Arc::new(RwLock::new(None)), error_msg: Arc::new(RwLock::new(None)), running_cfg: String::new(), - fetch_node_info, - stop_flag: Arc::new(AtomicBool::new(false)), data: Arc::new(EasyTierData::default()), } @@ -143,7 +127,6 @@ impl EasyTierLauncher { stop_signal: Arc, api_service: ArcMutApiService, data: Arc, - fetch_node_info: bool, ) -> Result<(), anyhow::Error> { let mut instance = Instance::new(cfg); let mut tasks = JoinSet::new(); @@ -175,48 +158,6 @@ impl EasyTierLauncher { } }); - // update my node info - if fetch_node_info { - let data_c = data.clone(); - let global_ctx_c = instance.get_global_ctx(); - let peer_mgr_c = instance.get_peer_manager().clone(); - let vpn_portal = instance.get_vpn_portal_inst(); - tasks.spawn(async move { - loop { - // Update TUN Device Name - *data_c.tun_dev_name.write().unwrap() = - global_ctx_c.get_flags().dev_name.clone(); - - let node_info = MyNodeInfo { - virtual_ipv4: global_ctx_c.get_ipv4().map(|ip| ip.into()), - hostname: global_ctx_c.get_hostname(), - version: EASYTIER_VERSION.to_string(), - ips: Some(global_ctx_c.get_ip_collector().collect_ip_addrs().await), - stun_info: Some(global_ctx_c.get_stun_info_collector().get_stun_info()), - listeners: global_ctx_c - .get_running_listeners() - .into_iter() - .map(Into::into) - .collect(), - vpn_portal_cfg: Some( - vpn_portal - .lock() - .await - .dump_client_config(peer_mgr_c.clone()) - .await, - ), - }; - *data_c.my_node_info.write().unwrap() = node_info.clone(); - *data_c.routes.write().unwrap() = peer_mgr_c.list_routes().await; - *data_c.peers.write().unwrap() = - PeerManagerRpcService::list_peers(&peer_mgr_c).await; - *data_c.foreign_network_summary.write().unwrap() = - peer_mgr_c.get_foreign_network_summary().await; - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - }); - } - #[cfg(any(target_os = "android", target_env = "ohos"))] Self::run_routine_for_android(&instance, &data, &mut tasks).await; @@ -253,7 +194,6 @@ impl EasyTierLauncher { instance_alive.store(true, std::sync::atomic::Ordering::Relaxed); let data = self.data.clone(); - let fetch_node_info = self.fetch_node_info; let api_service = self.api_service.clone(); self.thread_handle = Some(std::thread::spawn(move || { @@ -286,7 +226,6 @@ impl EasyTierLauncher { stop_notifier.clone(), api_service, data, - fetch_node_info, )); if let Err(e) = ret { error_msg.write().unwrap().replace(format!("{:?}", e)); @@ -305,31 +244,11 @@ impl EasyTierLauncher { .load(std::sync::atomic::Ordering::Relaxed) } - pub fn get_dev_name(&self) -> String { - self.data.tun_dev_name.read().unwrap().clone() - } - pub fn get_events(&self) -> Vec { let events = self.data.events.read().unwrap(); events.iter().cloned().collect() } - pub fn get_node_info(&self) -> MyNodeInfo { - self.data.my_node_info.read().unwrap().clone() - } - - pub fn get_routes(&self) -> Vec { - self.data.routes.read().unwrap().clone() - } - - pub fn get_peers(&self) -> Vec { - self.data.peers.read().unwrap().clone() - } - - pub fn get_foreign_network_summary(&self) -> RouteForeignNetworkSummary { - self.data.foreign_network_summary.read().unwrap().clone() - } - pub fn get_api_service(&self) -> Option> { match self.api_service.read() { Ok(guard) => guard.clone(), @@ -341,6 +260,12 @@ impl EasyTierLauncher { } } +impl Default for EasyTierLauncher { + fn default() -> Self { + Self::new() + } +} + impl Drop for EasyTierLauncher { fn drop(&mut self) { self.stop_flag @@ -380,13 +305,6 @@ impl NetworkInstance { } } - fn get_fetch_node_info(&self) -> bool { - match self.config_source { - ConfigSource::Cli | ConfigSource::File => false, - ConfigSource::Web | ConfigSource::GUI | ConfigSource::FFI => true, - } - } - pub fn get_config_source(&self) -> ConfigSource { self.config_source.clone() } @@ -395,18 +313,77 @@ impl NetworkInstance { self.launcher.is_some() && self.launcher.as_ref().unwrap().running() } - pub fn get_running_info(&self) -> Option { - self.launcher.as_ref()?; + pub async fn get_running_info(&self) -> anyhow::Result { + let launcher = self.launcher.as_ref().ok_or_else(|| { + anyhow::anyhow!("instance is not running, please start the instance first") + })?; + let api_service = self.get_api_service().ok_or_else(|| { + anyhow::anyhow!("failed to get api service, instance may not be running") + })?; + let ctrl = BaseController::default(); - let launcher = self.launcher.as_ref().unwrap(); - - let peers = launcher.get_peers(); - let routes = launcher.get_routes(); + let peers = api_service + .get_peer_manage_service() + .list_peer(ctrl.clone(), api::instance::ListPeerRequest::default()) + .await? + .peer_infos; + let my_info = api_service + .get_peer_manage_service() + .show_node_info(ctrl.clone(), api::instance::ShowNodeInfoRequest::default()) + .await? + .node_info + .ok_or_else(|| anyhow::anyhow!("failed to get my node info"))?; + let vpn_portal_cfg = api_service + .get_vpn_portal_service() + .get_vpn_portal_info( + ctrl.clone(), + api::instance::GetVpnPortalInfoRequest::default(), + ) + .await? + .vpn_portal_info + .map(|i| i.client_config); + let routes = api_service + .get_peer_manage_service() + .list_route(ctrl.clone(), api::instance::ListRouteRequest::default()) + .await? + .routes; let peer_route_pairs = list_peer_route_pair(peers.clone(), routes.clone()); + let foreign_network_summary = api_service + .get_peer_manage_service() + .get_foreign_network_summary( + ctrl.clone(), + api::instance::GetForeignNetworkSummaryRequest::default(), + ) + .await? + .summary; + let dev_name = api_service + .get_config_service() + .get_config(ctrl.clone(), api::config::GetConfigRequest::default()) + .await? + .config + .ok_or_else(|| anyhow::anyhow!("failed to get config"))? + .dev_name + .unwrap_or_else(|| "".to_string()); - Some(NetworkInstanceRunningInfo { - dev_name: launcher.get_dev_name(), - my_node_info: Some(launcher.get_node_info()), + Ok(NetworkInstanceRunningInfo { + dev_name, + my_node_info: Some(MyNodeInfo { + virtual_ipv4: my_info + .ipv4_addr + .parse::() + .ok() + .map(Into::into), + hostname: my_info.hostname, + version: EASYTIER_VERSION.to_string(), + ips: my_info.ip_list, + stun_info: my_info.stun_info, + listeners: my_info + .listeners + .into_iter() + .map(|s| s.parse::().unwrap().into()) + .collect(), + vpn_portal_cfg, + }), events: launcher .get_events() .iter() @@ -417,7 +394,7 @@ impl NetworkInstance { peer_route_pairs, running: launcher.running(), error_msg: launcher.error_msg(), - foreign_network_summary: Some(launcher.get_foreign_network_summary()), + foreign_network_summary, }) } @@ -436,7 +413,7 @@ impl NetworkInstance { return Ok(self.subscribe_event().unwrap()); } - let launcher = EasyTierLauncher::new(self.get_fetch_node_info()); + let launcher = EasyTierLauncher::new(); self.launcher = Some(launcher); let ev = self.subscribe_event().unwrap(); @@ -784,7 +761,7 @@ impl NetworkConfig { Ok(cfg) } - pub fn new_from_config(config: &TomlConfigLoader) -> Result { + pub fn new_from_config(config: impl ConfigLoader) -> Result { let default_config = TomlConfigLoader::default(); let mut result = Self { diff --git a/easytier/src/peers/rpc_service.rs b/easytier/src/peers/rpc_service.rs index dedfb1b..1a70a21 100644 --- a/easytier/src/peers/rpc_service.rs +++ b/easytier/src/peers/rpc_service.rs @@ -7,8 +7,9 @@ use crate::{ proto::{ api::instance::{ AclManageRpc, DumpRouteRequest, DumpRouteResponse, GetAclStatsRequest, - GetAclStatsResponse, GetWhitelistRequest, GetWhitelistResponse, - ListForeignNetworkRequest, ListForeignNetworkResponse, ListGlobalForeignNetworkRequest, + GetAclStatsResponse, GetForeignNetworkSummaryRequest, GetForeignNetworkSummaryResponse, + GetWhitelistRequest, GetWhitelistResponse, ListForeignNetworkRequest, + ListForeignNetworkResponse, ListGlobalForeignNetworkRequest, ListGlobalForeignNetworkResponse, ListPeerRequest, ListPeerResponse, ListRouteRequest, ListRouteResponse, PeerInfo, PeerManageRpc, ShowNodeInfoRequest, ShowNodeInfoResponse, }, @@ -139,6 +140,20 @@ impl PeerManageRpc for PeerManagerRpcService { .await) } + async fn get_foreign_network_summary( + &self, + _: BaseController, + _request: GetForeignNetworkSummaryRequest, + ) -> Result { + Ok(GetForeignNetworkSummaryResponse { + summary: Some( + weak_upgrade(&self.peer_manager)? + .get_foreign_network_summary() + .await, + ), + }) + } + async fn show_node_info( &self, _: BaseController, diff --git a/easytier/src/proto/api_config.proto b/easytier/src/proto/api_config.proto index 4a48672..568f485 100644 --- a/easytier/src/proto/api_config.proto +++ b/easytier/src/proto/api_config.proto @@ -3,6 +3,7 @@ syntax = "proto3"; import "common.proto"; import "acl.proto"; import "api_instance.proto"; +import "api_manage.proto"; package api.config; @@ -69,6 +70,15 @@ message PatchConfigRequest { message PatchConfigResponse {} +message GetConfigRequest { + api.instance.InstanceIdentifier instance = 1; +} + +message GetConfigResponse { + api.manage.NetworkConfig config = 1; +} + service ConfigRpc { rpc PatchConfig(PatchConfigRequest) returns (PatchConfigResponse); + rpc GetConfig(GetConfigRequest) returns (GetConfigResponse); } diff --git a/easytier/src/proto/api_instance.proto b/easytier/src/proto/api_instance.proto index a01af16..2afc2c5 100644 --- a/easytier/src/proto/api_instance.proto +++ b/easytier/src/proto/api_instance.proto @@ -139,6 +139,12 @@ message ListGlobalForeignNetworkResponse { map foreign_networks = 1; } +message GetForeignNetworkSummaryRequest { InstanceIdentifier instance = 1; } + +message GetForeignNetworkSummaryResponse { + peer_rpc.RouteForeignNetworkSummary summary = 1; +} + service PeerManageRpc { rpc ListPeer(ListPeerRequest) returns (ListPeerResponse); rpc ListRoute(ListRouteRequest) returns (ListRouteResponse); @@ -148,6 +154,8 @@ service PeerManageRpc { rpc ListGlobalForeignNetwork(ListGlobalForeignNetworkRequest) returns (ListGlobalForeignNetworkResponse); rpc ShowNodeInfo(ShowNodeInfoRequest) returns (ShowNodeInfoResponse); + rpc GetForeignNetworkSummary(GetForeignNetworkSummaryRequest) + returns (GetForeignNetworkSummaryResponse); } enum ConnectorStatus { diff --git a/easytier/src/rpc_service/config.rs b/easytier/src/rpc_service/config.rs index ccd52fd..1f1b921 100644 --- a/easytier/src/rpc_service/config.rs +++ b/easytier/src/rpc_service/config.rs @@ -3,7 +3,9 @@ use std::sync::Arc; use crate::{ instance_manager::NetworkInstanceManager, proto::{ - api::config::{ConfigRpc, PatchConfigRequest, PatchConfigResponse}, + api::config::{ + ConfigRpc, GetConfigRequest, GetConfigResponse, PatchConfigRequest, PatchConfigResponse, + }, rpc_types::{self, controller::BaseController}, }, }; @@ -33,4 +35,15 @@ impl ConfigRpc for ConfigRpcService { .patch_config(ctrl, input) .await } + + async fn get_config( + &self, + ctrl: Self::Controller, + input: GetConfigRequest, + ) -> Result { + super::get_instance_service(&self.instance_manager, &input.instance)? + .get_config_service() + .get_config(ctrl, input) + .await + } } diff --git a/easytier/src/rpc_service/instance_manage.rs b/easytier/src/rpc_service/instance_manage.rs index ac06d29..9cab1f9 100644 --- a/easytier/src/rpc_service/instance_manage.rs +++ b/easytier/src/rpc_service/instance_manage.rs @@ -76,7 +76,8 @@ impl WebClientService for InstanceManageRpcService { let mut ret = NetworkInstanceRunningInfoMap { map: self .manager - .collect_network_infos()? + .collect_network_infos() + .await? .into_iter() .map(|(k, v)| (k.to_string(), v)) .collect(), diff --git a/easytier/src/rpc_service/peer_manage.rs b/easytier/src/rpc_service/peer_manage.rs index 2a21e08..274e325 100644 --- a/easytier/src/rpc_service/peer_manage.rs +++ b/easytier/src/rpc_service/peer_manage.rs @@ -78,6 +78,17 @@ impl PeerManageRpc for PeerManageRpcService { .await } + async fn get_foreign_network_summary( + &self, + ctrl: Self::Controller, + req: crate::proto::api::instance::GetForeignNetworkSummaryRequest, + ) -> crate::proto::rpc_types::error::Result { + super::get_instance_service(&self.instance_manager, &req.instance)? + .get_peer_manage_service() + .get_foreign_network_summary(ctrl, req) + .await + } + async fn show_node_info( &self, ctrl: Self::Controller,