mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-05-09 11:45:39 +02:00
refactor: better process management on linux
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -874,18 +874,158 @@ pub mod linux {
|
||||
|
||||
pub async fn kill_browser_process_impl(
|
||||
pid: u32,
|
||||
profile_data_path: Option<&str>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
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<u32> {
|
||||
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<u32> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<u32> = 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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user