This commit is contained in:
parent
5d42db7331
commit
330bdf9941
@ -32,9 +32,6 @@ configure_file(
|
|||||||
# Set CMAKE_MODULE_PATH to include our custom find modules
|
# Set CMAKE_MODULE_PATH to include our custom find modules
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
# Find required libraries
|
|
||||||
find_package(LibSSH REQUIRED)
|
|
||||||
|
|
||||||
# Auto-detect source files
|
# Auto-detect source files
|
||||||
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||||
file(GLOB_RECURSE HEADERS "src/*.hpp")
|
file(GLOB_RECURSE HEADERS "src/*.hpp")
|
||||||
@ -48,7 +45,6 @@ target_include_directories(dropshell PRIVATE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils
|
${CMAKE_CURRENT_SOURCE_DIR}/src/utils
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/contrib
|
${CMAKE_CURRENT_SOURCE_DIR}/src/contrib
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src>
|
||||||
${LIBSSH_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
@ -58,7 +54,7 @@ FetchContent_Declare(
|
|||||||
GIT_TAG v2.1.5 # <HASH or TAG>
|
GIT_TAG v2.1.5 # <HASH or TAG>
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(libassert)
|
FetchContent_MakeAvailable(libassert)
|
||||||
target_link_libraries(dropshell PRIVATE libassert::assert)
|
target_link_libraries(dropshell libassert::assert)
|
||||||
|
|
||||||
# On windows copy libassert.dll to the same directory as the executable for your_target
|
# On windows copy libassert.dll to the same directory as the executable for your_target
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@ -72,7 +68,6 @@ endif()
|
|||||||
|
|
||||||
# Link libraries
|
# Link libraries
|
||||||
target_link_libraries(dropshell PRIVATE
|
target_link_libraries(dropshell PRIVATE
|
||||||
${LIBSSH_LIBRARIES}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install targets
|
# Install targets
|
||||||
|
4
build.sh
4
build.sh
@ -101,6 +101,9 @@ if [ $AUTO_INSTALL = true ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
read -p "Do you want to install the program? (y/n) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
print_status "Installing dropshell..."
|
print_status "Installing dropshell..."
|
||||||
sudo make install
|
sudo make install
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
@ -110,6 +113,7 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Return to original directory
|
# Return to original directory
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -108,6 +108,7 @@ void printversion() {
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
|
HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
|
||||||
|
HAPPYEXIT("makesafecmd", std::cout<<makesafecmd(safearg(argc,argv,2))<<std::endl)
|
||||||
HAPPYEXIT("version", printversion())
|
HAPPYEXIT("version", printversion())
|
||||||
BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
|
BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
|
||||||
ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
|
ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "utils/json.hpp"
|
#include "utils/json.hpp"
|
||||||
#include "utils/runner.hpp"
|
#include "utils/execute.hpp"
|
||||||
|
|
||||||
#include <libassert/assert.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -104,90 +102,94 @@ std::string server_env_manager::get_variable(const std::string& name) const {
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
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())
|
if (command.empty())
|
||||||
// return sCommand();
|
return sCommand();
|
||||||
|
|
||||||
// std::string remote_service_template_path = remotepath::service_template(mServerName,service_name);
|
std::string remote_service_template_path = remotepath::service_template(mServerName,service_name);
|
||||||
// std::string script_path = remote_service_template_path + "/" + command + ".sh";
|
std::string script_path = remote_service_template_path + "/" + command + ".sh";
|
||||||
|
|
||||||
// std::map<std::string, std::string> env_vars;
|
std::map<std::string, std::string> env_vars;
|
||||||
// if (!get_all_service_env_vars(mServerName, service_name, 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;
|
std::cerr << "Error: Failed to get all service env vars for " << service_name << std::endl;
|
||||||
// return sCommand();
|
return sCommand();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// std::string argstr = "";
|
std::string argstr = "";
|
||||||
// for (const auto& arg : args) {
|
for (const auto& arg : args) {
|
||||||
// argstr += " " + quote(dequote(trim(arg)));
|
argstr += " " + quote(dequote(trim(arg)));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
|
sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
|
||||||
|
|
||||||
// if (scommand.empty())
|
if (scommand.empty())
|
||||||
// std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
|
std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
|
||||||
|
|
||||||
// return scommand;
|
return scommand;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
|
bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
|
||||||
{
|
{
|
||||||
runner::runner_ssh test_runner(get_SSH_INFO(),"test",{"-d", quote(dir_path)});
|
sCommand scommand("test -d " + quote(dir_path));
|
||||||
return test_runner.execute();
|
return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
|
bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
|
||||||
runner::runner_ssh test_runner(get_SSH_INFO(),"test",{"-f",quote(file_path)});
|
sCommand scommand("test -f " + quote(file_path));
|
||||||
return test_runner.execute();
|
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
|
bool server_env_manager::check_remote_items_exist(const std::vector<std::string> &file_paths) const
|
||||||
{
|
{
|
||||||
// convert file_paths to a single string, separated by spaces
|
// convert file_paths to a single string, separated by spaces
|
||||||
std::string file_paths_str;
|
std::string file_paths_str;
|
||||||
|
std::string file_names_str;
|
||||||
for (const auto& file_path : file_paths) {
|
for (const auto& file_path : file_paths) {
|
||||||
file_paths_str += quote(file_path) + " ";
|
file_paths_str += quote(file_path) + " ";
|
||||||
|
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.
|
// check if all items in the vector exist on the remote server, in a single command.
|
||||||
runner::runner_ssh test_runner(get_SSH_INFO(),"bash",{"-c","for item in " + file_paths_str + "; do test -f $item; done"});
|
sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done");
|
||||||
return test_runner.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent, std::map<std::string, std::string> extra_env_vars, std::string * output) const
|
bool okay = execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
|
||||||
{
|
if (!okay) {
|
||||||
std::string working_dir = remotepath::service_template(mServerName,service_name);
|
std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl;
|
||||||
std::string script_path = working_dir + "/" + command + ".sh";
|
|
||||||
std::string argstr = "";
|
|
||||||
for (const auto& arg : args) {
|
|
||||||
argstr += " " + quote(dequote(trim(arg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent, std::map<std::string, std::string> extra_env_vars) const
|
||||||
|
{
|
||||||
|
sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, silent);
|
||||||
|
|
||||||
// add the extra env vars to the command
|
// add the extra env vars to the command
|
||||||
for (const auto& [key, value] : extra_env_vars)
|
for (const auto& [key, value] : extra_env_vars)
|
||||||
env_vars[key] = value;
|
scommand.add_env_var(key, value);
|
||||||
|
|
||||||
bool interactive = (command=="ssh");
|
if (scommand.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
ASSERT(!output || !silent); // if output is captured, silent must be false
|
cMode mode = (command=="ssh") ? (cMode::Interactive | cMode::RawCommand) : cMode::Silent;
|
||||||
ASSERT(!interactive || !silent); // if command is ssh, silent must be false
|
return execute_ssh_command(get_SSH_INFO(), scommand, mode);
|
||||||
|
|
||||||
if (interactive) {
|
|
||||||
runner::runner_ssh_interactive bash_runner(get_SSH_INFO(),"bash",{"-c", quote(script_path) + argstr}, working_dir, env_vars);
|
|
||||||
return bash_runner.execute();
|
|
||||||
} else {
|
|
||||||
runner::runner_ssh bash_runner(get_SSH_INFO(),"bash",{"-c", quote(script_path) + argstr}, working_dir, env_vars, silent);
|
|
||||||
return bash_runner.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, std::map<std::string, std::string> extra_env_vars) const
|
||||||
|
{
|
||||||
|
sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, false);
|
||||||
|
if (scommand.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// add the extra env vars to the command
|
||||||
|
for (const auto& [key, value] : extra_env_vars)
|
||||||
|
scommand.add_env_var(key, value);
|
||||||
|
|
||||||
|
cMode mode = cMode::CaptureOutput | cMode::RawCommand;
|
||||||
|
return execute_ssh_command(get_SSH_INFO(), scommand, mode, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh"
|
// base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh"
|
||||||
// echo YmFzaCAtYyAnRk9PPUJBUiBXSEVFPVlBWSBiYXNoIC4vdGVzdC5zaCcK | base64 -d | bash
|
// echo YmFzaCAtYyAnRk9PPUJBUiBXSEVFPVlBWSBiYXNoIC4vdGVzdC5zaCcK | base64 -d | bash
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "utils/runner.hpp"
|
#include "utils/execute.hpp"
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
class server_env_manager;
|
class server_env_manager;
|
||||||
@ -42,7 +43,7 @@ class server_env_manager {
|
|||||||
std::string get_SSH_USER() const { return get_variable("SSH_USER"); }
|
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_SSH_PORT() const { return get_variable("SSH_PORT"); }
|
||||||
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
|
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
|
||||||
runner::sSSHInfo get_SSH_INFO() const { return runner::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()}; }
|
||||||
bool is_valid() const { return mValid; }
|
bool is_valid() const { return mValid; }
|
||||||
std::string get_server_name() const { return mServerName; }
|
std::string get_server_name() const { return mServerName; }
|
||||||
|
|
||||||
@ -53,10 +54,12 @@ class server_env_manager {
|
|||||||
bool check_remote_items_exist(const std::vector<std::string>& file_paths) const;
|
bool check_remote_items_exist(const std::vector<std::string>& file_paths) const;
|
||||||
|
|
||||||
bool run_remote_template_command(const std::string& service_name, const std::string& command,
|
bool run_remote_template_command(const std::string& service_name, const std::string& command,
|
||||||
std::vector<std::string> args = {}, bool silent = false, std::map<std::string, std::string> extra_env_vars = {}, std::string * output = nullptr) const;
|
std::vector<std::string> args, bool silent, std::map<std::string, std::string> extra_env_vars) 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, std::map<std::string, std::string> extra_env_vars) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//sCommand construct_standard_template_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:
|
private:
|
||||||
std::string mServerName;
|
std::string mServerName;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -15,7 +16,7 @@
|
|||||||
#include "services.hpp"
|
#include "services.hpp"
|
||||||
#include "utils/directories.hpp"
|
#include "utils/directories.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "utils/runner.hpp"
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@ -52,31 +53,35 @@ bool service_runner::install(bool silent) {
|
|||||||
if (!tinfo.is_set())
|
if (!tinfo.is_set())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Create service directory
|
||||||
{ // Create service directory
|
std::string remote_service_path = remotepath::service(mServer, mService);
|
||||||
runner::runner_ssh mkdir_runner(mServerEnv.get_SSH_INFO(),"mkdir",{"-p",remotepath::service(mServer, mService)});
|
std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path);
|
||||||
if (!mkdir_runner.execute())
|
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to create service directory " << remotepath::service(mServer, mService) << std::endl;
|
std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{ // Check if rsync is installed on remote host
|
// Check if rsync is installed on remote host
|
||||||
runner::runner_ssh which_runner(mServerEnv.get_SSH_INFO(),"which",{"rsync"});
|
std::string check_rsync_cmd = "which rsync > /dev/null 2>&1";
|
||||||
if (!which_runner.execute())
|
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;
|
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// make sure all shell files are executable
|
|
||||||
make_shell_files_executable(tinfo.local_template_path().string());
|
|
||||||
|
|
||||||
// Copy template files
|
// Copy template files
|
||||||
{
|
{
|
||||||
if (!rsync_copy(tinfo.local_template_path().string()+"/", remotepath::service_template(mServer, mService)+"/", silent)) {
|
std::cout << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl << std::string(8,' ')<<"[REMOTE] " << remotepath::service_template(mServer, mService) << "/" << std::endl;
|
||||||
|
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + mServerEnv.get_SSH_PORT() + "' " +
|
||||||
|
quote(tinfo.local_template_path().string()+"/") + " "+
|
||||||
|
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 (!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;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,8 +93,14 @@ bool service_runner::install(bool silent) {
|
|||||||
std::cerr << "Error: Service directory not found: " << local_service_path << std::endl;
|
std::cerr << "Error: Service directory not found: " << local_service_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
std::cout << "Copying: [LOCAL] " << local_service_path << std::endl <<std::string(8,' ')<<"[REMOTE] " << remotepath::service_config(mServer,mService) << std::endl;
|
||||||
if (!rsync_copy(local_service_path + "/", remotepath::service_config(mServer,mService) + "/", silent)) {
|
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + mServerEnv.get_SSH_PORT() + "' " +
|
||||||
|
quote(local_service_path + "/") + " "+
|
||||||
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
||||||
|
quote(remotepath::service_config(mServer,mService) + "/");
|
||||||
|
if (!execute_local_command(rsync_cmd, silent ? cMode::Silent : cMode::None))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to copy service files using rsync" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,10 +141,10 @@ bool service_runner::uninstall(bool silent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Remove the service directory from the server
|
// 4. Remove the service directory from the server
|
||||||
{
|
std::string rm_cmd = "rm -rf " + quote(remotepath::service(mServer, mService));
|
||||||
runner::runner_ssh rm_runner(mServerEnv.get_SSH_INFO(),"rm",{"-rf",remotepath::service(mServer, mService)});
|
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent)) {
|
||||||
if (!rm_runner.execute())
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
std::cerr << "Failed to remove remote service directory at " << remotepath::service(mServer, mService) << std::endl;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Service " << mService << " successfully uninstalled from " << mServer << std::endl;
|
std::cout << "Service " << mService << " successfully uninstalled from " << mServer << std::endl;
|
||||||
@ -178,13 +189,12 @@ bool service_runner::fullnuke()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
std::string rm_cmd = "rm -rf " + quote(local_service_path);
|
||||||
runner::runner_local rm_runner("rm",{"-rf",quote(local_service_path)});
|
if (!execute_local_command(rm_cmd, cMode::Silent)) {
|
||||||
if (!rm_runner.execute())
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
std::cerr << "Failed to remove local service directory at " << local_service_path << std::endl;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Service " << mService << " successfully fully nuked from " << mServer << std::endl;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +286,14 @@ std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std
|
|||||||
{
|
{
|
||||||
std::map<std::string, ServiceStatus> status;
|
std::map<std::string, ServiceStatus> status;
|
||||||
|
|
||||||
|
std::string command = "_allservicesstatus";
|
||||||
std::string service_name = "dropshell-agent";
|
std::string service_name = "dropshell-agent";
|
||||||
|
|
||||||
|
if (!gTemplateManager().template_command_exists(service_name, command))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << service_name << " does not contain the " << command << " script" << std::endl;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
server_env_manager env(server_name);
|
server_env_manager env(server_name);
|
||||||
if (!env.is_valid()) {
|
if (!env.is_valid()) {
|
||||||
@ -285,16 +301,9 @@ std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmd_path = remotepath::service_template(server_name,service_name) + "/shared/_allservicesstatus.sh";
|
|
||||||
std::string output;
|
std::string output;
|
||||||
{
|
if (!env.run_remote_template_command_and_capture_output(service_name, command, {}, output, true, {}))
|
||||||
runner::runner_ssh_capture bash_runner(env.get_SSH_INFO(),output,cmd_path);
|
|
||||||
if (!bash_runner.execute())
|
|
||||||
{
|
|
||||||
std::cerr << "Error: Failed to run command script at " << cmd_path << std::endl;
|
|
||||||
return status;
|
return status;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss(output);
|
std::stringstream ss(output);
|
||||||
std::string line;
|
std::string line;
|
||||||
@ -398,13 +407,13 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sCommand scommand("bash");
|
||||||
server_env_manager env(server_name);
|
server_env_manager env(server_name);
|
||||||
if (!env.is_valid()) {
|
if (!env.is_valid()) {
|
||||||
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
runner::runner_ssh_interactive ssh_runner(env.get_SSH_INFO(),command);
|
return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive | cMode::RawCommand);
|
||||||
return ssh_runner.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_runner::edit_server(const std::string &server_name)
|
void service_runner::edit_server(const std::string &server_name)
|
||||||
@ -440,10 +449,10 @@ bool service_runner::edit_file(const std::string &file_path)
|
|||||||
const char* editor_env = std::getenv("EDITOR");
|
const char* editor_env = std::getenv("EDITOR");
|
||||||
|
|
||||||
if (editor_env && std::strlen(editor_env) > 0) {
|
if (editor_env && std::strlen(editor_env) > 0) {
|
||||||
editor_cmd = std::string(editor_env);
|
editor_cmd = std::string(editor_env) + " " + quote(file_path);
|
||||||
} else if (isatty(STDIN_FILENO)) {
|
} else if (isatty(STDIN_FILENO)) {
|
||||||
// Check if stdin is connected to a terminal if EDITOR is not set
|
// Check if stdin is connected to a terminal if EDITOR is not set
|
||||||
editor_cmd = "nano";
|
editor_cmd = "nano -w " + quote(file_path);
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Error: Standard input is not a terminal and EDITOR environment variable is not set." << std::endl;
|
std::cerr << "Error: Standard input is not a terminal and EDITOR environment variable is not set." << std::endl;
|
||||||
std::cerr << "Try setting the EDITOR environment variable (e.g., export EDITOR=nano) or run in an interactive terminal." << std::endl;
|
std::cerr << "Try setting the EDITOR environment variable (e.g., export EDITOR=nano) or run in an interactive terminal." << std::endl;
|
||||||
@ -452,8 +461,7 @@ bool service_runner::edit_file(const std::string &file_path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Editing file: " << file_path << std::endl;
|
std::cout << "Editing file: " << file_path << std::endl;
|
||||||
runner::runner_local_interactive editor_runner(editor_cmd,{file_path});
|
return execute_local_command(editor_cmd, cMode::Interactive | cMode::RawCommand);
|
||||||
return editor_runner.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool service_runner::interactive_ssh_service()
|
bool service_runner::interactive_ssh_service()
|
||||||
@ -559,9 +567,8 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
|||||||
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
|
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
|
||||||
|
|
||||||
// Copy backup file from local to server
|
// Copy backup file from local to server
|
||||||
runner::runner_local scp_runner("scp",{quote(local_backup_file_path), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path)});
|
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 (!scp_runner.execute())
|
if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) {
|
||||||
{
|
|
||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -638,14 +645,11 @@ bool service_runner::backup(bool silent) {
|
|||||||
// Create backups directory on server if it doesn't exist
|
// Create backups directory on server if it doesn't exist
|
||||||
std::string remote_backups_dir = remotepath::backups(mServer);
|
std::string remote_backups_dir = remotepath::backups(mServer);
|
||||||
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
||||||
{
|
std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
|
||||||
runner::runner_ssh mkdir_runner(mServerEnv.get_SSH_INFO(),"mkdir",{"-p",quote(remote_backups_dir)});
|
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent)) {
|
||||||
if (!mkdir_runner.execute())
|
|
||||||
{
|
|
||||||
std::cerr << "Failed to create backups directory on server" << std::endl;
|
std::cerr << "Failed to create backups directory on server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Create backups directory locally if it doesn't exist
|
// Create backups directory locally if it doesn't exist
|
||||||
std::string local_backups_dir = gConfig().get_local_backup_path();
|
std::string local_backups_dir = gConfig().get_local_backup_path();
|
||||||
@ -683,9 +687,10 @@ bool service_runner::backup(bool silent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy backup file from server to local
|
// Copy backup file from server to local
|
||||||
runner::runner_local scp_runner("scp",{"-P", mServerEnv.get_SSH_PORT(), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path), quote(local_backup_file_path)});
|
std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " +
|
||||||
if (!scp_runner.execute())
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
||||||
{
|
quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
|
||||||
|
if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) {
|
||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -701,20 +706,17 @@ bool service_runner::backup(bool silent) {
|
|||||||
cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
|
cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
|
||||||
{
|
{
|
||||||
std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
|
std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
|
||||||
{
|
std::string mkdir_cmd = "mkdir -p " + quote(p);
|
||||||
runner::runner_ssh mkdir_runner(server_env.get_SSH_INFO(),"mkdir",{"-p",quote(p)});
|
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent))
|
||||||
if (!mkdir_runner.execute())
|
|
||||||
std::cerr << "Failed to create temp directory on server" << std::endl;
|
std::cerr << "Failed to create temp directory on server" << std::endl;
|
||||||
else
|
else
|
||||||
mPath = p;
|
mPath = p;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cRemoteTempFolder::~cRemoteTempFolder()
|
cRemoteTempFolder::~cRemoteTempFolder()
|
||||||
{
|
{
|
||||||
runner::runner_ssh rm_runner(mServerEnv.get_SSH_INFO(),"rm",{"-rf",quote(mPath)});
|
std::string rm_cmd = "rm -rf " + quote(mPath);
|
||||||
if (!rm_runner.execute())
|
execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent);
|
||||||
std::cerr << "Failed to remove temp directory on server" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cRemoteTempFolder::path() const
|
std::string cRemoteTempFolder::path() const
|
||||||
@ -769,17 +771,4 @@ std::string service_runner::get_latest_backup_file(const std::string& server, co
|
|||||||
return latest_file;
|
return latest_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool service_runner::rsync_copy(const std::string& local_path, const std::string& remote_path, bool silent) {
|
|
||||||
std::cout << "Copying: [LOCAL] " << local_path << std::endl << std::string(8,' ')<<"[REMOTE] " << remote_path << std::endl;
|
|
||||||
|
|
||||||
runner::runner_local rsync_runner("rsync", {"--delete","--mkpath","-zrpc","-e",quote("ssh -p " + mServerEnv.get_SSH_PORT()),quote(local_path),
|
|
||||||
quote(mServerEnv.get_SSH_USER()+"@"+mServerEnv.get_SSH_HOST()+":"+remote_path)});
|
|
||||||
|
|
||||||
if (!rsync_runner.execute()) {
|
|
||||||
std::cerr << "Failed to copy files using rsync" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -87,8 +87,6 @@ class service_runner {
|
|||||||
// edit the service configuration file
|
// edit the service configuration file
|
||||||
void edit_service_config();
|
void edit_service_config();
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
bool rsync_copy(const std::string& local_path, const std::string& remote_path, bool silent);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// utility functions
|
// utility functions
|
||||||
|
182
src/utils/execute.cpp
Normal file
182
src/utils/execute.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
#include <libassert/assert.hpp>
|
||||||
|
|
||||||
|
#include "execute.hpp"
|
||||||
|
#include "contrib/base64.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
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_and_capture_output(const sCommand& command, std::string * output, cMode mode)
|
||||||
|
{
|
||||||
|
ASSERT(output != nullptr, "Output string must be provided");
|
||||||
|
ASSERT(is_raw(mode), "Capture output mode requires raw command mode");
|
||||||
|
ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode");
|
||||||
|
if (command.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
|
cStyle style = getStyle(mode);
|
||||||
|
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 (hasFlag(mode, cMode::Interactive)) {
|
||||||
|
ASSERT(! hasFlag(mode, cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together");
|
||||||
|
ASSERT(output == nullptr, "Interactive mode and an output string cannot be used together");
|
||||||
|
ASSERT(is_raw(mode), "Interactive mode requires raw command mode");
|
||||||
|
|
||||||
|
return execute_local_command_interactive(command, hasFlag(mode, cMode::Silent));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasFlag(mode, cMode::CaptureOutput)) {
|
||||||
|
ASSERT(output != nullptr, "Capture output mode requires an output string to be provided");
|
||||||
|
ASSERT(is_raw(mode), "Capture output mode requires raw command mode");
|
||||||
|
ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode");
|
||||||
|
|
||||||
|
return execute_local_command_and_capture_output(command, output, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
|
cStyle style = getStyle(mode);
|
||||||
|
std::string full_cmd = command.construct_cmd(style) + " 2>&1" + (hasFlag(mode, cMode::Silent) ? " > /dev/null" : "");
|
||||||
|
int ret = system(full_cmd.c_str());
|
||||||
|
|
||||||
|
bool ok = EXITSTATUSCHECK(ret);
|
||||||
|
if (!ok) {
|
||||||
|
std::cerr << "Error: Failed to execute command: " << std::endl;
|
||||||
|
std::cerr << full_cmd << std::endl;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(!(hasFlag(mode, cMode::Interactive) && !is_raw(mode)), "Interactive mode requires raw command mode");
|
||||||
|
ASSERT(!(hasFlag(mode, cMode::CaptureOutput) && output == nullptr), "Capture output mode must be used with an output string");
|
||||||
|
|
||||||
|
std::stringstream ssh_cmd;
|
||||||
|
ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "")
|
||||||
|
<< ssh_info.user << "@" << ssh_info.host;
|
||||||
|
|
||||||
|
std::string cmdstr;
|
||||||
|
if (!is_raw(mode))
|
||||||
|
cmdstr = quote("bash -c " + command.construct_cmd(cStyle::Safe));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string raw_cmd = command.construct_cmd(cStyle::Raw);
|
||||||
|
ASSERT(raw_cmd.find("'") == std::string::npos, "Raw command must not contain single quotes");
|
||||||
|
cmdstr = "bash -c "+ halfquote(raw_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
sCommand ssh_command(ssh_cmd.str() + " " + cmdstr);
|
||||||
|
|
||||||
|
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
|
83
src/utils/execute.hpp
Normal file
83
src/utils/execute.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#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,
|
||||||
|
RawCommand = 8
|
||||||
|
};
|
||||||
|
enum class cStyle {
|
||||||
|
Safe = 0,
|
||||||
|
Raw = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
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));}
|
||||||
|
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));}
|
||||||
|
inline cMode operator|=(cMode & lhs, cMode rhs) {return lhs = lhs | rhs;}
|
||||||
|
inline bool hasFlag(cMode mode, cMode flag) {return (mode & flag) == flag;}
|
||||||
|
inline bool is_safe(cStyle style) { return style == cStyle::Safe; }
|
||||||
|
inline bool is_raw(cStyle style) { return style == cStyle::Raw; }
|
||||||
|
inline bool is_raw(cMode mode) { return hasFlag(mode, cMode::RawCommand); }
|
||||||
|
inline cStyle getStyle(cMode mode) { return is_raw(mode) ? cStyle::Raw : cStyle::Safe; }
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -312,32 +312,6 @@ std::string random_alphanumeric_string(int length)
|
|||||||
return random_string;
|
return random_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void make_shell_files_executable(const std::string &dir_path)
|
|
||||||
{ // recursively make all shell files in the directory executable
|
|
||||||
|
|
||||||
const auto desired_perms = std::filesystem::perms::owner_read | std::filesystem::perms::owner_exec |
|
|
||||||
std::filesystem::perms::group_read | std::filesystem::perms::group_exec |
|
|
||||||
std::filesystem::perms::others_read | std::filesystem::perms::others_exec;
|
|
||||||
|
|
||||||
for (const auto& entry : std::filesystem::directory_iterator(dir_path)) {
|
|
||||||
if (entry.path().extension() == ".sh") {
|
|
||||||
// check if permissions are already set
|
|
||||||
auto currentperms = std::filesystem::status(entry.path()).permissions();
|
|
||||||
if ((currentperms & desired_perms) == desired_perms) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set permissions
|
|
||||||
std::cout << "Setting executable permissions for " << entry.path() << std::endl;
|
|
||||||
|
|
||||||
std::filesystem::permissions(entry.path(), desired_perms, std::filesystem::perm_options::add);
|
|
||||||
}
|
|
||||||
else if (std::filesystem::is_directory(entry.path())) {
|
|
||||||
make_shell_files_executable(entry.path());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string requote(std::string str) {
|
std::string requote(std::string str) {
|
||||||
return quote(trim(dequote(trim(str))));
|
return quote(trim(dequote(trim(str))));
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,4 @@ std::string replace_with_environment_variables_like_bash(std::string str);
|
|||||||
|
|
||||||
std::string random_alphanumeric_string(int length);
|
std::string random_alphanumeric_string(int length);
|
||||||
|
|
||||||
void make_shell_files_executable(const std::string& dir_path);
|
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
113
templates/dropshell-agent/_allservicesstatus.sh
Normal file
113
templates/dropshell-agent/_allservicesstatus.sh
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script checks ALL services on the server and returns a status for each.
|
||||||
|
|
||||||
|
# Return format is simple ENV with the following format:
|
||||||
|
# SERVICE_NAME_HEALTH=healthy|unhealthy|unknown
|
||||||
|
# SERVICE_NAME_PORTS=port1,port2,port3
|
||||||
|
|
||||||
|
# Get all services on the server
|
||||||
|
SCRIPT_DIR="$(dirname "$0")"
|
||||||
|
|
||||||
|
# // DROPSHELL_DIR
|
||||||
|
# // |-- backups
|
||||||
|
# // |-- services
|
||||||
|
# // |-- service name
|
||||||
|
# // |-- config <-- this is passed as argument to all scripts
|
||||||
|
# // |-- service.env
|
||||||
|
# // |-- template
|
||||||
|
# // |-- (script files)
|
||||||
|
# // |-- config
|
||||||
|
# // |-- service.env
|
||||||
|
# // |-- (other config files for specific server&service)
|
||||||
|
|
||||||
|
CURRENT_OUTPUT=""
|
||||||
|
CURRENT_EXIT_CODE=0
|
||||||
|
|
||||||
|
load_dotenv(){
|
||||||
|
local file_path=$1
|
||||||
|
if [ -f "${file_path}" ]; then
|
||||||
|
source "${file_path}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_command() {
|
||||||
|
local service_path=$1
|
||||||
|
local command=$2
|
||||||
|
local capture_output=${3:-false} # default to false if not specified
|
||||||
|
|
||||||
|
# check if the command is a file
|
||||||
|
if [ ! -f "${service_path}/template/${command}.sh" ]; then
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run the command in a subshell to prevent environment changes
|
||||||
|
CURRENT_OUTPUT=$(
|
||||||
|
set -a
|
||||||
|
load_dotenv "${service_path}/template/_default.env"
|
||||||
|
load_dotenv "${service_path}/config/service.env"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# update the main variables.
|
||||||
|
export CONFIG_PATH="${service_path}/config"
|
||||||
|
# SERVER is correct
|
||||||
|
export SERVICE="${SERVICE_NAME}"
|
||||||
|
|
||||||
|
if [ "$capture_output" = "true" ]; then
|
||||||
|
# Capture and return output
|
||||||
|
bash "${service_path}/template/${command}.sh" 2>&1
|
||||||
|
else
|
||||||
|
# Run silently and return exit code
|
||||||
|
bash "${service_path}/template/${command}.sh" > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
CURRENT_EXIT_CODE=$?
|
||||||
|
}
|
||||||
|
|
||||||
|
function command_exists() {
|
||||||
|
local service_path=$1
|
||||||
|
local command=$2
|
||||||
|
if [ ! -f "${service_path}/template/${command}.sh" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all services on the server
|
||||||
|
SERVICES_PATH=$(realpath "${SCRIPT_DIR}/../../")
|
||||||
|
|
||||||
|
# Get all service names
|
||||||
|
SERVICE_NAMES=$(ls "${SERVICES_PATH}")
|
||||||
|
|
||||||
|
# Iterate over all service names
|
||||||
|
for SERVICE_NAME in ${SERVICE_NAMES}; do
|
||||||
|
|
||||||
|
SERVICE_PATH=$(realpath "${SERVICES_PATH}/${SERVICE_NAME}")
|
||||||
|
|
||||||
|
#--------------------------------
|
||||||
|
# Get the service health
|
||||||
|
if ! command_exists "${SERVICE_PATH}" "status"; then
|
||||||
|
SERVICE_HEALTH="unknown"
|
||||||
|
else
|
||||||
|
run_command "${SERVICE_PATH}" "status" "false"
|
||||||
|
if [ "${CURRENT_EXIT_CODE}" -eq 0 ]; then
|
||||||
|
SERVICE_HEALTH="healthy"
|
||||||
|
else
|
||||||
|
SERVICE_HEALTH="unhealthy"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#--------------------------------
|
||||||
|
# Get the service ports
|
||||||
|
if ! command_exists "${SERVICE_PATH}" "ports"; then
|
||||||
|
SERVICE_PORTS=""
|
||||||
|
else
|
||||||
|
run_command "${SERVICE_PATH}" "ports" "true"
|
||||||
|
SERVICE_PORTS="${CURRENT_OUTPUT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#--------------------------------
|
||||||
|
# return the health and ports
|
||||||
|
echo "${SERVICE_NAME}_HEALTH=${SERVICE_HEALTH}"
|
||||||
|
echo "${SERVICE_NAME}_PORTS=${SERVICE_PORTS}"
|
||||||
|
done
|
@ -1,9 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# This script contains the common code for the autocommands.
|
# This script contains the common code for the autocommands.
|
||||||
|
_check_required_env_vars "BACKUP_FILE" "TEMP_DIR"
|
||||||
|
|
||||||
MYID=$(id -u)
|
MYID=$(id -u)
|
||||||
MYGRP=$(id -g)
|
MYGRP=$(id -g)
|
||||||
|
|
||||||
|
BACKUP_TEMP_PATH="$TEMP_DIR/backup"
|
||||||
|
|
||||||
_autocommandrun_volume() {
|
_autocommandrun_volume() {
|
||||||
local command="$1"
|
local command="$1"
|
||||||
local volume_name="$2"
|
local volume_name="$2"
|
||||||
@ -108,8 +112,7 @@ _autocommandparse() {
|
|||||||
|
|
||||||
local command="$1"
|
local command="$1"
|
||||||
shift
|
shift
|
||||||
local backup_temp_path="$1"
|
echo "autocommandparse: command=$command"
|
||||||
shift
|
|
||||||
|
|
||||||
# Extract the backup file and temp path (last two arguments)
|
# Extract the backup file and temp path (last two arguments)
|
||||||
local args=("$@")
|
local args=("$@")
|
||||||
@ -129,7 +132,7 @@ _autocommandparse() {
|
|||||||
|
|
||||||
# create backup folder unique to key/value.
|
# create backup folder unique to key/value.
|
||||||
local bfolder=$(echo "${key}_${value}" | tr -cd '[:alnum:]_-')
|
local bfolder=$(echo "${key}_${value}" | tr -cd '[:alnum:]_-')
|
||||||
local targetpath="${backup_temp_path}/${bfolder}"
|
local targetpath="${BACKUP_TEMP_PATH}/${bfolder}"
|
||||||
mkdir -p ${targetpath}
|
mkdir -p ${targetpath}
|
||||||
|
|
||||||
# Key must be one of volume, path or file
|
# Key must be one of volume, path or file
|
||||||
@ -152,57 +155,31 @@ _autocommandparse() {
|
|||||||
|
|
||||||
|
|
||||||
autocreate() {
|
autocreate() {
|
||||||
_check_required_env_vars "TEMP_DIR"
|
_autocommandparse create "$@"
|
||||||
|
|
||||||
local create_temp_path="$TEMP_DIR/create"
|
|
||||||
mkdir -p "$create_temp_path"
|
|
||||||
_autocommandparse create "$create_temp_path" "$@"
|
|
||||||
rm -rf "$create_temp_path"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autonuke() {
|
autonuke() {
|
||||||
_check_required_env_vars "TEMP_DIR"
|
_autocommandparse nuke "$@"
|
||||||
|
|
||||||
local nuke_temp_path="$TEMP_DIR/nuke"
|
|
||||||
mkdir -p "$nuke_temp_path"
|
|
||||||
_autocommandparse nuke "$nuke_temp_path" "$@"
|
|
||||||
rm -rf "$nuke_temp_path"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autobackup() {
|
autobackup() {
|
||||||
_check_required_env_vars "BACKUP_FILE" "TEMP_DIR"
|
mkdir -p "$BACKUP_TEMP_PATH"
|
||||||
local backup_temp_path="$TEMP_DIR/backup"
|
echo "_autocommandparse [backup] [$@]"
|
||||||
|
_autocommandparse backup "$@"
|
||||||
|
|
||||||
mkdir -p "$backup_temp_path"
|
tar zcvf "$BACKUP_FILE" -C "$BACKUP_TEMP_PATH" .
|
||||||
echo "_autocommandparse [backup] [$backup_temp_path] [$@]"
|
|
||||||
|
|
||||||
# backup the files into the temp path.
|
|
||||||
_autocommandparse backup "$backup_temp_path" "$@"
|
|
||||||
|
|
||||||
# create the backup file.
|
|
||||||
tar zcvf "$BACKUP_FILE" -C "$backup_temp_path" .
|
|
||||||
|
|
||||||
rm -rf "$backup_temp_path"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autorestore() {
|
autorestore() {
|
||||||
_check_required_env_vars "BACKUP_FILE" "TEMP_DIR"
|
echo "_autocommandparse [restore] [$@]"
|
||||||
local backup_temp_path="$TEMP_DIR/restore"
|
|
||||||
|
|
||||||
echo "_autocommandparse [restore] [$backup_temp_path] [$@]"
|
mkdir -p "$BACKUP_TEMP_PATH"
|
||||||
|
tar zxvf "$BACKUP_FILE" -C "$BACKUP_TEMP_PATH" --strip-components=1
|
||||||
|
|
||||||
mkdir -p "$backup_temp_path"
|
_autocommandparse restore "$@"
|
||||||
|
|
||||||
# extract the backup file into the temp path.
|
|
||||||
tar zxvf "$BACKUP_FILE" -C "$backup_temp_path" --strip-components=1
|
|
||||||
|
|
||||||
# restore the files from the temp path.
|
|
||||||
_autocommandparse restore "$backup_temp_path" "$@"
|
|
||||||
|
|
||||||
rm -rf "$backup_temp_path"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user