#![allow(unsafe_code)]
use std::fs::{self, File, OpenOptions};
use std::io::{self, Read};
use std::path::PathBuf;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 3 {
eprintln!("Usage: lock_helper <lock-file> <signal-file>");
std::process::exit(1);
}
let lock_path = PathBuf::from(&args[1]);
let signal_path = PathBuf::from(&args[2]);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(&lock_path)
.unwrap_or_else(|e| {
eprintln!("failed to open lock file {}: {e}", lock_path.display());
std::process::exit(1);
});
acquire_lock(&file, &lock_path);
fs::write(&signal_path, b"ready").unwrap_or_else(|e| {
eprintln!("failed to write signal file {}: {e}", signal_path.display());
std::process::exit(1);
});
let mut buf = [0u8; 1];
let _ = io::stdin().read(&mut buf);
drop(file);
}
fn acquire_lock(file: &File, lock_path: &std::path::Path) {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
loop {
if try_lock(file) {
return;
}
if std::time::Instant::now() >= deadline {
eprintln!(
"lock_helper: timed out acquiring lock on {}",
lock_path.display()
);
std::process::exit(1);
}
std::thread::sleep(std::time::Duration::from_millis(50));
}
}
#[cfg(windows)]
fn try_lock(file: &File) -> bool {
use std::os::windows::io::AsRawHandle;
use windows_sys::Win32::Storage::FileSystem::{
LockFileEx, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY,
};
use windows_sys::Win32::System::IO::OVERLAPPED;
let handle = file.as_raw_handle();
let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() };
#[allow(clippy::ptr_as_ptr)]
let result = unsafe {
LockFileEx(
handle as *mut _,
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0,
1,
0,
&mut overlapped,
)
};
result != 0
}
#[cfg(unix)]
fn try_lock(file: &File) -> bool {
use std::os::unix::io::AsRawFd;
let fd = file.as_raw_fd();
let result = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
result == 0
}
#[cfg(not(any(unix, windows)))]
fn try_lock(_file: &File) -> bool {
true
}