2026-07-11 13:16:10 +07:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
|
|
pub struct SessionLock {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
pid: u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SessionLock {
|
|
|
|
|
pub fn new(session_dir: &Path) -> Self {
|
|
|
|
|
SessionLock {
|
|
|
|
|
path: session_dir.join(".lock"),
|
|
|
|
|
pid: std::process::id(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn try_lock(&self) -> std::io::Result<bool> {
|
|
|
|
|
if self.path.exists() {
|
|
|
|
|
let content = fs::read_to_string(&self.path).unwrap_or_default();
|
|
|
|
|
if let Ok(pid) = content.trim().parse::<u32>() {
|
|
|
|
|
if self.is_alive(pid) {
|
|
|
|
|
return Ok(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fs::write(&self.path, self.pid.to_string())?;
|
|
|
|
|
Ok(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn unlock(&self) {
|
|
|
|
|
let _ = fs::remove_file(&self.path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_alive(&self, pid: u32) -> bool {
|
2026-07-12 10:23:26 +07:00
|
|
|
// SAFETY: `libc::kill(pid, 0)` does not send a signal; it only checks
|
|
|
|
|
// whether the process exists and the caller has permission to signal
|
|
|
|
|
// it. The integer argument is a PID already validated by `try_lock`.
|
2026-07-11 13:16:10 +07:00
|
|
|
unsafe { libc::kill(pid as i32, 0) == 0 }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for SessionLock {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
let _ = fs::remove_file(&self.path);
|
|
|
|
|
}
|
|
|
|
|
}
|