use crate::errors;
#[cfg(unix)]
type LockFile = nix::fcntl::Flock<std::fs::File>;
#[cfg(windows)]
type LockFile = std::fs::File;
#[derive(Debug)]
pub struct ApplyLockGuard {
_file: LockFile,
_path: std::path::PathBuf,
}
impl Drop for ApplyLockGuard {
fn drop(&mut self) {
if let Err(e) = self._file.set_len(0) {
tracing::debug!(path = ?self._path, error = %e, "failed to clear apply-lock PID on drop");
}
}
}
#[cfg(unix)]
pub fn acquire_apply_lock(state_dir: &std::path::Path) -> errors::Result<ApplyLockGuard> {
use std::io::Write;
std::fs::create_dir_all(state_dir)?;
let lock_path = state_dir.join("apply.lock");
let file = std::fs::OpenOptions::new()
.create(true)
.truncate(false)
.read(true)
.write(true)
.open(&lock_path)?;
let mut locked = nix::fcntl::Flock::lock(file, nix::fcntl::FlockArg::LockExclusiveNonblock)
.map_err(|(_file, errno)| {
if errno == nix::errno::Errno::EWOULDBLOCK {
let holder = std::fs::read_to_string(&lock_path).unwrap_or_default();
errors::CfgdError::from(errors::StateError::ApplyLockHeld {
holder: format!("pid {}", holder.trim()),
})
} else {
errors::CfgdError::from(std::io::Error::from(errno))
}
})?;
locked.set_len(0)?;
write!(locked, "{}", std::process::id())?;
locked.sync_all()?;
Ok(ApplyLockGuard {
_file: locked,
_path: lock_path,
})
}
#[cfg(windows)]
pub fn acquire_apply_lock(state_dir: &std::path::Path) -> errors::Result<ApplyLockGuard> {
use std::io::Write;
use std::os::windows::io::AsRawHandle;
use windows_sys::Win32::Storage::FileSystem::{
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx,
};
std::fs::create_dir_all(state_dir)?;
let lock_path = state_dir.join("apply.lock");
let file = std::fs::OpenOptions::new()
.create(true)
.truncate(false)
.read(true)
.write(true)
.open(&lock_path)?;
let handle = file.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE;
let mut overlapped: windows_sys::Win32::System::IO::OVERLAPPED = unsafe { std::mem::zeroed() };
let ret = unsafe {
LockFileEx(
handle,
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0,
1,
0,
&mut overlapped,
)
};
if ret == 0 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(33) {
let holder = std::fs::read_to_string(&lock_path).unwrap_or_default();
return Err(errors::StateError::ApplyLockHeld {
holder: format!("pid {}", holder.trim()),
}
.into());
}
return Err(err.into());
}
let mut f = file;
f.set_len(0)?;
write!(f, "{}", std::process::id())?;
f.sync_all()?;
Ok(ApplyLockGuard {
_file: f,
_path: lock_path,
})
}