use anyhow::{Context, Result};
pub(crate) fn write_pid_file(path: &std::path::Path) -> Result<()> {
let pid = std::process::id().to_string();
#[cfg(unix)]
{
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(path)
.with_context(|| format!("Failed to create PID file: {}", path.display()))?;
file.write_all(pid.as_bytes())
.with_context(|| format!("Failed to write PID file: {}", path.display()))?;
}
#[cfg(not(unix))]
{
std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.and_then(|mut f| {
use std::io::Write;
f.write_all(pid.as_bytes())
})
.with_context(|| format!("Failed to create PID file: {}", path.display()))?;
}
log::info!("PID file written: {}", path.display());
Ok(())
}
pub(crate) fn absolute_pid_file_path(path: &std::path::Path) -> std::path::PathBuf {
std::path::absolute(path).unwrap_or_else(|_| path.to_path_buf())
}
static IPC_PID_FILE_PATH: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new();
static IPC_PID_FILE_CLEANUP_DONE: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub(crate) fn register_ipc_pid_file_atexit(path: &std::path::Path) {
let _ = IPC_PID_FILE_PATH.set(absolute_pid_file_path(path));
let ret = unsafe { libc::atexit(remove_ipc_pid_file_at_exit) };
if ret != 0 {
log::warn!("Failed to register IPC PID file cleanup with atexit");
}
}
extern "C" fn remove_ipc_pid_file_at_exit() {
if IPC_PID_FILE_CLEANUP_DONE.load(std::sync::atomic::Ordering::Acquire) {
return;
}
if let Some(path) = IPC_PID_FILE_PATH.get() {
let _ = std::fs::remove_file(path);
}
}
pub(crate) fn cleanup_ipc_pid_file(path: &std::path::Path) {
let cleanup_path = IPC_PID_FILE_PATH
.get()
.cloned()
.unwrap_or_else(|| absolute_pid_file_path(path));
if let Err(e) = std::fs::remove_file(&cleanup_path) {
log::debug!(
"Could not remove PID file {}: {}",
cleanup_path.display(),
e
);
}
IPC_PID_FILE_CLEANUP_DONE.store(true, std::sync::atomic::Ordering::Release);
}