262 lines
8.8 KiB
C++
262 lines
8.8 KiB
C++
#include "version.hpp"
|
|
#include "config.hpp"
|
|
#include "service_runner.hpp"
|
|
#include "services.hpp"
|
|
#include "servers.hpp"
|
|
#include "utils/directories.hpp"
|
|
#include "templates.hpp"
|
|
#include "utils/utils.hpp"
|
|
#include "autocomplete.hpp"
|
|
#include "utils/hash.hpp"
|
|
#include "command_registry.hpp"
|
|
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iomanip>
|
|
#include <chrono>
|
|
#include <assert.hpp>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
namespace dropshell {
|
|
|
|
extern const std::string VERSION;
|
|
extern const std::string RELEASE_DATE;
|
|
extern const std::string AUTHOR;
|
|
extern const std::string LICENSE;
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
|
|
|
try {
|
|
// silently attempt to load the config file and templates.
|
|
gConfig().load_config();
|
|
if (gConfig().is_config_set())
|
|
gTemplateManager().load_sources();
|
|
|
|
|
|
// process the command line arguments.
|
|
std::vector<std::string> args(argv, argv + argc);
|
|
|
|
if (args.size() < 2)
|
|
args.push_back("help");
|
|
ASSERT(args.size() > 1, "No command provided, logic error.");
|
|
|
|
CommandContext ctx{args[0], args[1], std::vector<std::string>(args.begin() + 2, args.end())};
|
|
|
|
if (ctx.command == "autocomplete") {
|
|
CommandRegistry::instance().autocomplete(ctx);
|
|
return 0;
|
|
}
|
|
|
|
const CommandInfo* info = CommandRegistry::instance().find_command(ctx.command);
|
|
if (!info) {
|
|
std::cerr << "Unknown command: " << ctx.command << std::endl;
|
|
return 1;
|
|
}
|
|
if (info->requires_config && !gConfig().is_config_set()) {
|
|
std::cerr << "Valid dropshell configuration required for command: " << ctx.command << std::endl;
|
|
std::cerr << "Please run 'dropshell edit' to set up the dropshell configuration." << std::endl;
|
|
return 1;
|
|
}
|
|
if (info->requires_install && !gConfig().is_agent_installed()) {
|
|
std::cerr << "Dropshell agent not installed for command: " << ctx.command << std::endl;
|
|
std::cerr << "Please run 'dropshell install' to install the local dropshell agent." << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int arg_count = ctx.args.size();
|
|
if (arg_count < info->min_args || (info->max_args != -1 && arg_count > info->max_args)) {
|
|
std::cerr << "Invalid number of arguments for command: " << ctx.command << std::endl;
|
|
std::cerr << "Usage: " << std::endl;
|
|
std::cout << " ";
|
|
print_left_aligned(info->help_usage,30);
|
|
std::cout << info->help_description << std::endl;
|
|
return 1;
|
|
}
|
|
return info->handler(ctx);
|
|
|
|
}
|
|
catch (const std::exception& e) {
|
|
std::cerr << "Error: " << e.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
struct ServerAndServices {
|
|
std::string server_name;
|
|
std::vector<LocalServiceInfo> servicelist;
|
|
};
|
|
|
|
bool getCLIServices(const std::string & arg2, const std::string & arg3,
|
|
ServerAndServices & server_and_services)
|
|
{
|
|
if (arg2.empty()) return false;
|
|
server_and_services.server_name = arg2;
|
|
|
|
if (arg3.empty()) {
|
|
server_and_services.servicelist = get_server_services_info(arg2);
|
|
} else {
|
|
server_and_services.servicelist.push_back(get_service_info(arg2, arg3));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void printversion() {
|
|
maketitle("DropShell version " + VERSION);
|
|
std::cout << "Release date: " << RELEASE_DATE << std::endl;
|
|
std::cout << "Author: " << AUTHOR << std::endl;
|
|
std::cout << "License: " << LICENSE << std::endl;
|
|
}
|
|
|
|
auto command_match = [](const std::string& cmd_list, int argc, char* argv[]) -> bool {
|
|
std::istringstream iss(cmd_list);
|
|
std::string cmd_item;
|
|
while (iss >> cmd_item) {
|
|
if (cmd_item == safearg(argc, argv, 1)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
#define BOOLEXIT(CMD_LIST, RUNCMD) { \
|
|
if (command_match(CMD_LIST, argc, argv)) { \
|
|
return (RUNCMD) ? 0 : 1; \
|
|
} \
|
|
}
|
|
|
|
#define HAPPYEXIT(CMD_LIST, RUNCMD) { \
|
|
if (command_match(CMD_LIST, argc, argv)) { \
|
|
RUNCMD; \
|
|
return 0; \
|
|
} \
|
|
}
|
|
|
|
|
|
int old_main(int argc, char* argv[]) {
|
|
HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
|
|
HAPPYEXIT("version", printversion())
|
|
BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
|
|
ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
|
|
|
|
try {
|
|
// silently attempt to load the config file and templates.
|
|
gConfig().load_config();
|
|
if (gConfig().is_config_set())
|
|
gTemplateManager().load_sources();
|
|
|
|
std::string cmd = argv[1];
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
// from here we require the config file to be loaded.
|
|
if (!gConfig().is_config_set())
|
|
return die("Please run 'dropshell edit' to set up the dropshell configuration.");
|
|
|
|
|
|
const std::vector<std::string> & server_definition_paths = gConfig().get_local_server_definition_paths();
|
|
if (server_definition_paths.size()>1) { // only show if there are multiple.
|
|
std::cout << "Server definition paths: ";
|
|
for (auto & dir : server_definition_paths)
|
|
std::cout << "["<< dir << "] ";
|
|
std::cout << std::endl;
|
|
}
|
|
if (gTemplateManager().is_loaded() && gTemplateManager().get_source_count() > 0)
|
|
gTemplateManager().print_sources();
|
|
|
|
HAPPYEXIT("templates", gTemplateManager().list_templates());
|
|
|
|
if (cmd == "create-template") {
|
|
if (argc < 3) return die("Error: create-template requires a template name");
|
|
return (gTemplateManager().create_template(argv[2])) ? 0 : 1;
|
|
}
|
|
|
|
if (cmd == "create-server") {
|
|
if (argc < 3) return die("Error: create-server requires a server name");
|
|
return (create_server(argv[2])) ? 0 : 1;
|
|
}
|
|
|
|
if (cmd == "create-service") {
|
|
if (argc < 5) return die("Error: not enough arguments.\ndropshell create-service server template service");
|
|
return (create_service(argv[2], argv[3], argv[4])) ? 0 : 1;
|
|
}
|
|
|
|
if (cmd == "ssh" && argc < 4) {
|
|
if (argc < 3) return die("Error: ssh requires a server name and optionally service name");
|
|
service_runner::interactive_ssh(argv[2], "bash");
|
|
return 0;
|
|
}
|
|
|
|
|
|
// handle running a command.
|
|
std::set<std::string> commands;
|
|
get_all_used_commands(commands);
|
|
|
|
autocomplete::merge_commands(commands, autocomplete::service_commands_require_config); // handled by service_runner, but not in template_shell_commands.
|
|
|
|
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) {
|
|
if (!SIvalid(service_info))
|
|
std::cerr<<"Error: Unable to get service information."<<std::endl;
|
|
else {
|
|
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 on service "+service_info.service_name);
|
|
}
|
|
}
|
|
|
|
// success!
|
|
return 0;
|
|
}
|
|
|
|
// Unknown command
|
|
std::cerr << "Error: Unknown command '" << cmd << "'" << std::endl;
|
|
std::cerr << "Valid commands: ";
|
|
for (const auto& command : commands) {
|
|
if (!command.empty() && command[0]!='_')
|
|
std::cerr << command << " ";
|
|
}
|
|
std::cerr << std::endl;
|
|
return 1;
|
|
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "Error: " << e.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
} // namespace dropshell
|
|
|
|
int main(int argc, char* argv[]) {
|
|
return dropshell::main(argc, argv);
|
|
} |