From 5201e92cb365d93763a0122e455ce4c98e8abc71 Mon Sep 17 00:00:00 2001
From: Your Name <j@842.be>
Date: Mon, 12 May 2025 23:53:52 +1200
Subject: [PATCH] .

---
 src/commands/edit.cpp      | 21 +++++++++++++-----
 src/commands/install.cpp   |  4 ++--
 src/config.cpp             | 16 ++++++++++++--
 src/main.cpp               |  2 --
 src/server_env_manager.cpp |  9 ++++----
 src/server_env_manager.hpp |  2 +-
 src/service_runner.cpp     |  8 +++----
 src/utils/execute.cpp      | 45 ++++++++++++++++++++------------------
 src/utils/execute.hpp      | 14 +++++++-----
 9 files changed, 73 insertions(+), 48 deletions(-)

diff --git a/src/commands/edit.cpp b/src/commands/edit.cpp
index 2df6c2e..ac3dd07 100644
--- a/src/commands/edit.cpp
+++ b/src/commands/edit.cpp
@@ -50,7 +50,7 @@ struct EditCommandRegister {
 // ------------------------------------------------------------------------------------------------
 // utility function to edit a file
 // ------------------------------------------------------------------------------------------------
-bool edit_file(const std::string &file_path)
+bool edit_file(const std::string &file_path, bool has_bb64)
 {
     // make sure parent directory exists.
     std::string parent_dir = get_parent(file_path);
@@ -72,7 +72,15 @@ bool edit_file(const std::string &file_path)
     }
 
     std::cout << "Editing file: " << file_path << std::endl;
-    return execute_local_command(editor_cmd, nullptr, cMode::Interactive);
+
+    if (has_bb64) {
+        return execute_local_command(editor_cmd, nullptr, cMode::Interactive);
+    }
+    else {
+        // might not have bb64 at this early stage. Direct edit.
+        int ret = system(editor_cmd.c_str());
+        return EXITSTATUSCHECK(ret);
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -84,7 +92,7 @@ int edit_config()
         gConfig().save_config(false); // save defaults.
 
     std::string config_file = localfile::dropshell_json();
-    if (!edit_file(config_file) || !std::filesystem::exists(config_file))
+    if (!edit_file(config_file, false) || !std::filesystem::exists(config_file))
         return die("Error: Failed to edit config file.");
 
     gConfig().load_config();
@@ -92,6 +100,9 @@ int edit_config()
         return die("Error: Failed to load and parse edited config file!");
 
     gConfig().save_config(true);
+
+    // make sure we have executables.
+
     std::cout << "Successfully edited config file at " << config_file << std::endl;
     return 0;
 }
@@ -113,7 +124,7 @@ int edit_server(const std::string &server_name)
               << "Once moved, reinstall all services with:   dropshell install " << server_name;
 
     std::string config_file = serverpath + "/server.env";
-    if (!edit_file(config_file)) {
+    if (!edit_file(config_file, true)) {
         std::cerr << "Error: Failed to edit server.env" << std::endl;
         std::cerr << "You can manually edit this file at: " << config_file << std::endl;
         std::cerr << "After editing, " << aftertext.str() << std::endl;
@@ -135,7 +146,7 @@ int edit_service_config(const std::string &server, const std::string &service)
         return 1;
     }
 
-    if (edit_file(config_file) && std::filesystem::exists(config_file))
+    if (edit_file(config_file, true) && std::filesystem::exists(config_file))
         std::cout << "To apply your changes, run:\n   dropshell install " + server + " " + service << std::endl;
     return 0;
 }
diff --git a/src/commands/install.cpp b/src/commands/install.cpp
index 7fd0c71..4636792 100644
--- a/src/commands/install.cpp
+++ b/src/commands/install.cpp
@@ -88,7 +88,7 @@ bool install_service(const std::string& server, const std::string& service, bool
     // Create service directory
     std::string remote_service_path = remotepath::service(server, service);
     std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path);
-    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("",remotepath::executables(server), mkdir_cmd, {}), cMode::Silent))
+    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
     {
         std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
         return false;
@@ -96,7 +96,7 @@ bool install_service(const std::string& server, const std::string& service, bool
     
     // Check if rsync is installed on remote host
     std::string check_rsync_cmd = "which rsync";
-    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("",remotepath::executables(server), check_rsync_cmd, {}), cMode::Silent))
+    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", check_rsync_cmd, {}), cMode::Silent))
     {
         std::cerr << "rsync is not installed on the remote host" << std::endl;
         return false;
diff --git a/src/config.cpp b/src/config.cpp
index 627b297..2f4842b 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -5,7 +5,7 @@
 #include "utils/utils.hpp"
 #include "utils/json.hpp"
 #include <filesystem>
-
+#include "utils/execute.hpp"
 namespace dropshell {
 
 
@@ -85,7 +85,8 @@ bool config::save_config(bool create_aux_directories)
         std::vector<std::filesystem::path> paths = {
             get_local_template_cache_path(),
             get_local_backup_path(),
-            get_local_tempfiles_path()
+            get_local_tempfiles_path(),
+            get_local_executables_path()
         };
         for (auto & p : get_local_server_definition_paths()) 
             paths.push_back(p);
@@ -98,6 +99,17 @@ bool config::save_config(bool create_aux_directories)
             }
     }
 
