cfgd_core/util/
apply_lock.rs1use crate::errors;
2
3#[cfg(unix)]
7type LockFile = nix::fcntl::Flock<std::fs::File>;
8#[cfg(windows)]
9type LockFile = std::fs::File;
10
11#[derive(Debug)]
13pub struct ApplyLockGuard {
14 _file: LockFile,
15 _path: std::path::PathBuf,
16}
17
18impl Drop for ApplyLockGuard {
19 fn drop(&mut self) {
20 if let Err(e) = self._file.set_len(0) {
23 tracing::debug!(path = ?self._path, error = %e, "failed to clear apply-lock PID on drop");
24 }
25 }
26}
27
28#[cfg(unix)]
35pub fn acquire_apply_lock(state_dir: &std::path::Path) -> errors::Result<ApplyLockGuard> {
36 use std::io::Write;
37
38 std::fs::create_dir_all(state_dir)?;
39 let lock_path = state_dir.join("apply.lock");
40
41 let file = std::fs::OpenOptions::new()
42 .create(true)
43 .truncate(false)
44 .read(true)
45 .write(true)
46 .open(&lock_path)?;
47
48 let mut locked = nix::fcntl::Flock::lock(file, nix::fcntl::FlockArg::LockExclusiveNonblock)
49 .map_err(|(_file, errno)| {
50 if errno == nix::errno::Errno::EWOULDBLOCK {
51 let holder = std::fs::read_to_string(&lock_path).unwrap_or_default();
52 errors::CfgdError::from(errors::StateError::ApplyLockHeld {
53 holder: format!("pid {}", holder.trim()),
54 })
55 } else {
56 errors::CfgdError::from(std::io::Error::from(errno))
57 }
58 })?;
59
60 locked.set_len(0)?;
62 write!(locked, "{}", std::process::id())?;
63 locked.sync_all()?;
64
65 Ok(ApplyLockGuard {
66 _file: locked,
67 _path: lock_path,
68 })
69}
70
71#[cfg(windows)]
78pub fn acquire_apply_lock(state_dir: &std::path::Path) -> errors::Result<ApplyLockGuard> {
79 use std::io::Write;
80 use std::os::windows::io::AsRawHandle;
81 use windows_sys::Win32::Storage::FileSystem::{
82 LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx,
83 };
84
85 std::fs::create_dir_all(state_dir)?;
86 let lock_path = state_dir.join("apply.lock");
87
88 let file = std::fs::OpenOptions::new()
89 .create(true)
90 .truncate(false)
91 .read(true)
92 .write(true)
93 .open(&lock_path)?;
94
95 let handle = file.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE;
96 let mut overlapped: windows_sys::Win32::System::IO::OVERLAPPED = unsafe { std::mem::zeroed() };
100 let ret = unsafe {
106 LockFileEx(
107 handle,
108 LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
109 0,
110 1,
111 0,
112 &mut overlapped,
113 )
114 };
115 if ret == 0 {
116 let err = std::io::Error::last_os_error();
117 if err.raw_os_error() == Some(33) {
119 let holder = std::fs::read_to_string(&lock_path).unwrap_or_default();
120 return Err(errors::StateError::ApplyLockHeld {
121 holder: format!("pid {}", holder.trim()),
122 }
123 .into());
124 }
125 return Err(err.into());
126 }
127
128 let mut f = file;
129 f.set_len(0)?;
130 write!(f, "{}", std::process::id())?;
131 f.sync_all()?;
132
133 Ok(ApplyLockGuard {
134 _file: f,
135 _path: lock_path,
136 })
137}