diff --git a/desktop/tauri/src-tauri/src/main.rs b/desktop/tauri/src-tauri/src/main.rs index 746f2411..3f67f43d 100644 --- a/desktop/tauri/src-tauri/src/main.rs +++ b/desktop/tauri/src-tauri/src/main.rs @@ -66,11 +66,16 @@ impl portmaster::Handler for WsHandler { // relaunch the UI process now that the core is reachable again. if self.handle.portmaster().consume_restart_ui_proc_requested() { info!("restart-ui-process pending, relaunching UI process"); - if let Err(err) = relaunch::request_ui_relaunch() { - error!("failed to relaunch UI process after upgrade: {}", err); + match relaunch::request_ui_relaunch() { + Ok(()) => { + self.handle.exit(0); + return; + } + Err(err) => { + error!("failed to relaunch UI process after upgrade: {}", err); + error!("continuing with current UI process"); + } } - self.handle.exit(0); - return; } // we successfully connected to Portmaster. Set is_first_connect to false diff --git a/desktop/tauri/src-tauri/src/relaunch.rs b/desktop/tauri/src-tauri/src/relaunch.rs index e889be3f..e7540b51 100644 --- a/desktop/tauri/src-tauri/src/relaunch.rs +++ b/desktop/tauri/src-tauri/src/relaunch.rs @@ -1,5 +1,6 @@ use std::{ ffi::OsString, + path::Path, process::{Command, Stdio}, thread, time::Duration, @@ -11,6 +12,54 @@ const UI_RELAUNCH_HELPER_ENV: &str = "PORTMASTER_UI_RELAUNCH_HELPER"; const RELAUNCH_RETRY_COUNT: usize = 40; const RELAUNCH_RETRY_DELAY: Duration = Duration::from_millis(500); +fn current_process_argv0() -> Option { + std::env::args_os().next() +} + +fn is_usable_launch_program(program: &OsString) -> bool { + let path = Path::new(program); + + // Fail closed for command-only values (for example, "portmaster"): we cannot + // verify where they resolve to, so do not use them for relaunch. + if !path.is_absolute() && path.components().count() <= 1 { + return false; + } + + if !path.exists() || !path.is_file() { + return false; + } + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + if let Ok(meta) = std::fs::metadata(path) { + return meta.permissions().mode() & 0o111 != 0; + } + return false; + } + + #[cfg(not(unix))] + { + true + } +} + +fn resolve_launch_program() -> Result { + let current_exe = std::env::current_exe().ok().map(|p| p.into_os_string()); + let argv0 = current_process_argv0(); + + if let Some(program) = current_exe.as_ref().filter(|p| is_usable_launch_program(p)) { + return Ok(program.clone()); + } + + if let Some(program) = argv0.as_ref().filter(|p| is_usable_launch_program(p)) { + return Ok(program.clone()); + } + + Err("failed to determine relaunch executable: no verified launchable file path from current_exe or argv0".to_string()) +} + fn current_process_args() -> Vec { std::env::args_os() .skip(1) @@ -23,8 +72,7 @@ fn current_process_args() -> Vec { } pub fn request_ui_relaunch() -> Result<(), String> { - let exe = std::env::current_exe() - .map_err(|err| format!("failed to get current executable path: {}", err))?; + let exe = resolve_launch_program()?; let args = current_process_args(); let mut cmd = Command::new(&exe); @@ -53,8 +101,7 @@ pub fn run_relaunch_helper_if_requested() { } fn run_relaunch_helper() -> Result<(), String> { - let exe = std::env::current_exe() - .map_err(|err| format!("failed to get current executable path in relaunch helper: {}", err))?; + let exe = resolve_launch_program()?; let args = current_process_args(); debug!("[tauri] relaunch helper started");