+    // also make sure the executables are in the path.
+    std::string executables_path = get_local_executables_path();
+
+    // download bb64.
+    std::string cmd = "cd " + executables_path + " && curl -fsSL -o bb64 https://gitea.jde.nz/public/bb64/releases/download/latest/bb64.amd64 && chmod a+x bb64";
+    int ret = system(cmd.c_str());
+    if (EXITSTATUSCHECK(ret))
+        std::cout << "Downloaded bb64 to " << executables_path << std::endl;
+    else
+        std::cerr << "Failed to download bb64 to " << executables_path << std::endl;
+
     return true;
 }
   
diff --git a/src/main.cpp b/src/main.cpp
index 3f23907..dc6a214 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -141,8 +141,6 @@ auto command_match = [](const std::string& cmd_list, int argc, char* argv[]) ->
 }
 
 
-
-
 int old_main(int argc, char* argv[]) {
     HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
     HAPPYEXIT("version", printversion())
diff --git a/src/server_env_manager.cpp b/src/server_env_manager.cpp
index b5f83da..bf066ed 100644
--- a/src/server_env_manager.cpp
+++ b/src/server_env_manager.cpp
@@ -122,7 +122,6 @@ std::optional<sCommand> server_env_manager::construct_standard_template_run_cmd(
 
     sCommand sc(
         remote_service_template_path, 
-        remotepath::executables(mServerName),
         quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), 
         env_vars
     );
@@ -137,12 +136,12 @@ std::optional<sCommand> server_env_manager::construct_standard_template_run_cmd(
 
 bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
 {
-    sCommand scommand("",remotepath::executables(mServerName),"test -d " + quote(dir_path),{});
+    sCommand scommand("", "test -d " + quote(dir_path),{});
     return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
 }
 
 bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
-    sCommand scommand("",remotepath::executables(mServerName),"test -f " + quote(file_path),{});
+    sCommand scommand("", "test -f " + quote(file_path),{});
     return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
 }
 
@@ -156,7 +155,7 @@ bool server_env_manager::check_remote_items_exist(const std::vector<std::string>
         file_names_str += std::filesystem::path(file_path).filename().string() + " ";
     }
     // check if all items in the vector exist on the remote server, in a single command.
-    sCommand scommand("",remotepath::executables(mServerName),"for item in " + file_paths_str + "; do test -f $item; done",{});
+    sCommand scommand("", "for item in " + file_paths_str + "; do test -f $item; done",{});
 
     bool okay = execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
     if (!okay) {
@@ -187,7 +186,7 @@ bool server_env_manager::remove_remote_dir(const std::string &dir_path, bool sil
     if (!silent)
         std::cout << "Running command: " << remote_cmd << std::endl;
 
-    sCommand scommand("",remotepath::executables(mServerName),remote_cmd,{});
+    sCommand scommand("", remote_cmd,{});
     cMode mode = (silent ? cMode::Silent : cMode::None);
 
     return execute_ssh_command(get_SSH_INFO(), scommand, mode);
diff --git a/src/server_env_manager.hpp b/src/server_env_manager.hpp
index b1e9223..bd4a0d9 100644
--- a/src/server_env_manager.hpp
+++ b/src/server_env_manager.hpp
@@ -43,7 +43,7 @@ class server_env_manager {
         std::string get_SSH_USER() const { return get_variable("SSH_USER"); }
         std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); }
         std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
-        sSSHInfo get_SSH_INFO() const { return sSSHInfo{get_SSH_HOST(), get_SSH_USER(), get_SSH_PORT()}; }
+        sSSHInfo get_SSH_INFO() const { return sSSHInfo{get_SSH_HOST(), get_SSH_USER(), get_SSH_PORT(), get_server_name()}; }
         bool is_valid() const { return mValid; }
         std::string get_server_name() const { return mServerName; }
 
diff --git a/src/service_runner.cpp b/src/service_runner.cpp
index 00d1d92..e0f7cae 100644
--- a/src/service_runner.cpp
+++ b/src/service_runner.cpp
@@ -241,7 +241,7 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std:
         std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
         return false;
     }
-    sCommand scommand("",remotepath::executables(server_name), "bash",{});
+    sCommand scommand("", "bash",{});
     return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive);
 }
 
