refactor: better process management on linux

This commit is contained in:
zhom
2026-03-02 12:19:36 +04:00
parent 1ff17e6833
commit 576119e5a3
4 changed files with 184 additions and 21 deletions
+27 -6
View File
@@ -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());
+148 -8
View File
@@ -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(&current_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
}
}
+4 -4
View File
@@ -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 {
+5 -3
View File
@@ -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()
}