From bbc280a50a1bab7f2d7365c67d55efea73ef5506 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 6 May 2025 21:32:41 +1200 Subject: [PATCH] BROKEN --- src/server_env_manager.cpp | 156 ++------------------- src/server_env_manager.hpp | 42 +----- src/service_runner.cpp | 91 ++++-------- src/service_runner.hpp | 4 +- src/utils/execute.cpp | 167 +++++++++++++++++++++++ src/utils/execute.hpp | 89 ++++++++++++ src/utils/utils.cpp | 5 + src/utils/utils.hpp | 1 + templates/dropshell-agent/_nuke_other.sh | 25 ++++ 9 files changed, 335 insertions(+), 245 deletions(-) create mode 100644 src/utils/execute.cpp create mode 100644 src/utils/execute.hpp create mode 100644 templates/dropshell-agent/_nuke_other.sh diff --git a/src/server_env_manager.cpp b/src/server_env_manager.cpp index 4fe3060..f4e644b 100644 --- a/src/server_env_manager.cpp +++ b/src/server_env_manager.cpp @@ -2,10 +2,10 @@ #include "utils/directories.hpp" #include "utils/utils.hpp" #include "services.hpp" -#include "contrib/base64.hpp" #include "templates.hpp" #include "utils/utils.hpp" #include "utils/json.hpp" +#include "utils/execute.hpp" #include #include @@ -102,22 +102,12 @@ std::string server_env_manager::get_variable(const std::string& name) const { return it->second; } -// Helper method implementations -std::string server_env_manager::construct_ssh_cmd(bool allocateTTY) const { - std::stringstream ssh_cmd; - ssh_cmd << "ssh -p " << get_SSH_PORT() << " " << (allocateTTY ? "-tt " : "") - << get_SSH_USER() << "@" << get_SSH_HOST(); - return ssh_cmd.str(); -} - -sCommand server_env_manager::construct_standard_command_run_cmd(const std::string &service_name, const std::string &command, std::vector args, bool silent) const +sCommand server_env_manager::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, std::vector args, bool silent) const { if (command.empty()) return sCommand(); std::string remote_service_template_path = remotepath::service_template(mServerName,service_name); - std::string remote_service_config_path = remotepath::service_config(mServerName,service_name); - std::string script_path = remote_service_template_path + "/" + command + ".sh"; std::map env_vars; @@ -143,12 +133,12 @@ sCommand server_env_manager::construct_standard_command_run_cmd(const std::strin bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const { sCommand scommand("test -d " + quote(dir_path)); - return execute_ssh_command(scommand); + 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("test -f " + quote(file_path)); - return execute_ssh_command(scommand); + return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent); } bool server_env_manager::check_remote_items_exist(const std::vector &file_paths) const @@ -163,7 +153,7 @@ bool server_env_manager::check_remote_items_exist(const std::vector // check if all items in the vector exist on the remote server, in a single command. sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done"); - bool okay = execute_ssh_command(scommand); + bool okay = execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent); if (!okay) { std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl; return false; @@ -171,152 +161,26 @@ bool server_env_manager::check_remote_items_exist(const std::vector return true; } -bool EXITSTATUSCHECK(int ret) { - return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute. -} - -bool server_env_manager::execute_ssh_command(const sCommand& command, bool allocateTTY) const { - std::string full_cmd = construct_ssh_cmd(allocateTTY) + " " + (allocateTTY ? halfquote(command.construct_rawcmd()) : quote(command.construct_safecmd())); - int ret = system(full_cmd.c_str()); - if (ret == -1) { - std::cerr << "Error: Failed to execute command: " << full_cmd << std::endl; - // system() failed to execute - return false; - } else { - // Check if the command exited normally and with status 0 - return EXITSTATUSCHECK(ret); - } -} - -bool server_env_manager::execute_ssh_command_and_capture_output(const sCommand& command, std::string &output, bool allocateTTY) const -{ - std::string full_cmd = construct_ssh_cmd(allocateTTY) + " " + quote(command.construct_safecmd()); - return execute_local_command_and_capture_output(full_cmd, output); -} - bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector args, bool silent) const { - sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent); + sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, silent); if (scommand.get_command_to_run().empty()) return false; - bool allocateTTY = (command=="ssh"); - return execute_ssh_command(scommand, allocateTTY); + cMode mode = (command=="ssh") ? cMode::Interactive : cMode::Silent; + return execute_ssh_command(get_SSH_INFO(), scommand, mode); } bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector args, std::string &output, bool silent) const { - sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent); + sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, silent); if (scommand.get_command_to_run().empty()) return false; - bool allocateTTY = (command=="ssh"); - return execute_ssh_command_and_capture_output(scommand, output, allocateTTY); + return execute_ssh_command(get_SSH_INFO(), scommand, cMode::CaptureOutput, &output); } -bool server_env_manager::execute_local_command(const sCommand& command) { - if (command.get_command_to_run().empty()) - return false; - int ret = system(command.construct_safecmd().c_str()); - return EXITSTATUSCHECK(ret); -} - -bool server_env_manager::execute_local_command_interactive(const sCommand &command) -{ - if (command.get_command_to_run().empty()) - return false; - std::string full_command = command.construct_rawcmd(); // Get the command string - - pid_t pid = fork(); - - if (pid == -1) { - // Fork failed - perror("fork failed"); - return false; - } else if (pid == 0) { - // Child process - std::vector commandvec = {"bash", "-c", full_command.c_str(),NULL}; - - std::cout << "Executing command: "; - for (auto & x : commandvec) std::cout << x << " "; - std::cout << std::endl; - - execvp(commandvec[0], const_cast(commandvec.data())); - // If execvp returns, it means an error occurred - perror("execvp failed"); - exit(EXIT_FAILURE); // Exit child process on error - } else { - // Parent process - int ret; - // Wait for the child process to complete - waitpid(pid, &ret, 0); - - return EXITSTATUSCHECK(ret); - } -} - -bool server_env_manager::execute_local_command_and_capture_output(const sCommand& command, std::string &output) -{ - if (command.get_command_to_run().empty()) - return false; - std::string full_cmd = command.construct_safecmd() + " 2>&1"; - FILE *pipe = popen(full_cmd.c_str(), "r"); - if (!pipe) { - return false; - } - char buffer[128]; - while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { - output += buffer; - } - int ret = pclose(pipe); - return EXITSTATUSCHECK(ret); -} - - -std::string sCommand::construct_safecmd() const -{ - if (mCmd.empty()) - return ""; - std::string to_encode; - - for (const auto& env_var : mVars) { - to_encode += env_var.first + "=" + quote(dequote(trim(env_var.second))) + " "; - } - to_encode += mCmd; - - std::string encoded = base64_encode(to_encode); - - std::string commandstr = "echo " + encoded + " | base64 -d | bash"; - - if (!mDir.empty()) - commandstr = "cd " + quote(mDir) + " && " + commandstr; - return commandstr; -} - -std::string sCommand::construct_rawcmd() const -{ - if (mCmd.empty()) - return ""; - - std::string rawcmd; - if (!mDir.empty()) - rawcmd = "cd " + quote(mDir) + " && "; - - for (const auto& env_var : mVars) { - rawcmd += env_var.first + "=" + quote(dequote(trim(env_var.second))) + " "; - } - rawcmd += mCmd; - return rawcmd; -} // base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh" // echo YmFzaCAtYyAnRk9PPUJBUiBXSEVFPVlBWSBiYXNoIC4vdGVzdC5zaCcK | base64 -d | bash -std::string makesafecmd(const std::string &command) -{ - if (command.empty()) - return ""; - std::string encoded = base64_encode(dequote(trim(command))); - std::string commandstr = "echo " + encoded + " | base64 -d | bash"; - return commandstr; -} } // namespace dropshell \ No newline at end of file diff --git a/src/server_env_manager.hpp b/src/server_env_manager.hpp index d25d58d..41387c4 100644 --- a/src/server_env_manager.hpp +++ b/src/server_env_manager.hpp @@ -9,35 +9,13 @@ #include #include #include +#include "utils/execute.hpp" + namespace dropshell { -// class to hold a command to run on the remote server. -class sCommand { - public: - sCommand(std::string directory_to_run_in, std::string command_to_run, const std::map & env_vars) : - mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {} - sCommand(std::string command_to_run) : - mDir(""), mCmd(command_to_run), mVars({}) {} - sCommand() : mDir(""), mCmd(""), mVars({}) {} +class server_env_manager; - std::string get_directory_to_run_in() const { return mDir; } - std::string get_command_to_run() const { return mCmd; } - const std::map& get_env_vars() const { return mVars; } - - void add_env_var(const std::string& key, const std::string& value) { mVars[key] = value; } - - std::string construct_safecmd() const; - std::string construct_rawcmd() const; - - bool empty() const { return mCmd.empty(); } - - private: - std::string mDir; - std::string mCmd; - std::map mVars; -}; - -std::string makesafecmd(const std::string& command); +// ------------------------------------------------------------------------------------------------ // reads path / server.env and provides a class to access the variables. // each env file is required to have the following variables: @@ -65,6 +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()}; } bool is_valid() const { return mValid; } std::string get_server_name() const { return mServerName; } @@ -77,17 +56,8 @@ class server_env_manager { bool run_remote_template_command(const std::string& service_name, const std::string& command, std::vector args, bool silent=false) const; bool run_remote_template_command_and_capture_output(const std::string& service_name, const std::string& command, std::vector args, std::string & output, bool silent=false) const; - public: - bool execute_ssh_command(const sCommand& command, bool allocateTTY=false) const; - bool execute_ssh_command_and_capture_output(const sCommand& command, std::string & output, bool allocateTTY=false) const; - - static bool execute_local_command(const sCommand& command); - static bool execute_local_command_interactive(const sCommand& command); - static bool execute_local_command_and_capture_output(const sCommand& command, std::string & output); - private: - std::string construct_ssh_cmd(bool allocateTTY=false) const; - sCommand construct_standard_command_run_cmd(const std::string& service_name, const std::string& command, std::vector args, bool silent) const; + sCommand construct_standard_template_run_cmd(const std::string& service_name, const std::string& command, std::vector args, bool silent) const; private: std::string mServerName; diff --git a/src/service_runner.cpp b/src/service_runner.cpp index 73cc6f0..c597d0b 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -52,7 +52,7 @@ bool service_runner::install(bool silent) { // Create service directory std::string remote_service_path = remotepath::service(mServer, mService); std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path); - if (!mServerEnv.execute_ssh_command(mkdir_cmd)) + if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent)) { std::cerr << "Failed to create service directory " << remote_service_path << std::endl; return false; @@ -60,7 +60,7 @@ bool service_runner::install(bool silent) { // Check if rsync is installed on remote host std::string check_rsync_cmd = "which rsync > /dev/null 2>&1"; - if (!mServerEnv.execute_ssh_command(check_rsync_cmd)) + if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(check_rsync_cmd), cMode::Silent)) { std::cerr << "rsync is not installed on the remote host" << std::endl; return false; @@ -74,7 +74,7 @@ bool service_runner::install(bool silent) { mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remotepath::service_template(mServer, mService)+"/"); //std::cout << std::endl << rsync_cmd << std::endl << std::endl; - if (!mServerEnv.execute_local_command(rsync_cmd)) + if (!execute_local_command(rsync_cmd, silent ? cMode::Silent : cMode::None)) { std::cerr << "Failed to copy template files using rsync" << std::endl; std::cerr << "Is rsync installed on the remote host?" << std::endl; @@ -94,7 +94,7 @@ bool service_runner::install(bool silent) { quote(local_service_path + "/") + " "+ mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remotepath::service_config(mServer,mService) + "/"); - if (!mServerEnv.execute_local_command(rsync_cmd)) + if (!execute_local_command(rsync_cmd, silent ? cMode::Silent : cMode::None)) { std::cerr << "Failed to copy service files using rsync" << std::endl; return false; @@ -138,7 +138,7 @@ bool service_runner::uninstall(bool silent) { // 4. Remove the service directory from the server std::string rm_cmd = "rm -rf " + quote(remotepath::service(mServer, mService)); - if (!mServerEnv.execute_ssh_command(rm_cmd)) { + if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent)) { std::cerr << "Failed to remove service directory" << std::endl; return false; } @@ -153,32 +153,16 @@ bool service_runner::nuke(bool silent) if (!mServerEnv.is_valid()) return false; // should never hit this. - // 2. Check if service directory exists on server - if (!mServerEnv.check_remote_dir_exists(remotepath::service(mServer, mService))) - std::cerr << "Service is not installed: " << mService << std::endl; - else + std::map env_vars; + if (!get_all_service_env_vars(mServer, mService, env_vars)) + std::cerr << "Warning: Failed to get all service env vars for " << mService << std::endl; + + std::string remote_service_path = remotepath::service(mServer, mService); + bool okay = mServerEnv.run_remote_template_command("dropshell-agent", "_nuke_other", {mService, remote_service_path}, silent); + if (!okay) { - // try uninstalling. - if (gTemplateManager().template_command_exists(mServiceInfo.template_name, "uninstall")) - if (!mServerEnv.run_remote_template_command(mService, "uninstall", {})) - std::cerr << "Warning: Uninstall script failed, but continuing with directory removal" << std::endl; - - // try nuke script. - if (gTemplateManager().template_command_exists(mServiceInfo.template_name, "nuke")) - if (!mServerEnv.run_remote_template_command(mService, "nuke", {})) - std::cerr << "Warning: Nuke script failed, but continuing with directory removal" << std::endl; - - // try rm -rf. - std::string remotedir = remotepath::service(mServer, mService); - std::string remoteparentdir = get_parent(remotedir); - std::string remotechilddir = get_child(remotedir); - - std::string cmd = quote("rm -rf /parent/"+remotechilddir); - std::string rm_cmd = "docker run --rm -v "+quote(remoteparentdir)+":/parent debian bash -c "+cmd; - if (!mServerEnv.execute_ssh_command(rm_cmd)) { - std::cerr << "Failed to remove service directory" << std::endl; - return false; - } + std::cerr << "Warning: Nuke script failed" << std::endl; + return false; } std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl; @@ -206,7 +190,7 @@ bool service_runner::fullnuke() } std::string rm_cmd = "rm -rf " + quote(local_service_path); - if (!mServerEnv.execute_local_command(rm_cmd)) { + if (!execute_local_command(rm_cmd, cMode::Silent)) { std::cerr << "Failed to remove service directory" << std::endl; return false; } @@ -416,34 +400,20 @@ std::string service_runner::healthmark() return HealthStatus2String(status); } -void service_runner::interactive_ssh(const std::string & server_name, const std::string & command) { +bool service_runner::interactive_ssh(const std::string & server_name, const std::string & command) { std::string serverpath = localpath::server(server_name); if (serverpath.empty()) { std::cerr << "Error: Server not found: " << server_name << std::endl; - return; + return false; } - + + sCommand scommand("bash"); server_env_manager env(server_name); if (!env.is_valid()) { std::cerr << "Error: Invalid server environment file: " << server_name << std::endl; - return; + return false; } - - std::string ssh_address = env.get_SSH_HOST(); - std::string ssh_user = env.get_SSH_USER(); - std::string ssh_port = env.get_SSH_PORT(); - - std::string login = ssh_user + "@" + ssh_address; - - // Execute ssh with server_name and command - if (command.empty()) - execlp("ssh", "ssh", "-tt", login.c_str(), "-p", ssh_port.c_str(), nullptr); - else - execlp("ssh", "ssh", "-tt", login.c_str(), "-p", ssh_port.c_str(), command.c_str(), nullptr); - - // If exec returns, it means there was an error - perror("ssh execution failed"); - exit(EXIT_FAILURE); + return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive); } void service_runner::edit_server(const std::string &server_name) @@ -491,19 +461,19 @@ bool service_runner::edit_file(const std::string &file_path) } std::cout << "Editing file: " << file_path << std::endl; - return server_env_manager::execute_local_command_interactive(editor_cmd); + return execute_local_command(editor_cmd, cMode::Interactive); } -void service_runner::interactive_ssh_service() +bool service_runner::interactive_ssh_service() { std::set used_commands = get_used_commands(mServer, mService); if (used_commands.find("ssh") == used_commands.end()) { std::cerr << "Error: "<< mService <<" does not support ssh" << std::endl; - return; + return false; } std::vector args; // not passed through yet. - mServerEnv.run_remote_template_command(mService, "ssh", args); + return mServerEnv.run_remote_template_command(mService, "ssh", args); } void service_runner::edit_service_config() @@ -598,7 +568,7 @@ bool service_runner::restore(std::string backup_file, bool silent) // Copy backup file from local to server std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " + quote(local_backup_file_path) + " " + mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path) + (silent ? " > /dev/null 2>&1" : ""); - if (!mServerEnv.execute_local_command(scp_cmd)) { + if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) { std::cerr << "Failed to copy backup file from server" << std::endl; return false; } @@ -676,7 +646,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 (!mServerEnv.execute_ssh_command(mkdir_cmd)) { + 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; } @@ -720,7 +690,7 @@ bool service_runner::backup(bool silent) { std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " + mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : ""); - if (!mServerEnv.execute_local_command(scp_cmd)) { + if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) { std::cerr << "Failed to copy backup file from server" << std::endl; return false; } @@ -737,17 +707,16 @@ 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 (!server_env.execute_ssh_command(mkdir_cmd)) + 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; } - cRemoteTempFolder::~cRemoteTempFolder() { std::string rm_cmd = "rm -rf " + quote(mPath); - mServerEnv.execute_ssh_command(rm_cmd); + execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent); } std::string cRemoteTempFolder::path() const diff --git a/src/service_runner.hpp b/src/service_runner.hpp index b1b5511..af3fd3f 100644 --- a/src/service_runner.hpp +++ b/src/service_runner.hpp @@ -83,7 +83,7 @@ class service_runner { // launch an interactive ssh session on a server or service // replaces the current dropshell process with the ssh process - void interactive_ssh_service(); + bool interactive_ssh_service(); // edit the service configuration file void edit_service_config(); @@ -92,7 +92,7 @@ class service_runner { public: // utility functions static std::string get_latest_backup_file(const std::string& server, const std::string& service); - static void interactive_ssh(const std::string & server_name, const std::string & command); + static bool interactive_ssh(const std::string & server_name, const std::string & command); static void edit_server(const std::string & server_name); static bool edit_file(const std::string & file_path); static std::map get_all_services_status(std::string server_name); diff --git a/src/utils/execute.cpp b/src/utils/execute.cpp new file mode 100644 index 0000000..24afd38 --- /dev/null +++ b/src/utils/execute.cpp @@ -0,0 +1,167 @@ +#include "execute.hpp" +#include "assert.hpp" +#include "contrib/base64.hpp" +#include "utils/utils.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +bool EXITSTATUSCHECK(int ret) { + return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute. +} + +namespace dropshell { +bool execute_local_command_interactive(const sCommand &command, bool silent) +{ + if (command.get_command_to_run().empty()) + return false; + std::string full_command = command.construct_cmd(cStyle::Raw); // Get the command string + + pid_t pid = fork(); + + if (pid == -1) { + // Fork failed + perror("fork failed"); + return false; + } else if (pid == 0) { + // Child process + std::vector commandvec = {"bash", "-c", full_command.c_str(),NULL}; + + if (!silent) { + std::cout << "Executing command: "; + for (auto & x : commandvec) std::cout << x << " "; + std::cout << std::endl; + } + + execvp(commandvec[0], const_cast(commandvec.data())); + // If execvp returns, it means an error occurred + perror("execvp failed"); + exit(EXIT_FAILURE); // Exit child process on error + } else { + // Parent process + int ret; + // Wait for the child process to complete + waitpid(pid, &ret, 0); + + return EXITSTATUSCHECK(ret); + } +} + +bool execute_local_command(const sCommand& command, bool silent, bool safe) { + if (command.get_command_to_run().empty()) + return false; + cStyle style = safe ? cStyle::Safe : cStyle::Raw; + std::string full_cmd = command.construct_cmd(style) + " 2>&1" + (silent ? " > /dev/null" : ""); + int ret = system(full_cmd.c_str()); + return EXITSTATUSCHECK(ret); +} + +bool execute_local_command_and_capture_output(const sCommand& command, std::string &output, bool silent, bool safe) +{ + if (command.get_command_to_run().empty()) + return false; + cStyle style = safe ? cStyle::Safe : cStyle::Raw; + std::string full_cmd = command.construct_cmd(style) + " 2>&1"; + FILE *pipe = popen(full_cmd.c_str(), "r"); + if (!pipe) { + return false; + } + char buffer[128]; + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + output += buffer; + } + int ret = pclose(pipe); + return EXITSTATUSCHECK(ret); +} + + + +bool execute_local_command(const sCommand & command, cMode mode, std::string * output /* = nullptr */) +{ + if (mode & cMode::Interactive) { + ASSERT_MSG((mode & cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together"); + ASSERT_MSG(output == nullptr, "Interactive mode and an output string cannot be used together"); + ASSERT_MSG(! (mode & cMode::SafeCommand), "Interactive mode and safe command mode cannot be used together"); + return execute_local_command_interactive(command, mode & cMode::Silent); + } + + if (mode & cMode::CaptureOutput) { + ASSERT_MSG(output != nullptr, "Capture output mode requires an output string to be provided"); + return execute_local_command_and_capture_output(command, *output, mode & cMode::Silent, mode & cMode::SafeCommand); + } + + return execute_local_command(command, mode & cMode::Silent, mode & cMode::SafeCommand); +} + +bool execute_ssh_command(const sSSHInfo &ssh_info, const sCommand &command, cMode mode, std::string *output) +{ + if (command.get_command_to_run().empty()) + return false; + + ASSERT_MSG(!(mode & cMode::Interactive && mode & cMode::SafeCommand), "Safe command mode must not be used with Interactive mode"); + + std::stringstream ssh_cmd; + ssh_cmd << "ssh -p " << ssh_info.port << " " << (mode & cMode::Interactive ? "-tt " : "") + << ssh_info.user << "@" << ssh_info.host; + + std::string full_cmd = ssh_cmd.str() + " " + command.get_command_to_run(); + + sCommand ssh_command = { + command.get_directory_to_run_in(), + full_cmd, + command.get_env_vars() + }; + + bool rval = execute_local_command(ssh_command, mode, output); + + if (!rval) { + std::cerr < +#include + +namespace dropshell { + +class sCommand; + +// mode bitset +enum class cMode { + None = 0, + Interactive = 1, + Silent = 2, + CaptureOutput = 4, + SafeCommand = 8 +}; + +typedef struct sSSHInfo { + std::string host; + std::string user; + std::string port; +} sSSHInfo; + +bool execute_local_command(const sCommand & command, cMode mode = cMode::None, std::string * output = nullptr); +bool execute_ssh_command(const sSSHInfo & ssh_info, const sCommand & command, cMode mode = cMode::None, std::string * output = nullptr); + +std::string makesafecmd(const std::string& command); + + +// ------------------------------------------------------------------------------------------------ + +enum class cStyle { + Safe = 0, + Raw = 1 +}; + +inline bool is_safe(cStyle style) { return style == cStyle::Safe; } + +// class to hold a command to run on the remote server. +class sCommand { + public: + sCommand(std::string directory_to_run_in, std::string command_to_run, const std::map & env_vars) : + mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {} + sCommand(std::string command_to_run) : + mDir(""), mCmd(command_to_run), mVars({}) {} + sCommand() : mDir(""), mCmd(""), mVars({}) {} + + std::string get_directory_to_run_in() const { return mDir; } + std::string get_command_to_run() const { return mCmd; } + const std::map& get_env_vars() const { return mVars; } + + void add_env_var(const std::string& key, const std::string& value) { mVars[key] = value; } + + + std::string construct_cmd(cStyle style) const; + + bool empty() const { return mCmd.empty(); } + + private: + std::string mDir; + std::string mCmd; + std::map mVars; +}; + + +// Bitwise AND operator for cMode +inline bool operator&(cMode lhs, cMode rhs) { + return ( + static_cast(lhs) & static_cast(rhs) != 0 + ); +} + +inline cMode operator-(cMode lhs, cMode rhs) { + return static_cast( + static_cast(lhs) & ~static_cast(rhs) + ); +} + +inline cMode operator|(cMode lhs, cMode rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +} // namespace dropshell + + + +#endif diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index c8a32e7..a84d905 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -80,6 +80,11 @@ std::string halfquote(std::string str) return "'" + str + "'"; } +std::string escapequotes(std::string str) +{ + return std::regex_replace(str, std::regex("\""), "\\\""); +} + std::string multi2string(std::vector values) { std::string result; diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index 91c5869..58febfb 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -21,6 +21,7 @@ std::string dequote(std::string str); std::string quote(std::string str); std::string halfquote(std::string str); std::string requote(std::string str); +std::string escapequotes(std::string str); std::string multi2string(std::vector values); std::vector string2multi(std::string values); diff --git a/templates/dropshell-agent/_nuke_other.sh b/templates/dropshell-agent/_nuke_other.sh new file mode 100644 index 0000000..4fbcbc3 --- /dev/null +++ b/templates/dropshell-agent/_nuke_other.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$SCRIPT_DIR/shared/_common.sh" + + +A_SERVICE="$1" +A_SERVICE_PATH="$2" + + +# 1. Check if service directory exists on server +[ -d "$A_SERVICE_PATH" ] || _die "Service is not installed: $A_SERVICE" + +# uninstall the service +if [ -f "$A_SERVICE_PATH/uninstall.sh" ]; then + $A_SERVICE_PATH/uninstall.sh +fi + +# nuke the service +if [ -f "$A_SERVICE_PATH/nuke.sh" ]; then + $A_SERVICE_PATH/nuke.sh +fi + +# remove the service directory +rm -rf "$A_SERVICE_PATH"