use crate::{Config, Environment, WallSwitchResult};
use sysinfo::{Pid, ProcessRefreshKind, ProcessesToUpdate, System, UpdateKind};
pub fn kill_other_instances(config: &Config) -> WallSwitchResult<()> {
if config.dry_run {
if config.verbose {
println!("[DRY-RUN] Skipping killing other instances.");
}
return Ok(());
}
let env = Environment::new()?;
let pkg_name = env.get_pkg_name();
let current_pid: u32 = std::process::id();
let pids: Vec<u32> = get_pids(pkg_name, config)?;
for pid in pids {
if pid != current_pid {
if config.verbose {
println!("Killing previous instances: kill -9 {pid}\n");
}
kill_app(pid, config)?;
}
}
Ok(())
}
fn get_pids(pkg_name: &str, config: &Config) -> WallSwitchResult<Vec<u32>> {
let mut pids = Vec::new();
let current_pid = std::process::id();
let mut sys = System::new();
sys.refresh_processes_specifics(
ProcessesToUpdate::All,
true, ProcessRefreshKind::nothing().with_exe(UpdateKind::Always),
);
for (pid, process) in sys.processes() {
let pid_u32 = pid.as_u32();
if pid_u32 == current_pid {
continue;
}
if let Some(exe_path) = process.exe()
&& let Some(name) = exe_path.file_name().and_then(|n| n.to_str())
&& name == pkg_name
{
pids.push(pid_u32);
}
}
pids.sort();
pids.dedup();
if !pids.is_empty() && config.verbose {
println!("Process identification (pid) found via sysinfo:");
println!("pids: {pids:?}\n");
}
Ok(pids)
}
fn kill_app(pid_number: u32, config: &Config) -> WallSwitchResult<()> {
let pid = Pid::from_u32(pid_number);
let mut sys = System::new();
sys.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
if let Some(process) = sys.process(pid) {
if config.verbose {
println!("Sending native termination signal to PID: {pid_number}");
}
process.kill();
}
Ok(())
}
#[cfg(test)]
mod test_pids {
use super::*;
#[test]
fn test_current_pid_exclusion() {
let config = Config::default();
let env = Environment::fallback();
let pids = get_pids(env.get_pkg_name(), &config).unwrap();
let current_pid = std::process::id();
assert!(!pids.contains(¤t_pid));
}
}