@@ -426,7 +426,7 @@ bool service_runner::backup(bool silent) {
     std::string remote_backups_dir = remotepath::backups(mServer);
     if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
     std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
-    if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("",remotepath::executables(mServer), mkdir_cmd, {}), cMode::Silent)) {
+    if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("",mkdir_cmd, {}), cMode::Silent)) {
         std::cerr << "Failed to create backups directory on server" << std::endl;
         return false;
     }
@@ -484,7 +484,7 @@ cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mSe
 {
     std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
     std::string mkdir_cmd = "mkdir -p " + quote(p);
-    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("",remotepath::executables(server_env.get_server_name()), mkdir_cmd, {}), cMode::Silent)) 
+    if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent)) 
         std::cerr << "Failed to create temp directory on server" << std::endl;
     else 
         mPath = p;
@@ -493,7 +493,7 @@ cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mSe
 cRemoteTempFolder::~cRemoteTempFolder()
 {
     std::string rm_cmd = "rm -rf " + quote(mPath);
-    execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("",remotepath::executables(mServerEnv.get_server_name()), rm_cmd, {}), cMode::Silent);
+    execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("", rm_cmd, {}), cMode::Silent);
 }
 
 std::string cRemoteTempFolder::path() const
diff --git a/src/utils/execute.cpp b/src/utils/execute.cpp
index 17e71a3..a8eb448 100644
--- a/src/utils/execute.cpp
+++ b/src/utils/execute.cpp
@@ -12,19 +12,20 @@
 #include "utils/utils.hpp"
 #include "utils/b64ed.hpp"
 #include "config.hpp"
