From 1d24fcb2d0c842c125c0bae50bdf7bf3221c301f Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Thu, 29 May 2025 15:04:15 +0100 Subject: [PATCH 1/2] Fix GUI closing immediately when enabling service - Add retry logic with up to 10 attempts for IPC connection - Detect service installation/startup state to prevent premature closure - Implement exponential backoff for connection retries - Check installing_service() flag for accurate service state detection This fixes the issue where the GUI would close within ~500ms when enabling the service due to 'Access is denied' IPC errors. Fixes #7010 --- src/ui_interface.rs | 65 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/ui_interface.rs b/src/ui_interface.rs index b1ce62643..1ff1df293 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1157,9 +1157,14 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver { log::error!("ipc connection closed: {}", err); + // Don't quit immediately if we're starting the service + // Give the service time to start and allow retries if is_cm { - crate::ui_cm_interface::quit_cm(); + // Check if service is starting up + let mut service_starting = false; + + #[cfg(windows)] + { + // On Windows, check if we're in elevated mode or if service is being installed + let is_elevated = crate::platform::is_elevated(None).unwrap_or(false); + service_starting = !is_elevated && (crate::platform::installing_service() || + (crate::platform::is_installed() && !crate::platform::is_root())); + } + + #[cfg(not(windows))] + { + // On other platforms, check if service is installed but not running as root + service_starting = crate::platform::is_installed() && + !crate::platform::is_root(); + } + + if !service_starting { + crate::ui_cm_interface::quit_cm(); + } } break; } @@ -1247,6 +1274,36 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver= max_attempts { + let mut service_starting = false; + + #[cfg(windows)] + { + // On Windows, check if service is being installed/started + let is_elevated = crate::platform::is_elevated(None).unwrap_or(false); + service_starting = !is_elevated && (crate::platform::installing_service() || + (crate::platform::is_installed() && !crate::platform::is_root())); + } + + #[cfg(not(windows))] + { + // On other platforms + service_starting = crate::platform::is_installed() && + !crate::platform::is_root(); + } + + if !service_starting { + log::error!("Failed to connect after {} attempts, quitting", max_attempts); + crate::ui_cm_interface::quit_cm(); + break; + } + } } *UI_STATUS.lock().unwrap() = UiStatus { status_num: -1, @@ -1259,7 +1316,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver Date: Thu, 29 May 2025 16:21:08 +0100 Subject: [PATCH 2/2] Fix GUI closing when enabling service on Windows This fixes issue #7010 where the RustDesk GUI closes immediately when enabling the service. The problem was: 1. GUI has IPC server on \\.\\pipe\\RustDesk\\query 2. Service tries to start and create IPC server on same pipe 3. Service gets 'Access is denied' error 4. Service calls stop_main_window_process() which sends Close message to GUI 5. GUI exits immediately The fix: 1. Added retry logic with exponential backoff (up to 10 attempts) in server.rs 2. Added environment variable check to avoid killing GUI during service installation 3. Added Windows support in ipc.rs to restart GUI after receiving Close message 4. Increased wait times to allow proper handover of IPC server from GUI to service This allows the GUI to gracefully restart while the service takes over the IPC server. --- src/ipc.rs | 13 ++++++++++++- src/server.rs | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/ipc.rs b/src/ipc.rs index a74a0c103..bedb63e2e 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -420,10 +420,10 @@ async fn handle(data: Data, stream: &mut Connection) { if is_server() { let _ = privacy_mode::turn_off_privacy(0, Some(PrivacyModeState::OffByPeer)); } - #[cfg(any(target_os = "macos", target_os = "linux"))] if crate::is_main() { // below part is for main windows can be reopen during rustdesk installation and installing service from UI // this make new ipc server (domain socket) can be created. + #[cfg(not(windows))] std::fs::remove_file(&Config::ipc_path("")).ok(); #[cfg(target_os = "linux")] { @@ -442,6 +442,17 @@ async fn handle(data: Data, stream: &mut Connection) { .spawn() .ok(); } + #[cfg(windows)] + { + // On Windows, when service is being installed, wait a bit then restart the GUI + hbb_common::sleep(2.0).await; + if let Ok(exe) = std::env::current_exe() { + std::process::Command::new(&exe) + .arg("--no-server") + .spawn() + .ok(); + } + } // leave above open a little time hbb_common::sleep(0.3).await; // in case below exit failed diff --git a/src/server.rs b/src/server.rs index 87e6f390f..16c7d4b3f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -547,13 +547,40 @@ pub async fn start_server(is_server: bool, no_server: bool) { if is_server { crate::common::set_server_running(true); std::thread::spawn(move || { - if let Err(err) = crate::ipc::start("") { - log::error!("Failed to start ipc: {}", err); - if crate::is_server() { - log::error!("ipc is occupied by another process, try kill it"); - std::thread::spawn(stop_main_window_process).join().ok(); + // Retry IPC server start with exponential backoff during service installation + let mut attempts = 0; + let max_attempts = 10; // Increase attempts to handle service installation scenario + let mut wait_time = 1; + + loop { + match crate::ipc::start("") { + Ok(_) => break, + Err(err) => { + attempts += 1; + log::error!("Failed to start ipc (attempt {}/{}): {}", attempts, max_attempts, err); + + if attempts >= max_attempts { + if crate::is_server() { + log::error!("ipc is occupied by another process after {} attempts", max_attempts); + // Check if we're being started as part of service installation + // In that case, the GUI needs time to close its IPC server + let is_service_installation = std::env::var("RUSTDESK_SERVICE_INSTALLATION").is_ok(); + + if !is_service_installation { + log::error!("Not during service installation, try kill the process occupying IPC"); + std::thread::spawn(stop_main_window_process).join().ok(); + } else { + log::info!("Service installation detected, GUI should restart itself"); + } + } + std::process::exit(-1); + } + + // Wait before retrying with exponential backoff + std::thread::sleep(std::time::Duration::from_secs(wait_time)); + wait_time = std::cmp::min(wait_time * 2, 10); // Cap at 10 seconds + } } - std::process::exit(-1); } }); input_service::fix_key_down_timeout_loop();