mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Add single-instance enforcement (#460)
Prevent multiple BoilR instances from running simultaneously using a lock file with PID checking. This avoids potential data corruption when two instances try to modify Steam shortcuts concurrently. The lock file is stored in the config folder (boilr.lock) and contains the PID of the running instance. On startup, if a lock file exists, we check whether the process is still alive using sysinfo before deciding to block or take over the lock. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
e890ef940c
commit
ecb18d7192
+12
@@ -10,6 +10,7 @@ mod config;
|
|||||||
mod migration;
|
mod migration;
|
||||||
mod platforms;
|
mod platforms;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
mod single_instance;
|
||||||
mod steam;
|
mod steam;
|
||||||
mod steamgriddb;
|
mod steamgriddb;
|
||||||
mod sync;
|
mod sync;
|
||||||
@@ -20,6 +21,17 @@ use color_eyre::eyre::Result;
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
ensure_config_folder();
|
ensure_config_folder();
|
||||||
|
|
||||||
|
// Acquire single instance lock
|
||||||
|
let _instance_lock = match single_instance::InstanceLock::acquire() {
|
||||||
|
Ok(lock) => lock,
|
||||||
|
Err(msg) => {
|
||||||
|
eprintln!("Error: {}", msg);
|
||||||
|
eprintln!("Please close the other instance of BoilR first.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
migration::migrate_config();
|
migration::migrate_config();
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use sysinfo::{Pid, System};
|
||||||
|
|
||||||
|
use crate::config::get_config_folder;
|
||||||
|
|
||||||
|
/// Returns the path to the lock file
|
||||||
|
fn get_lock_file_path() -> PathBuf {
|
||||||
|
get_config_folder().join("boilr.lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a lock on the application instance
|
||||||
|
pub struct InstanceLock {
|
||||||
|
_file: File,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceLock {
|
||||||
|
/// Attempts to acquire an exclusive lock for this application instance.
|
||||||
|
/// Returns Ok(InstanceLock) if successful, or Err with a message if another instance is running.
|
||||||
|
pub fn acquire() -> Result<Self, String> {
|
||||||
|
let lock_path = get_lock_file_path();
|
||||||
|
|
||||||
|
// Ensure the config folder exists
|
||||||
|
if let Some(parent) = lock_path.parent() {
|
||||||
|
let _ = std::fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if lock file exists and contains a valid PID
|
||||||
|
if lock_path.exists() {
|
||||||
|
if let Ok(mut file) = File::open(&lock_path) {
|
||||||
|
let mut contents = String::new();
|
||||||
|
if file.read_to_string(&mut contents).is_ok() {
|
||||||
|
if let Ok(pid) = contents.trim().parse::<usize>() {
|
||||||
|
// Check if process with that PID is still running
|
||||||
|
if is_process_running(pid) {
|
||||||
|
return Err(format!(
|
||||||
|
"Another instance of BoilR is already running (PID: {})",
|
||||||
|
pid
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create/overwrite the lock file
|
||||||
|
match OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&lock_path)
|
||||||
|
{
|
||||||
|
Ok(mut file) => {
|
||||||
|
let pid = std::process::id();
|
||||||
|
if let Err(e) = write!(file, "{}", pid) {
|
||||||
|
return Err(format!("Failed to write lock file: {}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InstanceLock {
|
||||||
|
_file: file,
|
||||||
|
path: lock_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => Err(format!("Failed to create lock file: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InstanceLock {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = std::fs::remove_file(&self.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a process with the given PID is running using sysinfo
|
||||||
|
fn is_process_running(pid: usize) -> bool {
|
||||||
|
let mut system = System::new();
|
||||||
|
system.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
||||||
|
system.process(Pid::from(pid)).is_some()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user