BROKEN
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 21s

This commit is contained in:
Your Name 2025-05-06 21:32:41 +12:00
parent 484613d10d
commit bbc280a50a
9 changed files with 335 additions and 245 deletions

View File

@ -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 <iostream>
#include <memory>
@ -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<std::string> args, bool silent) const
sCommand server_env_manager::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, std::vector<std::string> 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<std::string, std::string> 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<std::string> &file_paths) const
@ -163,7 +153,7 @@ bool server_env_manager::check_remote_items_exist(const std::vector<std::string>
// 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<std::string>
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<std::string> 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<std::string> 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<const char *> 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<char* const*>(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

View File

@ -9,35 +9,13 @@
#include <map>
#include <memory>
#include <vector>
#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<std::string, std::string> & 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<std::string, std::string>& 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<std::string, std::string> 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<std::string> args, bool silent=false) const;
bool run_remote_template_command_and_capture_output(const std::string& service_name, const std::string& command, std::vector<std::string> 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<std::string> args, bool silent) const;
sCommand construct_standard_template_run_cmd(const std::string& service_name, const std::string& command, std::vector<std::string> args, bool silent) const;
private:
std::string mServerName;

View File

@ -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<std::string, std::string> 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<std::string> 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<std::string> 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

View File

@ -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<std::string, ServiceStatus> get_all_services_status(std::string server_name);

167
src/utils/execute.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "execute.hpp"
#include "assert.hpp"
#include "contrib/base64.hpp"
#include "utils/utils.hpp"
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <iostream>
#include <string>
#include <cstdlib>
#include <sstream>
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<const char *> 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<char* const*>(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 <<std::endl<<std::endl;
std::cerr << "Error: Failed to execute ssh command: { [" << ssh_command.get_directory_to_run_in() << "], [";
std::cerr << ssh_command.get_command_to_run() << "], [";
for (const auto& env_var : ssh_command.get_env_vars()) {
std::cerr << env_var.first << "=" << env_var.second << ", ";
}
std::cerr << "] }" << std::endl;
std::cerr <<std::endl<<std::endl;
}
return rval;
}
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;
}
std::string sCommand::construct_cmd(cStyle style) const
{
if (mCmd.empty())
return "";
std::string cdcmd;
if (!mDir.empty())
cdcmd = "cd " + quote(mDir) + " && ";
std::string cmdstr;
for (const auto& env_var : mVars) {
cmdstr += env_var.first + "=" + quote(dequote(trim(env_var.second))) + " ";
}
cmdstr += mCmd;
if (is_safe(style))
cmdstr = makesafecmd(cmdstr);
return cdcmd + cmdstr;
}
} // namespace dropshell

89
src/utils/execute.hpp Normal file
View File

@ -0,0 +1,89 @@
#ifndef EXECUTE_HPP
#define EXECUTE_HPP
#include <string>
#include <map>
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<std::string, std::string> & 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<std::string, std::string>& 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<std::string, std::string> mVars;
};
// Bitwise AND operator for cMode
inline bool operator&(cMode lhs, cMode rhs) {
return (
static_cast<int>(lhs) & static_cast<int>(rhs) != 0
);
}
inline cMode operator-(cMode lhs, cMode rhs) {
return static_cast<cMode>(
static_cast<int>(lhs) & ~static_cast<int>(rhs)
);
}
inline cMode operator|(cMode lhs, cMode rhs) {
return static_cast<cMode>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
} // namespace dropshell
#endif

View File

@ -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<std::string> values)
{
std::string result;

View File

@ -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<std::string> values);
std::vector<std::string> string2multi(std::string values);

View File

@ -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"