Compare commits

...

8 Commits

Author SHA1 Message Date
d0152c3ec7 dropshell release 2025.0518.1150
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-18 11:50:23 +12:00
ebb101e381 dropshell release 2025.0518.1145
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-18 11:45:37 +12:00
dc2f694ebe .
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 23:12:43 +12:00
038d08e638 dropshell release 2025.0517.2231
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 22:31:29 +12:00
27f86e95e7 .
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 22:24:25 +12:00
891f0d023f Self-test.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 20:41:17 +12:00
91f706ffcd dropshell release 2025.0517.2027
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 20:27:46 +12:00
0e1ac9ddd8 dropshell release DEV
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 20:21:31 +12:00
17 changed files with 386 additions and 55 deletions

View File

@ -8,6 +8,8 @@ A system management tool for server operations, written in C++.
curl -fsSL https://gitea.jde.nz/public/dropshell/releases/download/latest/install.sh | sudo bash curl -fsSL https://gitea.jde.nz/public/dropshell/releases/download/latest/install.sh | sudo bash
``` ```
This installs as dropshell, with a symlink ds if the ds command does not already exist.
## Installation of Agent ## Installation of Agent
Install the Agent on each server you wish to manage. Supports amd64 (x86_64) and arm64 (aarch64) architectures. Install the Agent on each server you wish to manage. Supports amd64 (x86_64) and arm64 (aarch64) architectures.
@ -24,3 +26,7 @@ Manual steps:
1. Test ssh'ing into the server. 1. Test ssh'ing into the server.
## Install Services
Set up a server and install a service:
1. `ds create-server SERVERNAME`

View File

