gix_tempfile/
signal.rs

1use once_cell::sync::Lazy;
2
3use crate::REGISTRY;
4
5/// Initialize signal handlers and other state to keep track of tempfiles, and **must be called before the first tempfile is created**,
6/// allowing to set the `mode` in which signal handlers are installed.
7///
8/// Only has an effect the first time it is called.
9///
10/// Note that it is possible to not call this function and instead call
11/// [`registry::cleanup_tempfiles_signal_safe()`][crate::registry::cleanup_tempfiles_signal_safe()]
12/// from a signal handler under the application's control.
13pub fn setup(mode: handler::Mode) {
14    handler::MODE.store(mode as usize, std::sync::atomic::Ordering::SeqCst);
15    Lazy::force(&REGISTRY);
16}
17
18///
19pub mod handler {
20    use std::sync::atomic::AtomicUsize;
21
22    pub(crate) static MODE: AtomicUsize = AtomicUsize::new(Mode::None as usize);
23
24    /// Define how our signal handlers act
25    #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
26    pub enum Mode {
27        /// Do not install a signal handler at all, but have somebody else call our handler directly.
28        None = 0,
29        /// Delete all remaining registered tempfiles on termination.
30        DeleteTempfilesOnTermination = 1,
31        /// Delete all remaining registered tempfiles on termination and emulate the default handler behaviour.
32        ///
33        /// This typically leads to the process being aborted.
34        DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour = 2,
35    }
36
37    impl Default for Mode {
38        /// By default we will emulate the default behaviour and abort the process.
39        ///
40        /// While testing, we will not abort the process.
41        fn default() -> Self {
42            if cfg!(test) {
43                Mode::DeleteTempfilesOnTermination
44            } else {
45                Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour
46            }
47        }
48    }
49
50    /// On linux we can handle the actual signal as we know it.
51    #[cfg(not(windows))]
52    pub(crate) fn cleanup_tempfiles_nix(sig: &libc::siginfo_t) {
53        crate::registry::cleanup_tempfiles_signal_safe();
54        let restore_original_behaviour = Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour as usize;
55        if MODE.load(std::sync::atomic::Ordering::SeqCst) == restore_original_behaviour {
56            signal_hook::low_level::emulate_default_handler(sig.si_signo).ok();
57        }
58    }
59
60    /// On windows, assume sig-term and emulate sig-term unconditionally.
61    #[cfg(windows)]
62    pub(crate) fn cleanup_tempfiles_windows() {
63        crate::registry::cleanup_tempfiles_signal_safe();
64        let restore_original_behaviour = Mode::DeleteTempfilesOnTerminationAndRestoreDefaultBehaviour as usize;
65        if MODE.load(std::sync::atomic::Ordering::SeqCst) == restore_original_behaviour {
66            signal_hook::low_level::emulate_default_handler(signal_hook::consts::SIGTERM).ok();
67        }
68    }
69
70    #[cfg(test)]
71    mod tests {
72        use std::path::Path;
73
74        use crate::{AutoRemove, ContainingDirectory};
75
76        fn filecount_in(path: impl AsRef<Path>) -> usize {
77            std::fs::read_dir(path).expect("valid dir").count()
78        }
79
80        #[test]
81        fn various_termination_signals_remove_tempfiles_unconditionally() -> Result<(), Box<dyn std::error::Error>> {
82            crate::signal::setup(Default::default());
83            let dir = tempfile::tempdir()?;
84            for sig in signal_hook::consts::TERM_SIGNALS {
85                let _tempfile = crate::new(dir.path(), ContainingDirectory::Exists, AutoRemove::Tempfile)?;
86                assert_eq!(
87                    filecount_in(dir.path()),
88                    1,
89                    "only one tempfile exists no matter the iteration"
90                );
91                signal_hook::low_level::raise(*sig)?;
92                assert_eq!(
93                    filecount_in(dir.path()),
94                    0,
95                    "the signal triggers removal but won't terminate the process (anymore)"
96                );
97            }
98            Ok(())
99        }
100    }
101}