This commit is contained in:
@ -7,86 +7,85 @@
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace dropshell {
|
||||
namespace dropshell
|
||||
{
|
||||
|
||||
namespace localfile
|
||||
{
|
||||
|
||||
namespace localfile {
|
||||
|
||||
std::string dropshell_json() {
|
||||
// Try ~/.config/dropshell/dropshell.json
|
||||
std::string homedir = localpath::current_user_home();
|
||||
if (!homedir.empty()) {
|
||||
fs::path user_path = fs::path(homedir) / ".config" / "dropshell" / filenames::dropshell_json;
|
||||
return user_path.string();
|
||||
std::string dropshell_json()
|
||||
{
|
||||
return localpath::dropshell_dir() + "/" + filenames::dropshell_json;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string server_json(const std::string &server_name) {
|
||||
std::string serverpath = localpath::server(server_name);
|
||||
return (serverpath.empty() ? "" : (fs::path(serverpath) / filenames::server_json).string());
|
||||
}
|
||||
std::string server_json(const std::string &server_name)
|
||||
{
|
||||
std::string serverpath = localpath::server(server_name);
|
||||
return (serverpath.empty() ? "" : (fs::path(serverpath) / filenames::server_json).string());
|
||||
}
|
||||
|
||||
std::string service_env(const std::string &server_name, const std::string &service_name) {
|
||||
std::string servicepath = localpath::service(server_name, service_name);
|
||||
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::service_env).string());
|
||||
}
|
||||
std::string service_env(const std::string &server_name, const std::string &service_name)
|
||||
{
|
||||
std::string servicepath = localpath::service(server_name, service_name);
|
||||
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::service_env).string());
|
||||
}
|
||||
|
||||
std::string template_info_env(const std::string &server_name, const std::string &service_name)
|
||||
std::string template_info_env(const std::string &server_name, const std::string &service_name)
|
||||
{
|
||||
std::string servicepath = localpath::service(server_name, service_name);
|
||||
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::template_info_env).string());
|
||||
}
|
||||
|
||||
std::string template_example()
|
||||
{
|
||||
return localpath::agent_local() + "/template_example";
|
||||
}
|
||||
|
||||
std::string bb64()
|
||||
{
|
||||
return localpath::agent_local() + "/bb64";
|
||||
}
|
||||
|
||||
} // namespace localfile
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
namespace localpath
|
||||
{
|
||||
std::string servicepath = localpath::service(server_name, service_name);
|
||||
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::template_info_env).string());
|
||||
}
|
||||
|
||||
std::string template_example()
|
||||
{
|
||||
return localpath::agent_local() + "/template_example";
|
||||
}
|
||||
std::string dropshell_dir()
|
||||
{
|
||||
return current_user_home() + "/.dropshell";
|
||||
}
|
||||
|
||||
std::string bb64()
|
||||
{
|
||||
return localpath::agent_local() + "/bb64";
|
||||
}
|
||||
|
||||
} // namespace localfile
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
namespace localpath {
|
||||
std::string server(const std::string &server_name) {
|
||||
for (std::filesystem::path dir : gConfig().get_local_server_definition_paths())
|
||||
std::string server(const std::string &server_name)
|
||||
{
|
||||
for (std::filesystem::path dir : gConfig().get_local_server_definition_paths())
|
||||
if (fs::exists(dir / server_name))
|
||||
return dir / server_name;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string service(const std::string &server_name, const std::string &service_name) {
|
||||
std::string service(const std::string &server_name, const std::string &service_name)
|
||||
{
|
||||
std::string serverpath = localpath::server(server_name);
|
||||
return ((serverpath.empty() || service_name.empty()) ? "" : (serverpath+"/"+service_name));
|
||||
return ((serverpath.empty() || service_name.empty()) ? "" : (serverpath + "/" + service_name));
|
||||
}
|
||||
|
||||
std::string remote_versions(const std::string &server_name, const std::string &service_name)
|
||||
{
|
||||
std::string template_cache_path = localpath::template_cache();
|
||||
return ((template_cache_path.empty() || service_name.empty()) ? "" :
|
||||
(template_cache_path+"/remote_versions/"+service_name+".json"));
|
||||
}
|
||||
std::string agent_local()
|
||||
{
|
||||
return current_user_home()+"/.local/dropshell_agent/agent-local";
|
||||
return dropshell_dir() + "/agent-local";
|
||||
}
|
||||
std::string agent_remote()
|
||||
{
|
||||
return current_user_home() + "/.local/dropshell_agent/agent-remote";
|
||||
return dropshell_dir() + "/agent-remote";
|
||||
}
|
||||
std::string current_user_home()
|
||||
{
|
||||
char * homedir = std::getenv("HOME");
|
||||
char *homedir = std::getenv("HOME");
|
||||
if (homedir)
|
||||
{
|
||||
std::filesystem::path homedir_path(homedir);
|
||||
@ -96,37 +95,32 @@ namespace localpath {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string dropshell_files()
|
||||
{
|
||||
return current_user_home() + "/.local/dropshell_files";
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string backups()
|
||||
{
|
||||
return dropshell_files() + "/backups";
|
||||
if (!gConfig().is_config_set())
|
||||
return "";
|
||||
return gConfig().get_backups_path();
|
||||
}
|
||||
|
||||
std::string temp_files()
|
||||
{
|
||||
return dropshell_files() + "/temp_files";
|
||||
return dropshell_dir() + "/temp_files";
|
||||
}
|
||||
|
||||
std::string template_cache()
|
||||
{
|
||||
return dropshell_files() + "template_cache";
|
||||
return dropshell_dir() + "/template_cache";
|
||||
}
|
||||
|
||||
bool create_directories()
|
||||
{
|
||||
std::vector<std::filesystem::path> paths = {
|
||||
dropshell_files(),
|
||||
dropshell_dir(),
|
||||
agent_local(),
|
||||
agent_remote(),
|
||||
template_cache(),
|
||||
backups(),
|
||||
temp_files()
|
||||
};
|
||||
temp_files()};
|
||||
for (auto &p : gConfig().get_local_server_definition_paths())
|
||||
paths.push_back(p);
|
||||
|
||||
@ -139,7 +133,7 @@ namespace localpath {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace localpath
|
||||
} // namespace localpath
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// remote paths
|
||||
@ -159,21 +153,26 @@ namespace localpath {
|
||||
// |-- service.env (default service config)
|
||||
// |-- (other config files for specific server&service)
|
||||
|
||||
|
||||
remotefile::remotefile(const std::string &server_name, const std::string &user) :
|
||||
mServer_name(server_name), mUser(user) {}
|
||||
remotefile::remotefile(const std::string &server_name, const std::string &user) : mServer_name(server_name), mUser(user) {}
|
||||
|
||||
std::string remotefile::service_env(const std::string &service_name) const
|
||||
{
|
||||
return remotepath(mServer_name,mUser).service_config(service_name) + "/" + filenames::service_env;
|
||||
return remotepath(mServer_name, mUser).service_config(service_name) + "/" + filenames::service_env;
|
||||
}
|
||||
|
||||
|
||||
remotepath::remotepath(const std::string &server_name, const std::string &user) : mServer_name(server_name), mUser(user) {}
|
||||
|
||||
std::string remotepath::DROPSHELL_DIR() const
|
||||
{
|
||||
return ServerConfig(mServer_name).get_user_dir(mUser);
|
||||
{
|
||||
try
|
||||
{
|
||||
return ServerConfig(mServer_name).get_user_dir(mUser);
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
error << "Failed to get remote dropshell directory for " << mServer_name << "@" << mUser << std::endl;
|
||||
error << "Exception: " << e.what() << std::endl;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string remotepath::services() const
|
||||
@ -218,22 +217,21 @@ namespace localpath {
|
||||
return (dsp.empty() ? "" : (dsp + "/agent"));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
std::string get_parent(const std::filesystem::path path)
|
||||
{
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
return path.parent_path().string();
|
||||
}
|
||||
|
||||
std::string get_parent(const std::filesystem::path path)
|
||||
{
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
return path.parent_path().string();
|
||||
}
|
||||
|
||||
std::string get_child(const std::filesystem::path path)
|
||||
{
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
return path.filename().string();
|
||||
}
|
||||
std::string get_child(const std::filesystem::path path)
|
||||
{
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
return path.filename().string();
|
||||
}
|
||||
|
||||
} // namespace dropshell
|
||||
|
@ -10,20 +10,15 @@ namespace dropshell {
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// local user config directories
|
||||
|
||||
// ~/.config/dropshell/dropshell.json
|
||||
|
||||
// ~/.local/dropshell_agent
|
||||
|
||||
// ~/.dropshell
|
||||
// |-- dropshell.json
|
||||
// |-- agent-local
|
||||
// |-- agent-install.sh
|
||||
// |-- bb64 (only used locally, as it's for the local machine's architecture!)
|
||||
// |-- template_example
|
||||
// |-- agent-remote
|
||||
// |-- (remote agent files, including _allservicesstatus.sh)
|
||||
|
||||
// ~/.local/dropshell_files
|
||||
// |-- backups
|
||||
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
|
||||
// |-- temp_files
|
||||
// |-- template_cache
|
||||
// |-- templates
|
||||
@ -35,6 +30,10 @@ namespace dropshell {
|
||||
// | |-- .template_info.env
|
||||
// | |-- (...other service config files...)
|
||||
|
||||
// backups_path
|
||||
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
|
||||
|
||||
|
||||
// server_definition_path
|
||||
// |-- <server_name>
|
||||
// |-- server.json
|
||||
@ -53,7 +52,6 @@ namespace dropshell {
|
||||
} // namespace filenames.
|
||||
|
||||
namespace localfile {
|
||||
// ~/.config/dropshell/dropshell.json
|
||||
std::string dropshell_json();
|
||||
std::string server_json(const std::string &server_name);
|
||||
std::string service_env(const std::string &server_name, const std::string &service_name);
|
||||
@ -63,16 +61,15 @@ namespace dropshell {
|
||||
} // namespace localfile
|
||||
|
||||
namespace localpath {
|
||||
std::string dropshell_dir();
|
||||
|
||||
std::string server(const std::string &server_name);
|
||||
std::string service(const std::string &server_name, const std::string &service_name);
|
||||
|
||||
std::string remote_versions(const std::string &server_name, const std::string &service_name);
|
||||
|
||||
std::string agent_local();
|
||||
std::string agent_remote();
|
||||
std::string current_user_home();
|
||||
|
||||
std::string dropshell_files();
|
||||
std::string backups();
|
||||
std::string temp_files();
|
||||
std::string template_cache();
|
||||
|
@ -183,7 +183,7 @@ namespace dropshell
|
||||
|
||||
if (!rval && !hasFlag(mode, cMode::Silent))
|
||||
{
|
||||
error << "Error: Failed to execute ssh command:" << std::endl;
|
||||
error << "Failed to execute ssh command:" << std::endl;
|
||||
debug << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_bb64_path) << std::endl;
|
||||
}
|
||||
return rval;
|
||||
|
@ -3,6 +3,8 @@
|
||||
#define XXH_INLINE_ALL
|
||||
#include "contrib/xxhash.hpp"
|
||||
|
||||
#include "output.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
@ -13,7 +15,7 @@ uint64_t hash_file(const std::string &path) {
|
||||
// Create hash state
|
||||
XXH64_state_t* const state = XXH64_createState();
|
||||
if (state == nullptr) {
|
||||
std::cerr << "Failed to create hash state" << std::endl;
|
||||
error << "Failed to create hash state" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -24,7 +26,7 @@ uint64_t hash_file(const std::string &path) {
|
||||
// Open file
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to open file: " << path << std::endl;
|
||||
error << "Failed to open file: " << path << std::endl;
|
||||
XXH64_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
@ -34,7 +36,7 @@ uint64_t hash_file(const std::string &path) {
|
||||
char buffer[buffer_size];
|
||||
while (file.read(buffer, buffer_size)) {
|
||||
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
|
||||
std::cerr << "Failed to update hash" << std::endl;
|
||||
error << "Failed to update hash" << std::endl;
|
||||
XXH64_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
@ -43,7 +45,7 @@ uint64_t hash_file(const std::string &path) {
|
||||
// Handle any remaining bytes
|
||||
if (file.gcount() > 0) {
|
||||
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
|
||||
std::cerr << "Failed to update hash" << std::endl;
|
||||
error << "Failed to update hash" << std::endl;
|
||||
XXH64_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
@ -59,14 +61,14 @@ uint64_t hash_directory_recursive(const std::string &path) {
|
||||
// Create hash state
|
||||
XXH64_state_t* const state = XXH64_createState();
|
||||
if (state == nullptr) {
|
||||
std::cerr << "Failed to create hash state" << std::endl;
|
||||
error << "Failed to create hash state" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize state with seed 0
|
||||
XXH64_hash_t const seed = 0; /* or any other value */
|
||||
if (XXH64_reset(state, seed) == XXH_ERROR) {
|
||||
std::cerr << "Failed to reset hash state" << std::endl;
|
||||
error << "Failed to reset hash state" << std::endl;
|
||||
XXH64_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
@ -81,7 +83,7 @@ uint64_t hash_directory_recursive(const std::string &path) {
|
||||
}
|
||||
}
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
std::cerr << "Filesystem error: " << e.what() << std::endl;
|
||||
error << "Filesystem error: " << e.what() << std::endl;
|
||||
XXH64_freeState(state);
|
||||
return 0;
|
||||
}
|
||||
@ -94,7 +96,7 @@ uint64_t hash_directory_recursive(const std::string &path) {
|
||||
|
||||
uint64_t hash_path(const std::string &path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::cerr << "Path does not exist: " << path << std::endl;
|
||||
error << "Path does not exist: " << path << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -103,28 +105,28 @@ uint64_t hash_path(const std::string &path) {
|
||||
} else if (std::filesystem::is_regular_file(path)) {
|
||||
return hash_file(path);
|
||||
} else {
|
||||
std::cerr << "Path is neither a file nor a directory: " << path << std::endl;
|
||||
error << "Path is neither a file nor a directory: " << path << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_demo(const std::string & path)
|
||||
{
|
||||
std::cout << "Hashing path: " << path << std::endl;
|
||||
info << "Hashing path: " << path << std::endl;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
XXH64_hash_t hash = hash_path(path);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
std::cout << "Hash: " << hash << " (took " << duration.count() << "ms)" << std::endl;
|
||||
info << "Hash: " << hash << " (took " << duration.count() << "ms)" << std::endl;
|
||||
}
|
||||
|
||||
int hash_demo_raw(const std::string & path)
|
||||
{
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::cout << 0 <<std::endl; return 1;
|
||||
info << 0 <<std::endl; return 1;
|
||||
}
|
||||
XXH64_hash_t hash = hash_path(path);
|
||||
std::cout << hash << std::endl;
|
||||
info << hash << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ bool replace_line_in_file(const std::string& file_path, const std::string& searc
|
||||
std::string line;
|
||||
|
||||
if (!input_file.is_open()) {
|
||||
std::cerr << "Error: Unable to open file: " << file_path << std::endl;
|
||||
error << "Unable to open file: " << file_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ bool replace_line_in_file(const std::string& file_path, const std::string& searc
|
||||
std::ofstream output_file(file_path);
|
||||
if (!output_file.is_open())
|
||||
{
|
||||
std::cerr << "Error: Unable to open file: " << file_path << std::endl;
|
||||
error << "Unable to open file: " << file_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (const auto& modified_line : file_lines)
|
||||
@ -156,7 +156,7 @@ int str2int(const std::string &str)
|
||||
try {
|
||||
return std::stoi(str);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: Invalid integer string: [" << str << "]" << std::endl;
|
||||
error << "Invalid integer string: [" << str << "]" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -313,7 +313,8 @@ std::string requote(std::string str) {
|
||||
|
||||
|
||||
int die(const std::string & msg) {
|
||||
std::cerr << msg << std::endl;
|
||||
error << "Fatal error:" << std::endl;
|
||||
error << msg << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -553,23 +554,40 @@ bool match_line(const std::string &line, const std::string &pattern) {
|
||||
// edits file in-place.
|
||||
bool file_replace_or_add_segment(std::string filepath, std::string segment)
|
||||
{
|
||||
std::string first_line = segment.substr(0, segment.find("\n"));
|
||||
|
||||
// look backwards until we get a non-empty line.
|
||||
size_t last_line_pos = segment.rfind("\n");
|
||||
while (last_line_pos != std::string::npos) {
|
||||
std::string last_line = segment.substr(last_line_pos + 1);
|
||||
if (!trim(last_line).empty()) {
|
||||
break;
|
||||
}
|
||||
last_line_pos = segment.rfind("\n", last_line_pos - 1);
|
||||
// Create a backup of the original file
|
||||
std::string backup_path = filepath + ".bak";
|
||||
try {
|
||||
std::filesystem::copy_file(filepath, backup_path, std::filesystem::copy_options::overwrite_existing);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error creating backup file: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle empty segment
|
||||
if (segment.empty()) {
|
||||
error << "Empty segment provided" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// split the segment into lines
|
||||
std::vector<std::string> segment_lines = split(segment, "\n");
|
||||
// remove empty lines
|
||||
segment_lines.erase(std::remove_if(segment_lines.begin(), segment_lines.end(), [](const std::string& line) {
|
||||
return trim(line).empty();
|
||||
}), segment_lines.end());
|
||||
// remove any lines that are just whitespace
|
||||
segment_lines.erase(std::remove_if(segment_lines.begin(), segment_lines.end(), [](const std::string& line) { return trim(line).empty(); }), segment_lines.end());
|
||||
|
||||
// check that the segment has at least two lines
|
||||
if (segment_lines.size() < 2) {
|
||||
error << "Segment must contain at least two non-empty lines" << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::string last_line = segment.substr(last_line_pos + 1);
|
||||
|
||||
// Read the entire file into memory
|
||||
std::ifstream input_file(filepath);
|
||||
if (!input_file.is_open()) {
|
||||
std::cerr << "Error: Unable to open file: " << filepath << std::endl;
|
||||
error << "Unable to open file: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -580,22 +598,21 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
|
||||
}
|
||||
input_file.close();
|
||||
|
||||
// Store original file size for verification
|
||||
size_t original_size = file_lines.size();
|
||||
if (original_size == 0) {
|
||||
warning << "File is empty" << std::endl;
|
||||
}
|
||||
|
||||
// Try to find the matching block
|
||||
bool found_match = false;
|
||||
for (size_t i = 0; i < file_lines.size(); i++) {
|
||||
if (match_line(file_lines[i], first_line)) {
|
||||
if (match_line(file_lines[i], segment_lines[0])) {
|
||||
// Found potential start, look for end
|
||||
for (size_t j = i + 1; j < file_lines.size(); j++) {
|
||||
if (match_line(file_lines[j], last_line)) {
|
||||
if (match_line(file_lines[j], segment_lines[segment_lines.size() - 1])) {
|
||||
// Found matching block, replace it
|
||||
file_lines.erase(file_lines.begin() + i, file_lines.begin() + j + 1);
|
||||
|
||||
// Split segment into lines and insert them
|
||||
std::vector<std::string> segment_lines;
|
||||
std::istringstream segment_stream(segment);
|
||||
while (std::getline(segment_stream, line)) {
|
||||
segment_lines.push_back(line);
|
||||
}
|
||||
file_lines.insert(file_lines.begin() + i, segment_lines.begin(), segment_lines.end());
|
||||
|
||||
found_match = true;
|
||||
@ -608,16 +625,13 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
|
||||
|
||||
// If no match found, append the segment
|
||||
if (!found_match) {
|
||||
std::istringstream segment_stream(segment);
|
||||
while (std::getline(segment_stream, line)) {
|
||||
file_lines.push_back(line);
|
||||
}
|
||||
file_lines.insert(file_lines.end(), segment_lines.begin(), segment_lines.end());
|
||||
}
|
||||
|
||||
// Write back to file
|
||||
std::ofstream output_file(filepath);
|
||||
if (!output_file.is_open()) {
|
||||
std::cerr << "Error: Unable to open file for writing: " << filepath << std::endl;
|
||||
error << "Unable to open file for writing: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -626,6 +640,13 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
|
||||
}
|
||||
output_file.close();
|
||||
|
||||
// If everything succeeded, remove the backup
|
||||
try {
|
||||
std::filesystem::remove(backup_path);
|
||||
} catch (const std::exception& e) {
|
||||
warning << "Could not remove backup file: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user