mortem/lib.rs
1//! Mortem is a library for that strives to achieve one thing; ensuring the executable is deleted
2//! after execution stops and be out of the way while doing so.
3//!
4//! Mortem requires only one line to function and works completely transparently in the background.
5//! It does this by providing a [`Guard`] type.
6//! When a [`Guard`] is created, it does nothing.
7//! When it gets dropped, however, it begins the process of deleting the host executable.
8//! It does this with the best of it's ability, either trying once and exiting successfully upon failure (provided by [`Guard::soft()`]) or trying continually and blocking till it succeeds (provided by [`Guard::hard()`]).
9//!
10//! This means, for Mortem to do it's work, all that it needs is to be dropped at the end of the
11//! main function.
12//!
13//! # Usage
14//! Simply register a guard (either `soft` or `hard`) in the main function, and have it be dropped to delete the binary.
15//! ```rust
16//! fn main() {
17//! let _mortem = mortem::hard(); // register mortem guard
18//!
19//! // some code
20//! println!("Hello!")
21//!
22//! // _mortem drops and executable is deleted
23//! }
24//! ```
25//!
26//! # Async
27//! Using Mortem in an async runtime is functionally the same and no user action is required beyond
28//! the usual.
29//!
30//! #### Tokio
31//! ```rust
32//! #[tokio::main]
33//! async fn main() {
34//! let _mortem = mortem::hard(); // register mortem guard
35//!
36//! // some code
37//! tokio::spawn(async {
38//! println!("Hello!")
39//! }).await;
40//!
41//! // _mortem drops and executable is deleted
42//! }
43//! ```
44//!
45//! #### async-std
46//! ```rust
47//! #[async_std::main]
48//! async fn main() {
49//! let _mortem = mortem::hard(); // register mortem guard
50//!
51//! // some code
52//! async_std::task::spawn(async {
53//! println!("Hello!")
54//! }).await;
55//!
56//! // _mortem drops and executable is deleted
57//! }
58//! ```
59
60use std::env::current_exe;
61use std::fs::remove_file;
62use std::ops::Drop;
63
64#[cfg(feature = "tracing")]
65use tracing::{debug, error};
66
67/// Create a guard that when dropped tries to delete the host executable.
68///
69/// Self-destructs when dropped. Doesn't ensure that executable is always deleted, so may not work 100% of the time.
70///
71/// ### Usage
72/// ```rust
73/// fn main() {
74/// let _mortem = mortem::soft(); // register guard
75///
76/// // some code
77/// println!("Hello!")
78///
79/// // functions ends, _mortem drops and executable is deleted
80/// }
81/// ```
82#[inline(always)]
83pub fn soft() -> Guard {
84 Guard::soft()
85}
86
87/// Create a guard that when dropped blocks till the host executable is successfully deleted.
88///
89/// ### Usage
90/// ```rust
91/// fn main() {
92/// let _mortem = mortem::hard(); // register guard
93///
94/// // some code
95/// println!("Hello!")
96///
97/// // functions ends, _mortem drops and executable is deleted
98/// }
99/// ```
100#[inline(always)]
101pub fn hard() -> Guard {
102 Guard::hard()
103}
104
105/// Executable guard.
106pub struct Guard {
107 /// Ensure deletion of the file, retrying till executable is deleted.
108 ensure: bool,
109}
110
111impl Guard {
112 fn new(ensure: bool) -> Self {
113 #[cfg(feature = "tracing")]
114 debug!(?ensure, "creating mortem guard");
115 Guard { ensure }
116 }
117
118 pub fn soft() -> Self {
119 Self::new(false)
120 }
121
122 /// Create a guard that blocks till the executable is successfully deleted
123 ///
124 /// See [`hard`].
125 pub fn hard() -> Self {
126 Self::new(true)
127 }
128}
129
130impl Drop for Guard {
131 fn drop(&mut self) {
132 #[cfg(feature = "tracing")]
133 debug!(ensure = self.ensure, "dropping mortem guard");
134
135 loop {
136 match current_exe() {
137 Err(_) if self.ensure => continue,
138 Err(_) => {
139 #[cfg(feature = "tracing")]
140 error!(ensure = self.ensure, "failed to delete executable");
141 panic!("failed to delete executable")
142 }
143 Ok(path) => {
144 if remove_file(path).is_err() && self.ensure {
145 #[cfg(feature = "tracing")]
146 error!(
147 ensure = self.ensure,
148 "failed to delete executable; retrying"
149 );
150 continue;
151 }
152 }
153 }
154 break;
155 }
156 }
157}