#![deny(missing_docs, unsafe_code, rust_2018_idioms)]
use std::{
io,
path::{Path, PathBuf},
sync::atomic::AtomicUsize,
};
use dashmap::DashMap;
use once_cell::sync::Lazy;
use tempfile::NamedTempFile;
mod fs;
pub use fs::{create_dir, remove_dir};
mod handler;
mod registration;
static SIGNAL_HANDLER_MODE: AtomicUsize = AtomicUsize::new(SignalHandlerMode::default() as usize);
static NEXT_MAP_INDEX: AtomicUsize = AtomicUsize::new(0);
static REGISTER: Lazy<DashMap<usize, Option<ForksafeTempfile>>> = Lazy::new(|| {
for sig in signal_hook::consts::TERM_SIGNALS {
#[allow(unsafe_code)]
unsafe {
#[cfg(not(windows))]
{
signal_hook_registry::register_sigaction(*sig, handler::cleanup_tempfiles_nix)
}
#[cfg(windows)]
{
signal_hook::low_level::register(*sig, handler::cleanup_tempfiles_windows)
}
}
.expect("signals can always be installed");
}
DashMap::new()
});
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
pub enum SignalHandlerMode {
DeleteTempfilesOnTermination = 0,
DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour = 1,
}
impl SignalHandlerMode {
const fn default() -> Self {
#[cfg(not(test))]
return SignalHandlerMode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour;
#[cfg(test)]
return SignalHandlerMode::DeleteTempfilesOnTermination;
}
}
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
pub enum ContainingDirectory {
Exists,
CreateAllRaceProof(create_dir::Retries),
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum AutoRemove {
Tempfile,
TempfileAndEmptyParentDirectoriesUntil {
boundary_directory: PathBuf,
},
}
impl AutoRemove {
pub(crate) fn execute_best_effort(self, directory_to_potentially_delete: &Path) -> Option<PathBuf> {
match self {
AutoRemove::Tempfile => None,
AutoRemove::TempfileAndEmptyParentDirectoriesUntil { boundary_directory } => {
crate::remove_dir::empty_until_boundary(directory_to_potentially_delete, &boundary_directory).ok();
Some(boundary_directory)
}
}
}
}
pub struct Registration {
id: usize,
}
struct ForksafeTempfile {
inner: NamedTempFile,
cleanup: AutoRemove,
owning_process_id: u32,
}
impl ForksafeTempfile {
fn new(inner: NamedTempFile, cleanup: AutoRemove) -> Self {
ForksafeTempfile {
inner,
cleanup,
owning_process_id: std::process::id(),
}
}
}
pub fn new(
containing_directory: impl AsRef<Path>,
directory: ContainingDirectory,
cleanup: AutoRemove,
) -> io::Result<Registration> {
Registration::new(containing_directory, directory, cleanup)
}
pub fn at_path(
path: impl AsRef<Path>,
directory: ContainingDirectory,
cleanup: AutoRemove,
) -> io::Result<Registration> {
Registration::at_path(path, directory, cleanup)
}
pub fn force_setup(mode: SignalHandlerMode) {
SIGNAL_HANDLER_MODE.store(mode as usize, std::sync::atomic::Ordering::Relaxed);
Lazy::force(®ISTER);
}