From ce5a64a4c71263f3b0ed7fd71e16254bd0d59060 Mon Sep 17 00:00:00 2001 From: Your Name <j@842.be> Date: Mon, 5 May 2025 22:53:41 +1200 Subject: [PATCH] . --- src/main.cpp | 74 ++++++++++--------- src/service_runner.cpp | 37 ++++++---- src/service_runner.hpp | 3 +- .../dropshell-agent/shared/_autocommands.sh | 25 +++++-- 4 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5c1711e..03c53f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,14 +40,17 @@ bool print_help() { std::cout << std::endl; std::cout << std::endl; std::cout << "Service commands: (if no service is specified, all services for the server are affected)" << std::endl; - std::cout << " install SERVER [SERVICE] Install/reinstall/update service(s). Non-destructive." << std::endl; std::cout << " list [SERVER] [SERVICE] List status/details of all servers/server/service." << std::endl; std::cout << " edit [SERVER] [SERVICE] Edit the configuration of dropshell/server/service." << std::endl; - std::cout << " COMMAND SERVER [SERVICE] Run a command on service(s)." << std::endl; std::cout << std::endl; - std::cout << "Standard commands: install, uninstall, backup, restore, start, stop" << std::endl; + std::cout << " install SERVER [SERVICE] Install/reinstall/update service(s). Safe/non-destructive." << std::endl; + std::cout << " uninstall SERVER [SERVICE] Uninstalls the service on the remote server. Leaves data intact." << std::endl; + std::cout << " nuke SERVER SERVICE Nuke the service on the remote server, deleting all remote data." << std::endl; std::cout << std::endl; - std::cout << " ssh SERVER [SERVICE] Launch an interactive shell on a server or service" << std::endl; + std::cout << " COMMAND SERVER [SERVICE] Run a command on service(s), e.g." << std::endl; + std::cout << " backup, restore, start, stop, logs" << std::endl; + std::cout << std::endl; + std::cout << " ssh SERVER SERVICE Launch an interactive shell on a server or service" << std::endl; std::cout << std::endl; std::cout << "Creation commands: (apply to the first local config directory)"<<std::endl; std::cout << " create-template TEMPLATE" << std::endl; @@ -116,18 +119,17 @@ int main(int argc, char* argv[]) { if (gConfig().is_config_set()) gTemplateManager().load_sources(); - if (argc < 2) { - print_help(); - return 0; - } + if (argc < 2) + return print_help() ? 0 : 1; + std::string cmd = argv[1]; - std::vector<std::string> argvec; - for (int i=0; i<argc; i++) - argvec.push_back(argv[i]); - - if (cmd == "autocomplete") + if (cmd == "autocomplete") { + std::vector<std::string> argvec; + for (int i=0; i<argc; i++) + argvec.push_back(argv[i]); return autocomplete(argvec) ? 0 : 1; + } if (cmd == "help" || cmd == "-h" || cmd == "--help" || cmd== "h" || cmd=="halp") return print_help() ? 0 : 1; @@ -216,30 +218,32 @@ int main(int argc, char* argv[]) { std::set<std::string> commands; get_all_used_commands(commands); commands.merge(std::set<std::string>{"ssh","edit","_allservicesstatus"}); // handled by service_runner, but not in template_shell_commands. - for (const auto& command : commands) { - if (cmd == command) { - ServerAndServices server_and_services; - if (!getCLIServices(safearg(argc, argv, 2), safearg(argc, argv, 3), server_and_services)) { - std::cerr << "Error: " << command << " command requires server name and optionally service name" << std::endl; - return 1; - } - for (const auto& service_info : server_and_services.servicelist) { - service_runner runner(server_and_services.server_name, service_info.service_name); - if (!runner.isValid()) { - std::cerr << "Error: Failed to initialize service" << std::endl; - return 1; - } - std::vector<std::string> additional_args; - for (int i=4; i<argc; i++) - additional_args.push_back(argv[i]); - if (!runner.run_command(command, additional_args)) { - std::cerr << command +" failed." << std::endl; - return 1; - } - } - return 0; + if (commands.count(cmd)) { + std::set<std::string> safe_commands = {"nuke", "fullnuke"}; + if (safe_commands.count(cmd) && argc < 4) + return die("Error: "+cmd+" requires a server name and service name. For safety, can't run on all services."); + + // get all the services to run the command on. + ServerAndServices server_and_services; + if (!getCLIServices(safearg(argc, argv, 2), safearg(argc, argv, 3), server_and_services)) + return die("Error: "+cmd+" command requires server name and optionally service name"); + + // run the command on each service. + for (const auto& service_info : server_and_services.servicelist) { + service_runner runner(server_and_services.server_name, service_info.service_name); + if (!runner.isValid()) + return die("Error: Failed to initialize service"); + + std::vector<std::string> additional_args; + for (int i=4; i<argc; i++) + additional_args.push_back(argv[i]); + if (!runner.run_command(cmd, additional_args)) + return die(cmd+" failed."); } + + // success! + return 0; } // Unknown command diff --git a/src/service_runner.cpp b/src/service_runner.cpp index f9f5bba..e8b322f 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -167,24 +167,33 @@ bool service_runner::nuke() } std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl; - std::cout << "Now deleteing local files..." << std::endl; - std::string local_service_path = localpath::service(mServer,mService); - if (local_service_path.empty() || !fs::exists(local_service_path)) { - std::cerr << "Error: Service directory not found: " << local_service_path << std::endl; - } - else - { - std::string rm_cmd = "rm -rf " + quote(local_service_path); - if (!mServerEnv.execute_local_command(rm_cmd)) { - std::cerr << "Failed to remove service directory" << std::endl; - return false; - } - } - std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl; + + std::cout << "There's nothing left on the remote server." << std::endl; + std::cout << "You can remove the local files with:" << std::endl; + std::cout << " rm -rf " << localpath::service(mServer,mService) << std::endl; + return true; } +bool service_runner::fullnuke() +{ + if (!mServerEnv.is_valid()) return false; // should never hit this. + + std::string local_service_path = localpath::service(mServer,mService); + if (local_service_path.empty() || !fs::exists(local_service_path)) { + std::cerr << "Error: Service directory not found: " << local_service_path << std::endl; + return false; + } + + std::string rm_cmd = "rm -rf " + quote(local_service_path); + if (!mServerEnv.execute_local_command(rm_cmd)) { + std::cerr << "Failed to remove service directory" << std::endl; + return false; + } + + return true; +} // ------------------------------------------------------------------------------------------------ diff --git a/src/service_runner.hpp b/src/service_runner.hpp index 6923936..412253f 100644 --- a/src/service_runner.hpp +++ b/src/service_runner.hpp @@ -78,7 +78,8 @@ class service_runner { bool restore(std::string backup_file, bool silent=false); // nuke the service - bool nuke(); + bool nuke(); // nukes all data for this service on the remote server + bool fullnuke(); // nuke all data for this service on the remote server, and then nukes all the local service definitionfiles // launch an interactive ssh session on a server or service // replaces the current dropshell process with the ssh process diff --git a/templates/dropshell-agent/shared/_autocommands.sh b/templates/dropshell-agent/shared/_autocommands.sh index ff19ba3..85dccdf 100644 --- a/templates/dropshell-agent/shared/_autocommands.sh +++ b/templates/dropshell-agent/shared/_autocommands.sh @@ -42,12 +42,22 @@ _autocommandrun_path() { ;; nuke) echo "Nuking path ${path}" - rm -rf ${path} + PATHPARENT=$(dirname ${path}) + PATHCHILD=$(basename ${path}) + if [ -d "${PATHPARENT}/${PATHCHILD}" ]; then + docker run --rm -v ${PATHPARENT}:/volume debian bash -c "rm -rf /volume/${PATHCHILD}" + else + echo "Path ${path} does not exist - nothing to nuke" + fi ;; backup) local backup_folder="$3" echo "Backing up path ${path}" - tar -czvf ${backup_folder}/backup.tgz -C ${path} . + if [ -d "${path}" ]; then + docker run --rm -v ${path}:/path -v ${backup_folder}:/backup debian bash -c "tar -czvf /backup/backup.tgz -C /path . && chown -R $MYID:$MYGRP /backup" + else + echo "Path ${path} does not exist - nothing to backup" + fi ;; restore) local backup_folder="$3" @@ -70,9 +80,14 @@ _autocommandrun_file() { backup) local backup_folder="$3" echo "Backing up file ${value}" - # get filename from path - local filename=$(basename ${value}) - cp ${value} ${backup_folder}/${filename} + + FILEPARENT=$(dirname ${value}) + FILENAME=$(basename ${value}) + if [ -f "${FILEPARENT}/${FILENAME}" ]; then + docker run --rm-v ${FILEPARENT}:/volume -v ${backup_folder}:/backup debian bash -c "cp /volume/${FILENAME} /backup/${FILENAME} && chown -R $MYID:$MYGRP /backup" + else + echo "File ${value} does not exist - nothing to backup" + fi ;; restore) local backup_folder="$3"