diff --git a/src-tauri/src/browser_runner.rs b/src-tauri/src/browser_runner.rs index 7692666..835d8be 100644 --- a/src-tauri/src/browser_runner.rs +++ b/src-tauri/src/browser_runner.rs @@ -1409,7 +1409,11 @@ impl BrowserRunner { #[cfg(target_os = "linux")] { use crate::platform_browser; - if let Err(e) = platform_browser::linux::kill_browser_process_impl(pid).await + if let Err(e) = platform_browser::linux::kill_browser_process_impl( + pid, + Some(&profile_path_str), + ) + .await { log::error!("Failed to force kill Camoufox process {}: {}", pid, e); } else { @@ -1490,7 +1494,12 @@ impl BrowserRunner { #[cfg(target_os = "linux")] { use crate::platform_browser; - if let Err(e) = platform_browser::linux::kill_browser_process_impl(pid).await { + if let Err(e) = platform_browser::linux::kill_browser_process_impl( + pid, + Some(&profile_path_str), + ) + .await + { log::error!("Failed to force kill Camoufox process {}: {}", pid, e); } else { // Verify the process is actually dead after force kill @@ -1578,7 +1587,8 @@ impl BrowserRunner { { use crate::platform_browser; if let Err(kill_err) = - platform_browser::linux::kill_browser_process_impl(pid).await + platform_browser::linux::kill_browser_process_impl(pid, Some(&profile_path_str)) + .await { log::error!( "Failed to force kill Camoufox process {}: {}", @@ -1848,7 +1858,12 @@ impl BrowserRunner { #[cfg(target_os = "linux")] { use crate::platform_browser; - if let Err(e) = platform_browser::linux::kill_browser_process_impl(pid).await { + if let Err(e) = platform_browser::linux::kill_browser_process_impl( + pid, + Some(&profile_path_str), + ) + .await + { log::error!("Failed to force kill Wayfern process {}: {}", pid, e); } else { sleep(Duration::from_millis(500)).await; @@ -1919,7 +1934,8 @@ impl BrowserRunner { { use crate::platform_browser; if let Err(kill_err) = - platform_browser::linux::kill_browser_process_impl(pid).await + platform_browser::linux::kill_browser_process_impl(pid, Some(&profile_path_str)) + .await { log::error!("Failed to force kill Wayfern process {}: {}", pid, kill_err); } else { @@ -2216,7 +2232,12 @@ impl BrowserRunner { platform_browser::windows::kill_browser_process_impl(pid).await?; #[cfg(target_os = "linux")] - platform_browser::linux::kill_browser_process_impl(pid).await?; + { + let profiles_dir = self.profile_manager.get_profiles_dir(); + let profile_data_path = profile.get_profile_data_path(&profiles_dir); + let profile_path_str = profile_data_path.to_string_lossy().to_string(); + platform_browser::linux::kill_browser_process_impl(pid, Some(&profile_path_str)).await?; + } #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))] return Err("Unsupported platform".into()); diff --git a/src-tauri/src/platform_browser.rs b/src-tauri/src/platform_browser.rs index 200cb45..0acdd9a 100644 --- a/src-tauri/src/platform_browser.rs +++ b/src-tauri/src/platform_browser.rs @@ -874,18 +874,158 @@ pub mod linux { pub async fn kill_browser_process_impl( pid: u32, + profile_data_path: Option<&str>, ) -> Result<(), Box> { - use sysinfo::{Pid, System}; - let system = System::new_all(); - if let Some(process) = system.process(Pid::from(pid as usize)) { - if !process.kill() { - return Err(format!("Failed to kill process {}", pid).into()); + use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System}; + + log::info!("Attempting to kill browser process with PID: {pid}"); + + let mut pids_to_kill = vec![pid]; + + // Find all descendant processes + let descendants = get_all_descendant_pids(pid); + pids_to_kill.extend(descendants); + + // Find additional processes using the same profile path + if let Some(profile_path) = profile_data_path { + let additional_pids = find_processes_by_profile_path(profile_path); + for p in additional_pids { + if !pids_to_kill.contains(&p) { + log::info!("Found additional process {} using profile path", p); + pids_to_kill.push(p); + } } - } else { - return Err(format!("Process {} not found", pid).into()); } - log::info!("Successfully killed browser process with PID: {pid}"); + log::info!("Total processes to kill: {:?}", pids_to_kill); + + // Send SIGKILL to all identified processes + for &p in &pids_to_kill { + log::info!("Sending SIGKILL to PID: {p}"); + let _ = Command::new("kill") + .args(["-KILL", &p.to_string()]) + .output(); + } + + // Also kill by process group and parent PID + let pid_str = pid.to_string(); + let _ = Command::new("pkill") + .args(["-KILL", "-P", &pid_str]) + .output(); + + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + + // Verify processes are dead + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + let mut still_running = Vec::new(); + for &p in &pids_to_kill { + if system.process(Pid::from(p as usize)).is_some() { + still_running.push(p); + } + } + + if !still_running.is_empty() { + log::info!( + "Processes {:?} still running, trying final termination", + still_running + ); + + for p in &still_running { + let _ = Command::new("kill") + .args(["-KILL", &p.to_string()]) + .output(); + } + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + let mut final_still_running = Vec::new(); + for &p in &pids_to_kill { + if system.process(Pid::from(p as usize)).is_some() { + final_still_running.push(p); + } + } + + if !final_still_running.is_empty() { + log::error!( + "ERROR: Processes {:?} could not be terminated despite aggressive attempts", + final_still_running + ); + return Err( + format!( + "Failed to terminate browser processes {:?} - still running", + final_still_running + ) + .into(), + ); + } + } + + log::info!("Browser termination completed for PID: {pid}"); Ok(()) } + + fn find_processes_by_profile_path(profile_path: &str) -> Vec { + use sysinfo::{ProcessRefreshKind, RefreshKind, System}; + + let mut pids = Vec::new(); + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + + for (pid, process) in system.processes() { + let cmd = process.cmd(); + if cmd.is_empty() { + continue; + } + + let has_profile = cmd.iter().any(|arg| { + if let Some(arg_str) = arg.to_str() { + arg_str.contains(profile_path) + } else { + false + } + }); + + if has_profile { + pids.push(pid.as_u32()); + } + } + + pids + } + + fn get_all_descendant_pids(parent_pid: u32) -> Vec { + use sysinfo::{ProcessRefreshKind, RefreshKind, System}; + + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + let mut descendants = Vec::new(); + let mut to_check = vec![parent_pid]; + let mut checked = std::collections::HashSet::new(); + + while let Some(current_pid) = to_check.pop() { + if checked.contains(¤t_pid) { + continue; + } + checked.insert(current_pid); + + for (pid, process) in system.processes() { + let pid_u32 = pid.as_u32(); + if let Some(parent) = process.parent() { + if parent.as_u32() == current_pid && !checked.contains(&pid_u32) { + descendants.push(pid_u32); + to_check.push(pid_u32); + } + } + } + } + + descendants + } } diff --git a/src-tauri/src/profile/manager.rs b/src-tauri/src/profile/manager.rs index 0864d9b..26783dc 100644 --- a/src-tauri/src/profile/manager.rs +++ b/src-tauri/src/profile/manager.rs @@ -9,7 +9,7 @@ use crate::proxy_manager::PROXY_MANAGER; use crate::wayfern_manager::WayfernConfig; use std::fs::{self, create_dir_all}; use std::path::{Path, PathBuf}; -use sysinfo::{Pid, System}; +use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System}; pub struct ProfileManager { camoufox_manager: &'static crate::camoufox_manager::CamoufoxManager, @@ -1217,7 +1217,9 @@ impl ProfileManager { // For non-camoufox browsers, use the existing PID-based logic let inner_profile = profile.clone(); - let mut system = System::new(); + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); let mut is_running = false; let mut found_pid: Option = None; @@ -1259,8 +1261,6 @@ impl ProfileManager { // If we didn't find the browser with the stored PID, search all processes if !is_running { - // Refresh all processes only when we need to search (expensive but necessary) - system.refresh_all(); for (pid, process) in system.processes() { let cmd = process.cmd(); if cmd.len() >= 2 { diff --git a/src-tauri/src/proxy_storage.rs b/src-tauri/src/proxy_storage.rs index 6ce47ac..a2b26a7 100644 --- a/src-tauri/src/proxy_storage.rs +++ b/src-tauri/src/proxy_storage.rs @@ -131,7 +131,9 @@ pub fn generate_proxy_id() -> String { } pub fn is_process_running(pid: u32) -> bool { - use sysinfo::{Pid, System}; - let system = System::new(); - system.process(Pid::from(pid as usize)).is_some() + use sysinfo::{ProcessRefreshKind, RefreshKind, System}; + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + system.process(sysinfo::Pid::from_u32(pid)).is_some() }