From 8f06fc31ae6f4fb00fd45e66f6fe33ef5d9118bb Mon Sep 17 00:00:00 2001
From: Your Name <j@842.be>
Date: Sun, 25 May 2025 19:17:51 +1200
Subject: [PATCH] Debugging

---
 source/src/commands/backupdata.cpp  |   4 +-
 source/src/commands/edit.cpp        |   6 +-
 source/src/commands/install.cpp     |   4 +-
 source/src/commands/list.cpp        |   2 +-
 source/src/commands/restoredata.cpp |   8 +-
 source/src/commands/start.cpp       |   2 +-
 source/src/config.cpp               |  14 ++-
 source/src/config.hpp               |   2 +
 source/src/main.cpp                 |   2 +-
 source/src/servers.cpp              |  18 +--
 source/src/services.cpp             |  12 +-
 source/src/templates.cpp            |  30 ++---
 source/src/utils/directories.cpp    | 172 ++++++++++++++--------------
 source/src/utils/directories.hpp    |  21 ++--
 source/src/utils/execute.cpp        |   2 +-
 source/src/utils/hash.cpp           |  28 ++---
 source/src/utils/utils.cpp          |  81 ++++++++-----
 17 files changed, 220 insertions(+), 188 deletions(-)

diff --git a/source/src/commands/backupdata.cpp b/source/src/commands/backupdata.cpp
index 9b0837c..67fbbf9 100644
--- a/source/src/commands/backupdata.cpp
+++ b/source/src/commands/backupdata.cpp
@@ -84,7 +84,7 @@ namespace dropshell
                                                       remote_command_script_file,
                                                       remotefile(server, user).service_env(service)}, user))
             {
-                error << "Error: Required service directories not found on remote server" << std::endl;
+                error << "Required service directories not found on remote server" << std::endl;
                 info << "Is the service installed?" << std::endl;
                 return false;
             }
@@ -103,7 +103,7 @@ namespace dropshell
             std::string local_backups_dir = localpath::backups();
             if (local_backups_dir.empty())
             {
-                error << "Error: Local backups directory not found" << std::endl;
+                error << "Local backups directory not found" << std::endl;
                 info << "Run 'dropshell edit' to configure DropShell" << std::endl;
                 return false;
             }
diff --git a/source/src/commands/edit.cpp b/source/src/commands/edit.cpp
index 38a900a..41eb7b6 100644
--- a/source/src/commands/edit.cpp
+++ b/source/src/commands/edit.cpp
@@ -94,11 +94,11 @@ int edit_config()
 
     std::string config_file = localfile::dropshell_json();
     if (!edit_file(config_file, false) || !std::filesystem::exists(config_file))
-        return die("Error: Failed to edit config file.");
+        return die("Failed to edit config file.");
 
     gConfig().load_config();
     if (!gConfig().is_config_set())
-        return die("Error: Failed to load and parse edited config file!");
+        return die("Failed to load and parse edited config file!");
 
     gConfig().save_config(true);
 
