feat(tui): implement status bar with connection and turn state indicators feat(tui): create workflow panel for agent status and progress visualization feat(web): introduce web frontend interface with static file serving feat(ws): add WebSocket interface for real-time communication and session management
88 lines
2.7 KiB
Rust
88 lines
2.7 KiB
Rust
//! Filesystem-backed `SessionLockRepository` implementation using a PID file
|
|
//! (`<session_dir>/.lock`) with atomic `O_CREAT|O_EXCL` acquisition.
|
|
|
|
use std::convert::TryInto;
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
|
|
use zesdex_domain::auth::{RepositoryError, SessionLockRepository};
|
|
|
|
/// Concrete filesystem session-lock repository.
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct FileSystemSessionLockRepository;
|
|
|
|
impl FileSystemSessionLockRepository {
|
|
pub fn new() -> Self {
|
|
FileSystemSessionLockRepository
|
|
}
|
|
}
|
|
|
|
impl SessionLockRepository for FileSystemSessionLockRepository {
|
|
fn try_lock(&self, session_dir: &Path) -> Result<bool, RepositoryError> {
|
|
let path = session_dir.join(".lock");
|
|
let pid = std::process::id();
|
|
|
|
match std::fs::OpenOptions::new()
|
|
.create_new(true)
|
|
.write(true)
|
|
.open(&path)
|
|
{
|
|
Ok(mut file) => {
|
|
write!(file, "{pid}")?;
|
|
file.sync_all()?;
|
|
return Ok(true);
|
|
}
|
|
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
|
|
Err(e) => return Err(RepositoryError::Io(e)),
|
|
}
|
|
|
|
let content = std::fs::read_to_string(&path).unwrap_or_default();
|
|
if let Ok(existing_pid) = content.trim().parse::<u32>() {
|
|
if self.is_alive(existing_pid) {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
|
|
let tmp = path.with_extension("lock.tmp");
|
|
{
|
|
let mut tmp_file = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.truncate(true)
|
|
.write(true)
|
|
.open(&tmp)?;
|
|
write!(tmp_file, "{pid}")?;
|
|
tmp_file.sync_all()?;
|
|
}
|
|
std::fs::rename(&tmp, &path)?;
|
|
if let Some(parent) = path.parent() {
|
|
let _ = std::fs::File::open(parent).and_then(|d| d.sync_all());
|
|
}
|
|
Ok(true)
|
|
}
|
|
|
|
fn unlock(&self, session_dir: &Path) -> Result<(), RepositoryError> {
|
|
let path = session_dir.join(".lock");
|
|
let _ = std::fs::remove_file(path);
|
|
Ok(())
|
|
}
|
|
|
|
fn is_alive(&self, pid: u32) -> bool {
|
|
let pid_signed: i32 = match pid.try_into() {
|
|
Ok(p) => p,
|
|
Err(_) => return false,
|
|
};
|
|
if unsafe { libc::kill(pid_signed, 0) != 0 } {
|
|
return false;
|
|
}
|
|
let proc_exe = std::path::PathBuf::from(format!("/proc/{pid}/exe"));
|
|
if let Ok(target) = std::fs::read_link(&proc_exe) {
|
|
if let Ok(exe) = std::env::current_exe() {
|
|
if target != exe {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|