use crate::config::repository::{clear_runtime_config_snapshot, get_runtime_config_snapshot}; use crate::config::types::stored_config::KeyValuePair; use crate::kernel_bridge::{ aggregate_requested_tun_routes, start_local_socket_server as start_local_socket_server_inner, stop_local_socket_server as stop_local_socket_server_inner, }; use crate::runtime::state::runtime_state::{ RuntimeAggregateState, RuntimeInstanceState, TunAggregateState, clear_tun_attached, is_tun_attached, mark_tun_attached, runtime_instance_from_config_snapshot, runtime_instance_from_running_info, }; use crate::{ASYNC_RUNTIME, INSTANCE_MANAGER, WEB_CLIENTS}; pub(crate) fn start_kernel( config_id: String, start_kernel_with_config_id: impl Fn(&str) -> bool, ) -> bool { start_kernel_with_config_id(&config_id) } pub(crate) fn stop_kernel( config_id: String, stop_web_client: impl Fn(&str) -> bool, parse_instance_uuid: impl Fn(&str) -> Option, maybe_stop_local_socket_server: impl Fn(), ) -> bool { clear_tun_attached(&config_id); if stop_web_client(&config_id) { clear_runtime_config_snapshot(&config_id); return true; } let _ = stop_local_socket_server_inner(); let Some(instance_id) = parse_instance_uuid(&config_id) else { return false; }; let ret = INSTANCE_MANAGER .delete_network_instance(vec![instance_id]) .map(|_| true) .unwrap_or_else(|err| { ohrs_log_error!("[Rust] stop_kernel failed {}: {}", config_id, err); false }); if ret { clear_runtime_config_snapshot(&config_id); } let has_active_instances = !INSTANCE_MANAGER.list_network_instance_ids().is_empty(); let has_web_clients = WEB_CLIENTS .lock() .map(|guard| !guard.is_empty()) .unwrap_or(false); if has_active_instances || has_web_clients { let _ = start_local_socket_server_inner(); } maybe_stop_local_socket_server(); ret } pub(crate) fn stop_network_instance( config_ids: Vec, stop_kernel: impl Fn(String) -> bool, ) -> bool { let mut ok = true; for config_id in config_ids { ok = stop_kernel(config_id) && ok; } ok } pub(crate) fn collect_network_infos() -> Vec { let infos = match ASYNC_RUNTIME.block_on(INSTANCE_MANAGER.collect_network_infos()) { Ok(infos) => infos, Err(err) => { ohrs_log_error!("[Rust] collect network infos failed {}", err); return vec![]; } }; infos .into_iter() .filter_map(|(key, value)| { serde_json::to_string(&value) .ok() .map(|value_json| KeyValuePair { key: key.to_string(), value: value_json, }) }) .collect() } pub(crate) fn set_tun_fd( config_id: String, fd: i32, parse_instance_uuid: impl Fn(&str) -> Option, ) -> bool { let Some(instance_id) = parse_instance_uuid(&config_id) else { ohrs_log_error!("[Rust] set_tun_fd invalid instance id: {}", config_id); return false; }; INSTANCE_MANAGER .set_tun_fd(&instance_id, fd) .map(|_| { mark_tun_attached(&config_id); ohrs_log_info!( "[Rust] set_tun_fd success instance={} fd={} marked_attached=true", config_id, fd ); true }) .unwrap_or_else(|err| { ohrs_log_error!("[Rust] set_tun_fd failed {}: {}", config_id, err); false }) } pub(crate) fn get_runtime_snapshot() -> RuntimeAggregateState { get_runtime_snapshot_inner() } pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState { let infos = match ASYNC_RUNTIME.block_on(INSTANCE_MANAGER.collect_network_infos()) { Ok(infos) => infos, Err(err) => { ohrs_log_error!("[Rust] collect network infos failed {}", err); return RuntimeAggregateState { instances: vec![], tun: TunAggregateState { active: false, attached_instance_ids: vec![], aggregated_routes: vec![], dns_servers: vec![], need_rebuild: false, }, running_instance_count: 0, }; } }; let mut live_infos = infos .into_iter() .map(|(instance_id, info)| (instance_id.to_string(), info)) .collect::>(); let mut active_config_ids = live_infos.keys().cloned().collect::>(); if let Ok(guard) = WEB_CLIENTS.lock() { for config_id in guard.keys() { if !active_config_ids.iter().any(|value| value == config_id) { active_config_ids.push(config_id.clone()); } } } let mut instances = Vec::with_capacity(active_config_ids.len()); for config_id in active_config_ids { if let Some(info) = live_infos.remove(&config_id) { let snapshot = get_runtime_config_snapshot(&config_id); let display_name = snapshot .as_ref() .map(|snapshot| snapshot.display_name.clone()) .unwrap_or_else(|| config_id.clone()); let magic_dns_enabled = snapshot .as_ref() .and_then(|snapshot| snapshot.config.enable_magic_dns) .unwrap_or(false); let need_exit_node = snapshot .as_ref() .map(|snapshot| !snapshot.config.exit_nodes.is_empty()) .unwrap_or(false); instances.push(runtime_instance_from_running_info( config_id, display_name, magic_dns_enabled, need_exit_node, info, )); } else if let Some(snapshot) = get_runtime_config_snapshot(&config_id) { instances.push(runtime_instance_from_config_snapshot( config_id, snapshot.display_name, snapshot.config, true, )); } else { let tun_attached = is_tun_attached(&config_id); instances.push(RuntimeInstanceState { config_id: config_id.clone(), instance_id: config_id.clone(), display_name: config_id.clone(), running: true, tun_required: tun_attached, tun_attached, magic_dns_enabled: false, need_exit_node: false, error_message: None, my_node_info: None, events: Vec::new(), routes: Vec::new(), peers: Vec::new(), }); } } instances.sort_by(|a, b| { a.display_name .cmp(&b.display_name) .then_with(|| a.instance_id.cmp(&b.instance_id)) }); let attached_instance_ids = instances .iter() .filter(|instance| instance.tun_required) .map(|instance| instance.instance_id.clone()) .collect::>(); let aggregated_routes = aggregate_requested_tun_routes(&instances); let running_instance_count = instances.iter().filter(|instance| instance.running).count() as i32; let tun_active = !attached_instance_ids.is_empty(); RuntimeAggregateState { instances, tun: TunAggregateState { active: tun_active, attached_instance_ids, aggregated_routes, dns_servers: vec![], need_rebuild: false, }, running_instance_count, } }