@@ -112,7 +112,7 @@ int edit_config()
 int edit_server(const std::string &server_name)
 {
     if (localpath::server(server_name).empty()) {
-        std::cerr << "Error: Server not found: " << server_name << std::endl;
+        error << "Server not found: " << server_name << std::endl;
         return -1;
     }
 
diff --git a/source/src/commands/install.cpp b/source/src/commands/install.cpp
index d6b48a5..06e4a84 100644
--- a/source/src/commands/install.cpp
+++ b/source/src/commands/install.cpp
@@ -334,7 +334,7 @@ complete -F _dropshell_completions ds
             info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl;
 
             std::string agent_path = remotepath(server.get_server_name(),user.user).agent();
-            ASSERT(agent_path == user.dir+"/agent", "Agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir);
+            ASSERT(agent_path == user.dir+"/agent", "Remote agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir);
             ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name());
 
             // now create the agent.
@@ -348,7 +348,7 @@ complete -F _dropshell_completions ds
             bool okay = execute_ssh_command(server.get_SSH_INFO(user.user), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr);
             if (!okay)
             {
-                error << "ERROR: Failed to install remote agent on " << server.get_server_name() << std::endl;
+                error << "Failed to install remote agent on " << server.get_server_name() << std::endl;
                 return 1;
             }
 
diff --git a/source/src/commands/list.cpp b/source/src/commands/list.cpp
index 0ecbc11..3ab1839 100644
--- a/source/src/commands/list.cpp
+++ b/source/src/commands/list.cpp
@@ -147,7 +147,7 @@ void list_servers() {
 void show_server_details(const std::string& server_name) {
     ServerConfig env(server_name);
     if (!env.is_valid()) {
-        error << "Error: Invalid server environment file: " << server_name << std::endl;
+        error << "Invalid server environment file: " << server_name << std::endl;
         return;
     }
 
diff --git a/source/src/commands/restoredata.cpp b/source/src/commands/restoredata.cpp
index ab76b15..1cb5636 100644
--- a/source/src/commands/restoredata.cpp
+++ b/source/src/commands/restoredata.cpp
@@ -60,7 +60,7 @@ namespace dropshell
         std::string local_backups_dir = localpath::backups();
         if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
         {
-            error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
+            error << "Local backups directory not found: " << local_backups_dir << std::endl;
             return {};
         }
 
@@ -150,19 +150,19 @@ namespace dropshell
         std::string local_backups_dir = localpath::backups();
         if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
         {
-            error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
+            error << "Local backups directory not found: " << local_backups_dir << std::endl;
             return 1;
         }
         std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_details->get_filename()).string();
         if (!std::filesystem::exists(local_backup_file_path))
         {
-            error << "Error: Backup file not found at " << local_backup_file_path << std::endl;
+            error << "Backup file not found at " << local_backup_file_path << std::endl;
             return 1;
         }
 
         if (backup_details->get_template_name() != service_info.template_name)
         {
-            error << "Error: Backup template does not match service template. Can't restore." << std::endl;
+            error << "Backup template does not match service template. Can't restore." << std::endl;
             info << "Backup template:  " << backup_details->get_template_name() << std::endl;
             info << "Service template: " << service_info.template_name << std::endl;
             return 1;
diff --git a/source/src/commands/start.cpp b/source/src/commands/start.cpp
index 2bc41bc..8720422 100644
--- a/source/src/commands/start.cpp
+++ b/source/src/commands/start.cpp
@@ -67,7 +67,7 @@ namespace dropshell
     {
         if (ctx.args.size() < 2)
         {
-            std::cerr << "Error: Server name and service name are both required" << std::endl;
+            error << "Server name and service name are both required" << std::endl;
             return 1;
         }
 
diff --git a/source/src/config.cpp b/source/src/config.cpp
index 16e412f..1019e05 100644
--- a/source/src/config.cpp
+++ b/source/src/config.cpp
@@ -37,7 +37,7 @@ bool config::load_config() { // load json config file.
     }
     catch (nlohmann::json::parse_error& ex)
     {
-        std::cerr << "Error: Failed to parse config file: " << ex.what() << std::endl;
+        error << "Failed to parse config file: " << ex.what() << std::endl;
         return false;
     }
 
@@ -69,7 +69,7 @@ bool config::save_config(bool create_aux_directories)
     if (!mIsConfigSet)
     {
         std::string homedir = localpath::current_user_home();
-        std::string dropshell_base = homedir + "/.local/dropshell_files";
+        std::string dropshell_base = homedir + "/.dropshell";
 
         mConfig["server_definition_paths"] = {
             dropshell_base + "/servers"
@@ -81,6 +81,10 @@ bool config::save_config(bool create_aux_directories)
             "https://templates.dropshell.app"
         };        
         mConfig["template_upload_token"] = "SECRETTOKEN";
+
+        mConfig["backups_path"] = {
+            dropshell_base + "/backups"
+        };
     }   
 
     config_file << mConfig.dump(4);
@@ -175,4 +179,10 @@ std::string config::get_template_upload_token() {
     return mConfig["template_upload_token"];
 }
 
+std::string config::get_backups_path()
+{
+    return mConfig["backups_path"];
+}
+
+
 } // namespace dropshell 
\ No newline at end of file
diff --git a/source/src/config.hpp b/source/src/config.hpp
index 090d706..dd2cfd0 100644
--- a/source/src/config.hpp
+++ b/source/src/config.hpp
@@ -28,6 +28,8 @@ class config {
         std::string get_template_upload_url();  
         std::string get_template_upload_token();
 
+        std::string get_backups_path();
+
     private:
         nlohmann::json mConfig;
         bool mIsConfigSet;
diff --git a/source/src/main.cpp b/source/src/main.cpp
index 0d987fb..15bbc88 100644
--- a/source/src/main.cpp
+++ b/source/src/main.cpp
@@ -80,7 +80,7 @@ int main(int argc, char* argv[]) {
 
     }
     catch (const std::exception& e) {
-        std::cerr << "Error: " << e.what() << std::endl;
+        error << "Uncaught Exception: " << e.what() << std::endl;
         return 1;
     }
 }
diff --git a/source/src/servers.cpp b/source/src/servers.cpp
index 82c2b48..430fb66 100644
--- a/source/src/servers.cpp
+++ b/source/src/servers.cpp
@@ -104,7 +104,7 @@ namespace dropshell
         catch (const std::exception &e)
         {
             error << "Failed to parse " << server_json_path << std::endl;
-            error << "Error: " << e.what() << std::endl;
+            error << "Exception: " << e.what() << std::endl;
 
             mValid = false;
         }
@@ -241,7 +241,7 @@ namespace dropshell
         bool okay = execute_ssh_command(sshinfo, scommand, cMode::Silent);
         if (!okay)
         {
-            std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl;
+            error << "Required items not found on remote server: " << file_names_str << std::endl;
             return false;
         }
         return true;
@@ -341,7 +341,7 @@ namespace dropshell
         std::map<std::string, std::string> env_vars;
         if (!get_all_service_env_vars(mServerName, service_name, env_vars))
         {
-            std::cerr << "Error: Failed to get all service env vars for " << service_name << std::endl;
+            error << "Failed to get all service env vars for " << service_name << std::endl;
             return std::nullopt;
         }
 
@@ -358,7 +358,7 @@ namespace dropshell
 
         if (sc.empty())
         {
-            std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
+            error << "Failed to construct command for " << service_name << " " << command << std::endl;
             return std::nullopt;
         }
         return sc;
@@ -388,7 +388,7 @@ namespace dropshell
                         ServerConfig env(server_name);
                         if (!env.is_valid())
                         {
-                            std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl;
+                            error << "Invalid server environment file: " << entry.path().string() << std::endl;
                             continue;
                         }
                         servers.push_back(env);
@@ -406,7 +406,7 @@ namespace dropshell
         std::string server_existing_dir = localpath::server(server_name);
         if (!server_existing_dir.empty())
         {
-            error << "Error: Server name already exists: " << server_name << std::endl;
+            error << "Server name already exists: " << server_name << std::endl;
             info << "Current server path: " << server_existing_dir << std::endl;
             return false;
         }
@@ -415,7 +415,7 @@ namespace dropshell
         auto lsdp = gConfig().get_local_server_definition_paths();
         if (lsdp.empty() || lsdp[0].empty())
         {
-            error << "Error: Local server definition path not found" << std::endl;
+            error << "Local server definition path not found" << std::endl;
             info << "Run 'dropshell edit' to configure DropShell" << std::endl;
             return false;
         }
@@ -434,7 +434,9 @@ namespace dropshell
         server_env_file << "            \"USER\": \"" << user << "\"," << std::endl;
         server_env_file << "            \"DIR\": \"" << "/home/" + user << "/.dropshell\"" << std::endl;
         server_env_file << "        }" << std::endl;
-        server_env_file << "    ]" << std::endl;
+        server_env_file << "    ]," << std::endl;
+        server_env_file << "    \"HAS_DOCKER\": \"true\"," << std::endl;
+        server_env_file << "    \"DOCKER_ROOTLESS\": \"false\"" << std::endl;
         server_env_file << "}" << std::endl;
         server_env_file.close();
 
diff --git a/source/src/services.cpp b/source/src/services.cpp
index 0258851..9aa5cd8 100644
--- a/source/src/services.cpp
+++ b/source/src/services.cpp
@@ -34,8 +34,8 @@ namespace dropshell
         std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
         if (local_server_definition_paths.empty())
         {
-            std::cerr << "Error: No local server definition paths found" << std::endl;
-            std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
+            error << "No local server definition paths found" << std::endl;
+            info << "Run 'dropshell edit' to configure DropShell" << std::endl;
             return services;
         }
 
@@ -158,7 +158,7 @@ namespace dropshell
         auto service_info = get_service_info(server_name, service_name);
         if (service_info.local_template_path.empty())
         {
-            std::cerr << "Error: Service not found: " << service_name << std::endl;
+            error << "Service not found: " << service_name << std::endl;
             return commands;
         }
 
@@ -183,7 +183,7 @@ namespace dropshell
         auto service_info = get_service_info(server_name, service_name);
         if (service_info.local_template_path.empty())
         {
-            std::cerr << "Error: Service not found: " << service_name << std::endl;
+            error << "Service not found: " << service_name << std::endl;
             return backups;
         }
 
@@ -211,7 +211,7 @@ namespace dropshell
 
         if (localpath::service(server_name, service_name).empty() || !fs::exists(localpath::service(server_name, service_name)))
         {
-            std::cerr << "Error: Service not found: " << service_name << " on server " << server_name << std::endl;
+            error << "Service not found: " << service_name << " on server " << server_name << std::endl;
             return false;
         }
 
@@ -265,7 +265,7 @@ namespace dropshell
         template_info tinfo = gTemplateManager().get_template_info(it->second);
         if (!tinfo.is_set())
         {
-            std::cerr << "Error: Template '" << it->second << "' not found" << std::endl;
+            error << "Template '" << it->second << "' not found" << std::endl;
             return false;
         }
 
diff --git a/source/src/templates.cpp b/source/src/templates.cpp
index f453734..5c803db 100644
--- a/source/src/templates.cpp
+++ b/source/src/templates.cpp
@@ -163,7 +163,7 @@
         ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found.");
         template_source_interface* source = get_source(template_name);
         if (!source) {
-            std::cerr << "Error: Template '" << template_name << "' not found" << std::endl;
+            error << "Template '" << template_name << "' not found" << std::endl;
             return false;
         }
         return source->template_command_exists(template_name, command);
@@ -175,21 +175,21 @@
         std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
         
         if (local_server_definition_paths.empty()) {
-            std::cerr << "Error: No local server definition paths found" << std::endl;
-            std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
+            error << "No local server definition paths found" << std::endl;
+            info << "Run 'dropshell edit' to configure DropShell" << std::endl;
             return false;
         }
 
-        auto info = get_template_info(template_name);
-        if (info.is_set()) {
-            std::cerr << "Error: Template '" << template_name << "' already exists at " << info.locationID() << std::endl;
+        auto tinfo = get_template_info(template_name);
+        if (tinfo.is_set()) {
+            error << "Template '" << template_name << "' already exists at " << tinfo.locationID() << std::endl;
             return false;
         }
 
         auto local_template_paths = gConfig().get_local_template_paths();
         if (local_template_paths.empty()) {
-            std::cerr << "Error: No local template paths found" << std::endl;
-            std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
+            error << "No local template paths found" << std::endl;
+            info << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
             return false;
         }
         std::string new_template_path = local_template_paths[0] + "/" + template_name;
@@ -200,7 +200,7 @@
         // 2. Copy the example template from the system templates directory
         auto example_info = gTemplateManager().get_template_info("example-nginx");
         if (!example_info.is_set()) {
-            std::cerr << "Error: Example template not found" << std::endl;
+            error << "Example template not found" << std::endl;
             return false;
         }
         std::string example_template_path = example_info.local_template_path();
@@ -222,7 +222,7 @@
         std::string replacement_line = "TEMPLATE=" + template_name;
         std::string service_env_path = new_template_path + "/config/" + filenames::template_info_env;
         if (!replace_line_in_file(service_env_path, search_string, replacement_line)) {
-            std::cerr << "Error: Failed to replace TEMPLATE= line in the " << filenames::template_info_env <<" file" << std::endl;
+            error << "Failed to replace TEMPLATE= line in the " << filenames::template_info_env <<" file" << std::endl;
             return false;
         }
 
@@ -278,7 +278,7 @@
     bool template_manager::required_file(std::string path, std::string template_name)
     {
         if (!std::filesystem::exists(path)) {
-            std::cerr << "Error: " << path << " file not found in template - REQUIRED." << template_name << std::endl;
+            error << path << " file not found in template - REQUIRED." << template_name << std::endl;
             return false;
         }
         return true;
@@ -323,7 +323,7 @@
                 std::filesystem::path path = template_path + "/" + file;
                 auto perms = std::filesystem::status(path).permissions();
                 if ((perms & std::filesystem::perms::owner_exec) == std::filesystem::perms::none)
-                    std::cerr << "Error: " << file << " is not executable" << std::endl;
+                    error << file << " is not executable" << std::endl;
             }
         }        
 
@@ -347,18 +347,18 @@
         // determine template name.
         auto it = all_env_vars.find("TEMPLATE");
         if (it == all_env_vars.end()) {
-            std::cerr << "Error: TEMPLATE variable not found in " << template_path << std::endl;
+            error << "TEMPLATE variable not found in " << template_path << std::endl;
             return false;
         }
 
         std::string env_template_name = it->second;
         if (env_template_name.empty()) {
-            std::cerr << "Error: TEMPLATE variable is empty in " << template_path << std::endl;
+            error << "TEMPLATE variable is empty in " << template_path << std::endl;
             return false;
         }
 
         if (env_template_name != template_name) {
-            std::cerr << "Error: TEMPLATE variable is wrong in " << template_path << std::endl;
+            error << "TEMPLATE variable is wrong in " << template_path << std::endl;
             return false;
         }
 
diff --git a/source/src/utils/directories.cpp b/source/src/utils/directories.cpp
index 441ad67..65c64c4 100644
--- a/source/src/utils/directories.cpp
+++ b/source/src/utils/directories.cpp
@@ -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
diff --git a/source/src/utils/directories.hpp b/source/src/utils/directories.hpp
index 4a07648..49a4889 100644
--- a/source/src/utils/directories.hpp
+++ b/source/src/utils/directories.hpp
@@ -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();
diff --git a/source/src/utils/execute.cpp b/source/src/utils/execute.cpp
index 39b854f..2843cf3 100644
--- a/source/src/utils/execute.cpp
+++ b/source/src/utils/execute.cpp
@@ -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;
diff --git a/source/src/utils/hash.cpp b/source/src/utils/hash.cpp
index b4625c5..eabb17d 100644
--- a/source/src/utils/hash.cpp
+++ b/source/src/utils/hash.cpp
@@ -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;
 }
 
diff --git a/source/src/utils/utils.cpp b/source/src/utils/utils.cpp
index 341844c..509f6a4 100644
--- a/source/src/utils/utils.cpp
+++ b/source/src/utils/utils.cpp
@@ -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;
 }