diff --git a/crates/xy/src/pidfile.rs b/crates/xy/src/pidfile.rs new file mode 100644 index 0000000..3bf6ee0 --- /dev/null +++ b/crates/xy/src/pidfile.rs @@ -0,0 +1,46 @@ +use std::fs::{File, OpenOptions}; +use std::io::Write; +use std::os::unix::fs::OpenOptionsExt; +use std::path::{Path, PathBuf}; + +#[derive(Debug)] +pub struct PidFile { + path: PathBuf, + _file: File, +} + +impl PidFile { + pub fn acquire(path: &Path) -> std::io::Result { + let mut f = OpenOptions::new() + .write(true).create_new(true).mode(0o600).open(path)?; + writeln!(f, "{}", std::process::id())?; + Ok(Self { path: path.to_path_buf(), _file: f }) + } +} + +impl Drop for PidFile { + fn drop(&mut self) { let _ = std::fs::remove_file(&self.path); } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + #[test] + fn acquire_then_second_fails() { + let dir = tempdir().unwrap(); + let p = dir.path().join("x.pid"); + let _g = PidFile::acquire(&p).unwrap(); + let err = PidFile::acquire(&p).unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::AlreadyExists); + } + + #[test] + fn drop_removes_file() { + let dir = tempdir().unwrap(); + let p = dir.path().join("x.pid"); + { let _g = PidFile::acquire(&p).unwrap(); assert!(p.exists()); } + assert!(!p.exists()); + } +}