1use crate::errors::CoreError;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5pub struct LockFile {
7 path: PathBuf,
8}
9
10impl LockFile {
11 pub fn acquire(path: &Path) -> Result<Self, CoreError> {
13 if path.exists() {
14 let contents = fs::read_to_string(path)
16 .map_err(|e| CoreError::Lock(format!("reading lockfile: {e}")))?;
17 if let Ok(pid) = contents.trim().parse::<i32>() {
18 if is_process_alive(pid) {
19 return Err(CoreError::Lock(format!(
20 "another retro process is running (PID {pid})"
21 )));
22 }
23 }
24 let _ = fs::remove_file(path);
26 }
27
28 let pid = std::process::id();
29 fs::write(path, pid.to_string())
30 .map_err(|e| CoreError::Lock(format!("writing lockfile: {e}")))?;
31
32 Ok(LockFile {
33 path: path.to_path_buf(),
34 })
35 }
36
37 pub fn try_acquire(path: &Path) -> Option<Self> {
40 match Self::acquire(path) {
41 Ok(lock) => Some(lock),
42 Err(_) => None,
43 }
44 }
45}
46
47impl Drop for LockFile {
48 fn drop(&mut self) {
49 let _ = fs::remove_file(&self.path);
50 }
51}
52
53fn is_process_alive(pid: i32) -> bool {
55 unsafe { libc::kill(pid, 0) == 0 }
58}