use crate::{Config, WallSwitchResult, get_config_path};
use std::{fs, path::PathBuf, process, thread::sleep, time::Duration};
use sysinfo::{Pid, ProcessesToUpdate, System};
pub fn kill_other_instances(config: &Config) -> WallSwitchResult<()> {
if config.dry_run {
if config.verbose {
println!("[DRY-RUN] Skipping single-instance locking and termination.");
}
return Ok(());
}
let lock_path = get_lock_path()?;
let current_pid = process::id();
let mut sys = System::new();
if lock_path.exists()
&& let Ok(content) = fs::read_to_string(&lock_path)
&& let Ok(old_pid_u32) = content.trim().parse::<u32>()
{
let old_pid = Pid::from_u32(old_pid_u32);
sys.refresh_processes(ProcessesToUpdate::Some(&[old_pid]), true);
if let Some(process) = sys.process(old_pid) {
if old_pid_u32 != current_pid {
if config.verbose {
println!(
"Terminating previous instance found in lockfile (PID: {})...\n",
old_pid_u32
);
}
process.kill();
sleep(Duration::from_millis(100));
}
}
}
if let Some(parent) = lock_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&lock_path, current_pid.to_string())?;
Ok(())
}
fn get_lock_path() -> WallSwitchResult<PathBuf> {
let mut path = get_config_path()?;
path.set_file_name("wallswitch.lock");
Ok(path)
}
#[cfg(test)]
mod tests_pids {
use super::*;
#[test]
fn test_lock_path_resolves_correctly() {
let path_result = get_lock_path();
assert!(path_result.is_ok());
let path = path_result.unwrap();
assert!(path.to_string_lossy().ends_with("wallswitch.lock"));
}
#[test]
fn test_lockfile_write_and_parse() {
let temp_dir = std::env::temp_dir().join("wallswitch_test_pids");
fs::create_dir_all(&temp_dir).unwrap();
let test_lock_file = temp_dir.join("test_wallswitch.lock");
let current_pid = std::process::id();
fs::write(&test_lock_file, current_pid.to_string()).unwrap();
let content = fs::read_to_string(&test_lock_file).unwrap();
let parsed_pid: u32 = content.trim().parse().unwrap();
assert_eq!(parsed_pid, current_pid);
let _ = fs::remove_dir_all(temp_dir);
}
#[test]
fn test_lockfile_invalid_content_handling() {
let temp_dir = std::env::temp_dir().join("wallswitch_test_pids_invalid");
fs::create_dir_all(&temp_dir).unwrap();
let test_lock_file = temp_dir.join("invalid_test_wallswitch.lock");
fs::write(&test_lock_file, "corrupt_data_abc_123").unwrap();
let content = fs::read_to_string(&test_lock_file).unwrap();
let parsed_result = content.trim().parse::<u32>();
assert!(parsed_result.is_err());
let _ = fs::remove_dir_all(temp_dir);
}
#[test]
fn test_sysinfo_query_of_self_pid() {
let mut sys = System::new();
let current_pid = Pid::from_u32(std::process::id());
sys.refresh_processes(ProcessesToUpdate::Some(&[current_pid]), true);
let current_process = sys.process(current_pid);
assert!(current_process.is_some());
}
}