This commit is contained in:
262
source/src/main.cpp
Normal file
262
source/src/main.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
#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);
|
||||
}
|
Reference in New Issue
Block a user