1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! `self-replace` is a crate that allows binaries to replace themselves with newer
//! versions or to uninstall themselves.  On Unix systems this is a simple feat, but
//! on Windows a few hacks are needed which is why this crate exists.
//!
//! This is a useful operation when working with single-executable utilties that
//! want to implement a form of self updating or self uninstallation.
//!
//! ## Self Deletion
//!
//! The [`self_delete`] function schedules a binary for self deletion.  On Unix the
//! file system entry is immediately deleted, on Windows the file is deleted after the
//! process shuts down.  Note that you should not use this function to be followed up
//! by a replacement operation, for that use [`self_replace`] as on Windows the file
//! will still be locked.
//!
//! ```
//! # fn foo() -> Result<(), std::io::Error> {
//! self_replace::self_delete()?;
//! # Ok(()) }
//! ```
//!
//! ## Self Replacing
//!
//! This replaces the binary with another binary.  The provided path is copied over and
//! if the function successfully completes, you can delete the source binary.
//!
//! ```
//! use std::fs;
//!
//! # fn foo() -> Result<(), std::io::Error> {
//! let new_binary = "/path/to/new/binary";
//! self_replace::self_replace(&new_binary)?;
//! fs::remove_file(&new_binary)?;
//! # Ok(()) }
//! ```
//!
//! ## Implementation
//!
//! The way this is implemented depends on the operating system.  On UNIX systems you
//! can usually not directly write into an executable, but you can swap it out which is
//! exactly what this is doing.  For deleting, the file is just unlinked, for replacing
//! a new file is placed right next to the current executable and an atomic move with
//! `rename` is performed.
//!
//! On Windows the situation is trickier because when an executable launches it can be
//! renamed, but it cannot be unlinked.  This means that we cannot clean up after
//! ourselves easily.  In either case, we first move our executable aside so the name
//! on the file system is made available for the new executable.  Then for both
//! deleting and replacing, we create a copy of our own executable first.  After this we
//! open that copied executable with `FILE_FLAG_DELETE_ON_CLOSE` and schedule it to
//! be spawned when we shut down.  Just before we shut down this copy is then spawned.
//! This library contains a special glue code that detects this copy of the executable
//! and does nothing else but waiting for the parent to quit and to then delete the
//! parent executable.  There is an extra hack in there in that it spawns another system
//! executable that stays alive until after we shut down (in our case `ping`) to make
//! the self deletion of the copy work.  This is necessary because our running executable
//! must not be the last user of that file handle as otherwise the deletion
//! won't work as the executable still cannot be deleted.  Presumably this is
//! because `CreateProcess` and friends do not open the executable with
//! `FILE_FLAG_DELETE_ON_CLOSE`.
use std::io;
use std::path::Path;

#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;

/// Deletes the executable in a platform independent manner.
///
/// The deletion on windows is delayed until the process shuts down.  For updating
/// instead of deleting, use [`self_replace`] instead.  Not that on Windows you can
/// only call this function once during the execution of the program.
///
/// ```
/// # fn foo() -> Result<(), std::io::Error> {
/// self_replace::self_delete()?;
/// # Ok(()) }
/// ```
pub fn self_delete() -> Result<(), io::Error> {
    #[cfg(unix)]
    {
        crate::unix::self_delete()
    }
    #[cfg(windows)]
    {
        crate::windows::self_delete()
    }
}

/// Replaces the running executable with a different one.
///
/// This replaces the binary with another binary.  The provided path is copied over and
/// if the function successfully completes, you can delete the source binary.
///
/// ```
/// use std::fs;
///
/// # fn foo() -> Result<(), std::io::Error> {
/// let new_binary = "/path/to/new/binary";
/// self_replace::self_replace(&new_binary)?;
/// fs::remove_file(&new_binary)?;
/// # Ok(()) }
/// ```
///
/// Note that after this function concludes, the new executable is already placed at the
/// old location, and the previous executable has been moved to a temporary alternative
/// location.  This also means that if you want to manipulate that file further (for
/// instance to change the permissions) you can do so.
///
/// By default the permissions of the original file are restored.
pub fn self_replace<P: AsRef<Path>>(new_executable: P) -> Result<(), io::Error> {
    #[cfg(unix)]
    {
        crate::unix::self_replace(new_executable.as_ref())
    }
    #[cfg(windows)]
    {
        crate::windows::self_replace(new_executable.as_ref())
    }
}