use std::path::PathBuf;
#[cfg(target_os = "macos")]
#[derive(Debug)]
#[allow(dead_code)]
pub(super) struct WrapperSingleInstanceLock {
_file: std::fs::File,
}
#[allow(dead_code)]
pub(super) fn wrapper_lock_path() -> Option<PathBuf> {
dirs::cache_dir().map(|d| d.join("Freenet").join("wrapper.lock"))
}
#[derive(Debug)]
#[allow(dead_code)]
pub(super) enum AcquireWrapperLockOutcome {
Acquired(WrapperSingleInstanceLock),
AnotherWrapperRunning,
UnavailableSoProceed,
}
#[cfg(target_os = "macos")]
#[allow(dead_code)]
pub(super) fn acquire_wrapper_single_instance_lock() -> AcquireWrapperLockOutcome {
use std::os::unix::io::AsRawFd;
let Some(lock_path) = wrapper_lock_path() else {
tracing::warn!("Wrapper lock: cache directory unresolvable; proceeding without lock");
return AcquireWrapperLockOutcome::UnavailableSoProceed;
};
if let Some(parent) = lock_path.parent() {
if let Err(e) = std::fs::create_dir_all(parent) {
tracing::warn!(
"Wrapper lock: failed to create {}: {}; proceeding without lock",
parent.display(),
e
);
return AcquireWrapperLockOutcome::UnavailableSoProceed;
}
}
let file = match std::fs::OpenOptions::new()
.create(true)
.truncate(false)
.write(true)
.open(&lock_path)
{
Ok(f) => f,
Err(e) => {
tracing::warn!(
"Wrapper lock: failed to open {}: {}; proceeding without lock",
lock_path.display(),
e
);
return AcquireWrapperLockOutcome::UnavailableSoProceed;
}
};
let rc = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) };
if rc != 0 {
AcquireWrapperLockOutcome::AnotherWrapperRunning
} else {
AcquireWrapperLockOutcome::Acquired(WrapperSingleInstanceLock { _file: file })
}
}
#[cfg(target_os = "windows")]
#[derive(Debug)]
#[allow(dead_code)]
pub(super) struct WrapperSingleInstanceLock {
handle: winapi::um::winnt::HANDLE,
}
#[cfg(target_os = "windows")]
impl Drop for WrapperSingleInstanceLock {
fn drop(&mut self) {
unsafe {
winapi::um::handleapi::CloseHandle(self.handle);
}
}
}
#[cfg(target_os = "windows")]
pub(super) const WRAPPER_MUTEX_NAME: &str = "FreenetWrapperSingleInstance";
#[cfg(target_os = "windows")]
#[allow(dead_code)]
pub(super) fn acquire_wrapper_single_instance_lock() -> AcquireWrapperLockOutcome {
use std::os::windows::ffi::OsStrExt;
let name: Vec<u16> = std::ffi::OsStr::new(WRAPPER_MUTEX_NAME)
.encode_wide()
.chain(std::iter::once(0))
.collect();
let handle = unsafe {
winapi::um::synchapi::CreateMutexW(
std::ptr::null_mut(),
winapi::shared::minwindef::FALSE,
name.as_ptr(),
)
};
if handle.is_null() {
tracing::warn!(
"Wrapper lock: CreateMutexW failed (error {}); proceeding without lock",
unsafe { winapi::um::errhandlingapi::GetLastError() }
);
return AcquireWrapperLockOutcome::UnavailableSoProceed;
}
let already_exists = unsafe { winapi::um::errhandlingapi::GetLastError() }
== winapi::shared::winerror::ERROR_ALREADY_EXISTS;
if already_exists {
unsafe {
winapi::um::handleapi::CloseHandle(handle);
}
AcquireWrapperLockOutcome::AnotherWrapperRunning
} else {
AcquireWrapperLockOutcome::Acquired(WrapperSingleInstanceLock { handle })
}
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
#[allow(dead_code)]
pub(super) fn acquire_wrapper_single_instance_lock() -> AcquireWrapperLockOutcome {
AcquireWrapperLockOutcome::UnavailableSoProceed
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
#[derive(Debug)]
#[allow(dead_code)]
pub(super) struct WrapperSingleInstanceLock;
#[cfg(any(target_os = "windows", target_os = "macos"))]
pub(super) static FIRST_RUN_OPENER_SPAWNED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);