A smol Zig library that lets a running executable replace (or delete) itself.
This can be used, for instance, in applications implementing a self update feature keeping the current installation path intact.
Only windows, linux, and unix like systems (macOS, *BSD) are supported.
- Add the library to your build.zig.zon:
zig fetch --save git+https://github.com/weezy20/replace-exe.git- Add it to your build.zig:
const exe = b.addExecutable(.{
...
});
// Add the replace_exe dependency
const libreplace_exe = b.dependency("replace_exe", .{});
exe.root_module.addImport("replace_exe", libreplace_exe.module("replace_exe"));- Call
selfReplaceorselfDeletefrom your code:
// step 1: import
const re = @import("replace_exe");
// step 2: register hook as soon as possible in main(). This is a no-op on non-windows OS:
pub fn main() !void {
const allocator = std.heap.page_allocator;
// CRITICAL (windows): Call init() BEFORE any application logic.
// On Windows, this detects if the process is a cleanup helper and exits immediately.
// Any code before this line will run in helper processes too if spawned using selfDelete or selfReplace in windows!
re.init(allocator); // or re.init(null) if you want to go with the default ArenaAllocator(std.heap.page_allocator) for `selfDeleteInit()` operations
// your logic here..
}
// Replace current executable with a new one
try re.selfReplace(allocator, "path/to/new/executable");
// Warning: Deletes current executable
try re.selfDelete();Note: On windows, if you're deleting the parent folder itself of the current exe itself then you might not want to use selfDelete directly but instead provide the current parent dir (or really any dir that you want to prevent from being locked) using selfDeleteExcludingPath(path: []const u8) where the function ensures that no temporary exes are put into that path, thereby preventing its deletion for the lifetime of the current running executable.
The current strategy is to place temporary exes in %TMP% or %TEMP% & if that fails due to cross filesystem paths (exe & temp dir being on different filesystems) we fallback to storing the temp exe helpers in current parent of running exe unless selfDeleteExcludingPath(p) is provided in which case, the parent of p would be selected & as a fallback, we would go back to using the same parent dir of current-exe.
Recommended guidelines:
- Call these functions at most once each per program execution
- (UNSAFE windows) If using both, prefer do selfReplace before selfDelete - This will still not cleanup the helpers properly as the original file would've been moved & will spawn the new/exe/ which my not have the call to
init()thereby preventing self-cleanup. It's best to avoid this.- Ideally, make them mutually exclusive in your application logic
Some example code is provided in the demo folder:
demo.zig is an application that calls selfReplace to replace itself with the updated version demo2.zig
Build the demo applications with:
# Build zig demo exes:
zig build -Ddemo
# Build the demo-c executable alongside the above:
zig build -Ddemo -DcapiThen run the first demo exe:
./zig-out/bin/demo delete # self-delete
./zig-out/bin/demo replace ?</path/to/new/exe> # self-replace; default path is ./zig-out/bin/demo2Try out the demo-c exe which calls libreplace-exe from C:
./zig-out/bin/demo-c delete
# verify demo-c is deletedIf you're using it via FFI, the function signatures are defined in replace_exe.h and can be used in your code as the following:
selfReplacebecomesself_replace(const char* path)selfDeletebecomesself_delete()selfDeleteExcludingPathbecomesself_delete_excluding_path(const char* path)init(?std.mem.Allocator)becomesinit()
See c_api.zig for definitions. Building the library as a shared object or static library for use with C/C++:
Building:
# This builds a .so shared library like libreplace-exe.so that you can link against
zig build -Dcapi -Dso -Doptimization=ReleaseFast
# Or if you prefer a static library:
zig build -Dcapi -Doptimization=ReleaseFastExample usage in C (using dynamic library): See demo.c
- Build your C app using either of the generated libraries:
gcc demo/demo.c -Izig-out/include -Lzig-out/lib -lreplace-exe -o test- Run the C demo:
LD_LIBRARY_PATH=zig-out/lib ./test /path/to/new/executable- Run again to verify replacement:
LD_LIBRARY_PATH=zig-out/lib ./testOr if you prefer go (using cgo): See demo/main.go for an example.
- Build your Go app linking
libreplace-exe.aorlibreplace-exe.so:
go build -o demo-go demo/main.go- Run the Go demo:
./demo-go replace /path/to/new/executable