-
-bool EXITSTATUSCHECK(int ret)
-{
-    return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
-}
+#include "utils/directories.hpp"
 
 namespace dropshell
 {
+    bool EXITSTATUSCHECK(int ret)
+    {
+        return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
+    }
+
     bool execute_local_command_interactive(const sCommand &command)
     {
         if (command.get_command_to_run().empty())
             return false;
-        std::string full_command = command.construct_cmd(); // Get the command string
+        std::string full_command = command.construct_cmd(gConfig().get_local_executables_path()); // Get the command string
 
         pid_t pid = fork();
 
@@ -55,7 +56,7 @@ namespace dropshell
         ASSERT(output != nullptr, "Output string must be provided");
         if (command.get_command_to_run().empty())
             return false;
-        std::string full_cmd = command.construct_cmd() + " 2>&1";
+        std::string full_cmd = command.construct_cmd(gConfig().get_local_executables_path()) + " 2>&1";
         FILE *pipe = popen(full_cmd.c_str(), "r");
         if (!pipe)
         {
@@ -72,12 +73,12 @@ namespace dropshell
 
     bool execute_local_command(std::string command, std::string *output, cMode mode)
     {
-        return execute_local_command("",command,{}, output, mode);
+        return execute_local_command("", command, {}, output, mode);
     }
 
     bool execute_local_command(std::string directory_to_run_in, std::string command_to_run, const std::map<std::string, std::string> &env_vars, std::string *output, cMode mode)
     {
-        sCommand command(directory_to_run_in, gConfig().get_local_executables_path(), command_to_run, env_vars);
+        sCommand command(directory_to_run_in, command_to_run, env_vars);
 
         if (hasFlag(mode, cMode::Interactive))
         {
@@ -98,7 +99,7 @@ namespace dropshell
         if (command.get_command_to_run().empty())
             return false;
         bool silent = hasFlag(mode, cMode::Silent);
-        std::string full_cmd = command.construct_cmd() + " 2>&1" + (silent ? " > /dev/null" : "");
+        std::string full_cmd = command.construct_cmd(gConfig().get_local_executables_path()) + " 2>&1" + (silent ? " > /dev/null" : "");
         int ret = system(full_cmd.c_str());
 
         bool ok = EXITSTATUSCHECK(ret);
@@ -121,36 +122,38 @@ namespace dropshell
         ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "")
                 << ssh_info.user << "@" << ssh_info.host;
 
+        std::string remote_executables_path = remotepath::executables(ssh_info.server_ID);
+
         bool rval = execute_local_command(
-            "",                                                             // directory to run in
-            ssh_cmd.str() + " " + remote_command.construct_cmd(),           // local command to run
-            {},                                                             // environment variables
-            output,                                                         // output string
-            mode                                                            // mode
-            );
+            "",                                                                          // directory to run in
+            ssh_cmd.str() + " " + remote_command.construct_cmd(remote_executables_path), // local command to run
+            {},                                                                          // environment variables
+            output,                                                                      // output string
+            mode                                                                         // mode
+        );
 
         if (!rval && !hasFlag(mode, cMode::Silent))
         {
             std::cerr << std::endl
                       << std::endl;
             std::cerr << "Error: Failed to execute ssh command:" << std::endl;
-            std::cerr << "\033[90m" << ssh_cmd.str() + " " + remote_command.construct_cmd() << "\033[0m" << std::endl;
+            std::cerr << "\033[90m" << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_executables_path) << "\033[0m" << std::endl;
             std::cerr << std::endl
                       << std::endl;
         }
         return rval;
     }
 
-    std::string sCommand::makesafecmd(const std::string &command) const
+    std::string sCommand::makesafecmd(std::string executables_path, const std::string &command) const
     {
         if (command.empty())
             return "";
         std::string encoded = base64_encode(dequote(trim(command)));
-        std::string commandstr = mExecutablesPath + "/bb64 " + encoded;
+        std::string commandstr = executables_path + "/bb64 " + encoded;
         return commandstr;
     }
 
-    std::string sCommand::construct_cmd() const
+    std::string sCommand::construct_cmd(std::string executables_path) const
     {
         if (mCmd.empty())
             return "";
@@ -167,7 +170,7 @@ namespace dropshell
 
         cmdstr += mCmd;
 
-        cmdstr = makesafecmd(cmdstr);
+        cmdstr = makesafecmd(executables_path, cmdstr);
 
         return cmdstr;
     }
diff --git a/src/utils/execute.hpp b/src/utils/execute.hpp
index 2d2c3ee..5f9baee 100644
--- a/src/utils/execute.hpp
+++ b/src/utils/execute.hpp
@@ -28,6 +28,7 @@ typedef struct sSSHInfo {
     std::string host;
     std::string user;
     std::string port;
+    std::string server_ID; // dropshell name for server.
 } sSSHInfo;
 
 bool execute_local_command(std::string command, std::string * output = nullptr, cMode mode = cMode::None);
@@ -41,11 +42,10 @@ bool execute_ssh_command(const sSSHInfo & ssh_info, const sCommand & remote_comm
 // class to hold a command to run on the remote server.
 class sCommand {
     public:
-        sCommand(std::string directory_to_run_in, std::string executables_path, std::string command_to_run, const std::map<std::string, std::string> & env_vars) : 
-            mDir(directory_to_run_in), mExecutablesPath(executables_path), mCmd(command_to_run), mVars(env_vars) {}
+        sCommand(std::string directory_to_run_in, std::string command_to_run, const std::map<std::string, std::string> & env_vars) : 
+            mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {}
 
         std::string get_directory_to_run_in() const { return mDir; }
-        std::string get_executables_path() const { return mExecutablesPath; }
         std::string get_command_to_run() const { return mCmd; }
         const std::map<std::string, std::string>& get_env_vars() const { return mVars; }
 
@@ -53,18 +53,20 @@ class sCommand {
         
         bool empty() const { return mCmd.empty(); }
     
-        std::string construct_cmd() const;
+        std::string construct_cmd(std::string executables_path) const;
 
     private:
-        std::string makesafecmd(const std::string& command) const;
+        std::string makesafecmd(std::string executables_path, const std::string& command) const;
 
     private:
         std::string mDir;
-        std::string mExecutablesPath;
         std::string mCmd;
         std::map<std::string, std::string> mVars;
 };
 
+bool EXITSTATUSCHECK(int ret);
+
+
 } // namespace dropshell