@ -51,7 +51,7 @@ add_dependencies(dropshell run_createagent)
# Set include directories # Set include directories
# build dir goes first so that we can use the generated version.hpp # build dir goes first so that we can use the generated version.hpp
target_include_directories(dropshell PRIVATE target_include_directories(dropshell PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src> $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src/autogen>
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/utils ${CMAKE_CURRENT_SOURCE_DIR}/src/utils
${CMAKE_CURRENT_SOURCE_DIR}/src/contrib ${CMAKE_CURRENT_SOURCE_DIR}/src/contrib

7
source/agent/selftest.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
echo "Running remote agent self-test..."
echo "Completed remote agent self-test."
exit 0

View File

@ -6,7 +6,9 @@
set -e set -e
rm -f build_amd64/dropshell build_arm64/dropshell build/dropshell.amd64 build/dropshell.arm64 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
rm -f $SCRIPT_DIR/build_amd64/dropshell $SCRIPT_DIR/build_arm64/dropshell $SCRIPT_DIR/output/dropshell.amd64 $SCRIPT_DIR/output/dropshell.arm64
# Determine number of CPU cores for parallel build # Determine number of CPU cores for parallel build
if command -v nproc >/dev/null 2>&1; then if command -v nproc >/dev/null 2>&1; then
@ -15,6 +17,9 @@ else
JOBS=4 # fallback default JOBS=4 # fallback default
fi fi
PREV_PWD=$PWD
cd $SCRIPT_DIR
# Build for amd64 (musl) # Build for amd64 (musl)
echo "Building for amd64 (musl)..." echo "Building for amd64 (musl)..."
cmake -B build_amd64 -DCMAKE_BUILD_TYPE=Release \ cmake -B build_amd64 -DCMAKE_BUILD_TYPE=Release \
@ -23,8 +28,8 @@ cmake -B build_amd64 -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXE_LINKER_FLAGS="-static" \ -DCMAKE_EXE_LINKER_FLAGS="-static" \
-DCMAKE_CXX_FLAGS="-march=x86-64" . -DCMAKE_CXX_FLAGS="-march=x86-64" .
cmake --build build_amd64 --target dropshell --config Release -j"$JOBS" cmake --build build_amd64 --target dropshell --config Release -j"$JOBS"
mkdir -p build mkdir -p output
cp build_amd64/dropshell build/dropshell.amd64 cp build_amd64/dropshell output/dropshell.amd64
# Build for arm64 (musl) # Build for arm64 (musl)
echo "Building for arm64 (musl)..." echo "Building for arm64 (musl)..."
@ -35,18 +40,20 @@ cmake -B build_arm64 -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="-march=armv8-a" \ -DCMAKE_CXX_FLAGS="-march=armv8-a" \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 . -DCMAKE_SYSTEM_PROCESSOR=aarch64 .
cmake --build build_arm64 --target dropshell --config Release -j"$JOBS" cmake --build build_arm64 --target dropshell --config Release -j"$JOBS"
mkdir -p build mkdir -p output
cp build_arm64/dropshell build/dropshell.arm64 cp build_arm64/dropshell output/dropshell.arm64
if [ ! -f build/dropshell.amd64 ]; then if [ ! -f output/dropshell.amd64 ]; then
echo "build/dropshell.amd64 not found!" >&2 echo "output/dropshell.amd64 not found!" >&2
exit 1 exit 1
fi fi
if [ ! -f build/dropshell.arm64 ]; then if [ ! -f output/dropshell.arm64 ]; then
echo "build/dropshell.arm64 not found!" >&2 echo "output/dropshell.arm64 not found!" >&2
exit 1 exit 1
fi fi
echo "Builds complete:" echo "Builds complete:"
ls -lh build/dropshell.* ls -lh output/dropshell.*
cd $PREV_PWD

View File

@ -1,6 +1,10 @@
#!/bin/bash #!/bin/bash
set -e set -e
# directory of this script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "Script directory: $SCRIPT_DIR"
# Check for GITEA_TOKEN_DEPLOY or GITEA_TOKEN # Check for GITEA_TOKEN_DEPLOY or GITEA_TOKEN
if [ -n "$GITEA_TOKEN_DEPLOY" ]; then if [ -n "$GITEA_TOKEN_DEPLOY" ]; then
TOKEN="$GITEA_TOKEN_DEPLOY" TOKEN="$GITEA_TOKEN_DEPLOY"
@ -11,27 +15,32 @@ else
exit 1 exit 1
fi fi
$SCRIPT_DIR/multibuild.sh
BUILD_DIR=$SCRIPT_DIR/build
./multibuild.sh OLD_PWD=$PWD
cd $SCRIPT_DIR
if [ ! -f "build/dropshell.amd64" ]; then
echo "build/dropshell.amd64 not found!" >&2 if [ ! -f "output/dropshell.amd64" ]; then
echo "output/dropshell.amd64 not found!" >&2
echo "Please run multibuild.sh first." >&2 echo "Please run multibuild.sh first." >&2
exit 1 exit 1
fi fi
if [ ! -f "build/dropshell.arm64" ]; then if [ ! -f "output/dropshell.arm64" ]; then
echo "build/dropshell.arm64 not found!" >&2 echo "output/dropshell.arm64 not found!" >&2
echo "Please run multibuild.sh first." >&2 echo "Please run multibuild.sh first." >&2
exit 1 exit 1
fi fi
TAG=$(./build/dropshell.amd64 --version) TAG=$("$SCRIPT_DIR/output/dropshell.amd64" --version)
[ -z "$TAG" ] && echo "Failed to get version from dropshell.amd64" >&2 && exit 1
echo "Publishing dropshell version $TAG" echo "Publishing dropshell version $TAG"
# make sure we've commited. # make sure we've commited.
git add . && git commit -m "dropshell release $TAG" && git push git add "$SCRIPT_DIR/../" && git commit -m "dropshell release $TAG" && git push
# Find repo info from .git/config # Find repo info from .git/config
@ -73,8 +82,10 @@ fi
# Upload binaries and install.sh # Upload binaries and install.sh
for FILE in dropshell.amd64 dropshell.arm64 install.sh server_autosetup.sh; do for FILE in dropshell.amd64 dropshell.arm64 install.sh server_autosetup.sh; do
if [ -f "build/$FILE" ]; then if [ -f "output/$FILE" ]; then
filetoupload="build/$FILE" filetoupload="output/$FILE"
elif [ -f "../$FILE" ]; then
filetoupload="../$FILE"
elif [ -f "$FILE" ]; then elif [ -f "$FILE" ]; then
filetoupload="$FILE" filetoupload="$FILE"
else else
@ -93,3 +104,5 @@ for FILE in dropshell.amd64 dropshell.arm64 install.sh server_autosetup.sh; do
done done
echo "Published dropshell version $TAG to $REPO_URL (tag $TAG) with binaries." echo "Published dropshell version $TAG to $REPO_URL (tag $TAG) with binaries."
cd $OLD_PWD

View File

@ -105,6 +105,22 @@ static bool _recreate_file_(const std::filesystem::path& outpath, uint64_t file_
bool recreate_tree(std::string destination_folder) { bool recreate_tree(std::string destination_folder) {
namespace fs = std::filesystem; namespace fs = std::filesystem;
bool any_written = false; bool any_written = false;
{
// File: selftest.sh
fs::path outpath = fs::path(destination_folder) / "selftest.sh";
static const char filedata_base64[] = "IyEvYmluL2Jhc2gKCmVjaG8gIlJ1bm5pbmcgcmVtb3RlIGFnZW50IHNlbGYtdGVzdC4uLiIKCmVj"\
"aG8gIkNvbXBsZXRlZCByZW1vdGUgYWdlbnQgc2VsZi10ZXN0LiIKCmV4aXQgMAo=";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 11594895391899191874ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
{ {
// File: datacommands.sh // File: datacommands.sh
fs::path outpath = fs::path(destination_folder) / "datacommands.sh"; fs::path outpath = fs::path(destination_folder) / "datacommands.sh";

View File

@ -1,15 +1,11 @@
// version.hpp (dummy for linter/IntelliSense)
#pragma once #pragma once
// DUMMY VERSION - replaced by build process.
#include <string> #include <string>
namespace dropshell { namespace dropshell {
extern const std::string VERSION;
// Version information extern const std::string RELEASE_DATE;
const std::string VERSION = "DEV"; extern const std::string AUTHOR;
const std::string RELEASE_DATE = "NEVER"; extern const std::string LICENSE;
const std::string AUTHOR = "j842"; }
const std::string LICENSE = "MIT";
} // namespace dropshell

View File

@ -17,9 +17,9 @@ namespace dropshell
static std::vector<std::string> create_service_name_list = {"create-service"}; static std::vector<std::string> create_service_name_list = {"create-service"};
// Static registration // Static registration
struct UninstallCommandRegister struct CreateServiceCommandRegister
{ {
UninstallCommandRegister() CreateServiceCommandRegister()
{ {
CommandRegistry::instance().register_command({create_service_name_list, CommandRegistry::instance().register_command({create_service_name_list,
create_service_handler, create_service_handler,

View File

@ -57,10 +57,13 @@ void help_autocomplete(const CommandContext& ctx) {
void show_command(const std::string& cmd) { void show_command(const std::string& cmd) {
const auto& cmd_info = CommandRegistry::instance().find_command(cmd); const auto& cmd_info = CommandRegistry::instance().find_command(cmd);
if (!cmd_info) if (!cmd_info)
{
std::cout << "Unknown command: " << cmd << std::endl; std::cout << "Unknown command: " << cmd << std::endl;
return;
}
std::cout << " "; std::cout << " ";
print_left_aligned(cmd_info->help_usage, 30); print_left_aligned(cmd_info->help_usage, 32);
std::cout << cmd_info->help_description << std::endl; std::cout << cmd_info->help_description << std::endl;
} }
@ -119,6 +122,13 @@ int help_handler(const CommandContext& ctx) {
{ {
// show more! // show more!
show_command("list"); show_command("list");
std::cout << std::endl;
show_command("install");
show_command("uninstall");
show_command("nuke");
std::cout << std::endl;
show_command("start");
show_command("stop");
} }
return 0; return 0;
} }

View File

@ -35,8 +35,8 @@ namespace dropshell
false, // requires_install false, // requires_install
0, // min_args (after command) 0, // min_args (after command)
2, // max_args (after command) 2, // max_args (after command)
"install SERVER [SERVICE|all]", "install [SERVER] [SERVICE|all]",
"Install/reinstall service(s). Safe/non-destructive way to update.", "Install/reinstall host, remote servers, or service(s). Safe/non-destructive way to update.",
// heredoc // heredoc
R"( R"(
Install components on a server. This is safe to re-run (non-destructive) and used to update Install components on a server. This is safe to re-run (non-destructive) and used to update
@ -121,6 +121,7 @@ namespace dropshell
// Run install script // Run install script
{ {
std::cout << "Running " << service_info.template_name << " install script on " << server << "..." << std::endl;
server_env.run_remote_template_command(service, "install", {}, silent, {}); server_env.run_remote_template_command(service, "install", {}, silent, {});
} }
@ -298,10 +299,12 @@ namespace dropshell
// now create the agent. // now create the agent.
// copy across from the local agent files. // copy across from the local agent files.
std::cout << "Copying local agent files to remote server... " << std::flush;
shared_commands::rsync_tree_to_remote(localpath::files_for_remote_agent(), agent_path, server_env, false); shared_commands::rsync_tree_to_remote(localpath::files_for_remote_agent(), agent_path, server_env, false);
std::cout << "done." << std::endl;
// add in bb64. We can't use execute_remote_command() here, as that relies on bb64 which we're installing! // add in bb64. We can't use execute_remote_command() here, as that relies on bb64 which we're installing!
std::cout << "Installing bb64 on " << server << std::endl << std::flush; std::cout << "Installing bb64 on " << server << "..." << std::endl << std::flush;
std::string remote_cmd = std::string remote_cmd =
"ssh -p " + server_env.get_SSH_INFO().port + " " + server_env.get_SSH_INFO().user + "@" + server_env.get_SSH_INFO().host + "ssh -p " + server_env.get_SSH_INFO().port + " " + server_env.get_SSH_INFO().user + "@" + server_env.get_SSH_INFO().host +
@ -315,11 +318,14 @@ namespace dropshell
std::cout << "Downloaded bb64 to " << agent_path << " on remote server." << std::endl; std::cout << "Downloaded bb64 to " << agent_path << " on remote server." << std::endl;
// just test all is ok // just test all is ok
// run the self-test.
std::string output; std::string output;
bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "./bb64 -i VGhlIGRyb3BzaGVsbCByZW1vdGUgYWdlbnQgaXMgY29ycmVjdGx5IGluc3RhbGxlZC4=", {}), cMode::CaptureOutput, &output); bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "./selftest.sh", {}), cMode::Defaults, &output);
if (!okay) if (!okay)
{ {
std::cerr << "Failed to install bb64 on " << server << std::endl; std::cerr << "ERROR: Failed to install remote agent on " << server << std::endl;
std::cerr << "ERROR: Output: " << output << std::endl;
return 1; return 1;
} }

View File

@ -7,6 +7,7 @@
#include "tableprint.hpp" #include "tableprint.hpp"
#include "transwarp.hpp" #include "transwarp.hpp"
#include "server_env_manager.hpp" #include "server_env_manager.hpp"
#include "services.hpp"
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
@ -161,7 +162,7 @@ void show_server_details(const std::string& server_name) {
// list services, and run healthcheck on each // list services, and run healthcheck on each
{ {
tableprint tp("Services: " + server_name, false); tableprint tp("Services: " + server_name, false);
tp.add_row({"Status", "Service", "Ports"}); tp.add_row({"Status", "Service", "Template","Ports"});
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server_name); std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server_name);
@ -175,7 +176,7 @@ void show_server_details(const std::string& server_name) {
for (const auto& port : service_status.ports) for (const auto& port : service_status.ports)
ports_str += std::to_string(port) + " "; ports_str += std::to_string(port) + " ";
tp.add_row({healthy, service_name, ports_str}); tp.add_row({healthy, service_name, get_service_info(server_name,service_name).template_name, ports_str});
} // end of for (const auto& service : services) } // end of for (const auto& service : services)
tp.print(); tp.print();
} // end of list services } // end of list services

View File

@ -10,8 +10,6 @@
#include "utils/assert.hpp" #include "utils/assert.hpp"
#pragma message ("TODO: Fix issues with Nuke below.")
namespace dropshell { namespace dropshell {
int nuke_handler(const CommandContext& ctx); int nuke_handler(const CommandContext& ctx);
@ -33,9 +31,16 @@ struct NukeCommandRegister {
"Nuke a service on a server. Destroys everything, both local and remote!", "Nuke a service on a server. Destroys everything, both local and remote!",
// heredoc // heredoc
R"( R"(
Nuke a service on a server. Destroys everything, both local and remote! Nuke a service.
Examples:
nuke SERVER SERVICE nuke the given service on the given server. nuke SERVER SERVICE nuke the given service on the given server.
nuke SERVER all nuke all services on the given server. nuke SERVER all nuke all services on the given server.
Note: This command is destructive and will destroy all data and all configuration,
both on the dropshell host and on the remote server.
Use with caution!
)" )"
}); });
} }
@ -60,11 +65,14 @@ int nuke_one(std::string server, std::string service)
// otherwise just uninstall. // otherwise just uninstall.
if (gTemplateManager().template_command_exists(service_info.template_name, "nuke")) if (gTemplateManager().template_command_exists(service_info.template_name, "nuke"))
{ {
std::cout << "Running nuke script for " << service << " on " << server << std::endl;
if (!server_env.run_remote_template_command(service, "nuke", {}, false, {})) if (!server_env.run_remote_template_command(service, "nuke", {}, false, {}))
std::cerr << "Warning: Failed to run nuke script: " << service << std::endl; std::cerr << "Warning: Failed to run nuke script: " << service << std::endl;
} }
else else
{ {
std::cout << "No nuke script found for " << service << " on " << server << std::endl;
std::cout << "Running uninstall script instead and will clean directories." << std::endl;
if (!server_env.run_remote_template_command(service, "uninstall", {}, false, {})) if (!server_env.run_remote_template_command(service, "uninstall", {}, false, {}))
std::cerr << "Warning: Failed to uninstall service: " << service << std::endl; std::cerr << "Warning: Failed to uninstall service: " << service << std::endl;
} }

View File

@ -0,0 +1,81 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int ssh_handler(const CommandContext &ctx);
static std::vector<std::string> ssh_name_list = {"ssh"};
// Static registration
struct SSHCommandRegister
{
SSHCommandRegister()
{
CommandRegistry::instance().register_command({ssh_name_list,
ssh_handler,
shared_commands::std_autocomplete,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"ssh SERVER",
"SSH into a server, or into a docker container for a service.",
R"(
ssh SERVER SERVICE SSH into a docker container for a service.
ssh SERVER SSH into a server.
)"});
}
} ssh_command_register;
bool ssh_into_server(const std::string &server)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
execute_ssh_command(server_env.get_SSH_INFO(), sCommand(remotepath::DROPSHELL_DIR(server), "ls --color && bash", {}), cMode::Interactive);
return true;
}
bool ssh_into_service(const std::string &server, const std::string &service)
{
return true;
}
int ssh_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 1)
{
std::cerr << "Error: Server name is required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
if (ctx.args.size() < 2)
{
// ssh into the server
return ssh_into_server(server) ? 0 : 1;
}
std::string service = safearg(ctx.args, 1);
// ssh into the specific service.
return ssh_into_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -0,0 +1,91 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int start_handler(const CommandContext &ctx);
static std::vector<std::string> start_name_list = {"start", "start-service"};
// Static registration
struct StartCommandRegister
{
StartCommandRegister()
{
CommandRegistry::instance().register_command({start_name_list,
start_handler,
shared_commands::std_autocomplete_allowall,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"start SERVER SERVICE|all",
"Start a service or all services on a server.",
R"(
start SERVER SERVICE Starts the given service on the given server.
start SERVER all Starts all services on the given server.
Note: This command will not create any data or configuration.
It will simply start the service on the remote server.
Stop the service with stop, or uninstall with uninstall.
)"});
}
} start_command_register;
bool start_service(const std::string &server, const std::string &service)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
// run the start script.
bool started = server_env.run_remote_template_command(service, "start", {}, false, {});
if (started)
{
std::cout << "Service " << service << " on server " << server << " started." << std::endl;
return true;
}
std::cerr << "Error: Failed to start service " << service << " on server " << server << std::endl;
return false;
}
int start_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 2)
{
std::cerr << "Error: Server name and service name are both required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
std::string service = safearg(ctx.args, 1);
if (service == "all")
{
// install all services on the server
maketitle("Stopping all services on " + server);
bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto &service : services)
okay &= start_service(server, service.service_name);
return okay ? 0 : 1;
}
// start the specific service.
return start_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -0,0 +1,91 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int stop_handler(const CommandContext &ctx);
static std::vector<std::string> stop_name_list = {"stop", "stop-service"};
// Static registration
struct StopCommandRegister
{
StopCommandRegister()
{
CommandRegistry::instance().register_command({stop_name_list,
stop_handler,
shared_commands::std_autocomplete_allowall,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"stop SERVER SERVICE|all",
"Stop a service or all services on a server.",
R"(
stop SERVER SERVICE Stops the given service on the given server.
stop SERVER all Stops all services on the given server.
Note: This command will not destroy any data or configuration.
It will simply stop the service on the remote server.
Restart the service with start, or update and start it with install.
)"});
}
} stop_command_register;
bool stop_service(const std::string &server, const std::string &service)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
// run the stop script.
bool stopped = server_env.run_remote_template_command(service, "stop", {}, false, {});
if (stopped)
{
std::cout << "Service " << service << " on server " << server << " stopped." << std::endl;
return true;
}
std::cerr << "Error: Failed to stop service " << service << " on server " << server << std::endl;
return false;
}
int stop_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 2)
{
std::cerr << "Error: Server name and service name are both required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
std::string service = safearg(ctx.args, 1);
if (service == "all")
{
// install all services on the server
maketitle("Stopping all services on " + server);
bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto &service : services)
okay &= stop_service(server, service.service_name);
return okay ? 0 : 1;
}
// stop the specific service.
return stop_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -31,17 +31,17 @@ namespace dropshell
"Uninstall a service on a server. Does not remove configuration or user data.", "Uninstall a service on a server. Does not remove configuration or user data.",
// heredoc // heredoc
R"( R"(
Uninstall a service on a server. Does not remove configuration or user data. Uninstall a service, leaving all configuration and data intact.
uninstall SERVER SERVICE uninstall the given service on the given server.
uninstall SERVER all uninstall all services on the given server. uninstall SERVER SERVICE Uninstall the given service on the given server.
uninstall SERVER all Uninstall all services on the given server.
Update and reinstall the service with install, or delete all configuration and data with nuke.
)"}); )"});
} }
} uninstall_command_register; } uninstall_command_register;
bool uninstall_service(const std::string &server, const std::string &service, bool silent = false)
bool uninstall_service(const std::string &server, const std::string &service, bool silent=false)
{ {
if (!silent) if (!silent)
maketitle("Uninstalling " + service + " on " + server); maketitle("Uninstalling " + service + " on " + server);
@ -81,7 +81,6 @@ namespace dropshell
return true; return true;
} }
int uninstall_handler(const CommandContext &ctx) int uninstall_handler(const CommandContext &ctx)
{ {
if (ctx.args.size() < 1) if (ctx.args.size() < 1)
@ -109,5 +108,4 @@ namespace dropshell
return uninstall_service(server, service) ? 0 : 1; return uninstall_service(server, service) ? 0 : 1;
} }
} // namespace dropshell } // namespace dropshell

View File

@ -204,7 +204,7 @@ bool server_env_manager::run_remote_template_command(const std::string &service_
if (scommand->get_command_to_run().empty()) if (scommand->get_command_to_run().empty())
return false; return false;
cMode mode = (command=="ssh") ? (cMode::Interactive) : cMode::Silent; cMode mode = (command=="ssh") ? (cMode::Interactive) : (silent ? cMode::Silent : cMode::Defaults);
return execute_ssh_command(get_SSH_INFO(), scommand.value(), mode); return execute_ssh_command(get_SSH_INFO(), scommand.value(), mode);
} }