#include "version.hpp" #include "config.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 "output.hpp" #include #include #include #include #include #include #include #include #include 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 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(args.begin() + 2, args.end())}; if (ctx.command == "autocomplete") { CommandRegistry::instance().autocomplete(ctx); return 0; } const CommandInfo* cmdinfo = CommandRegistry::instance().find_command(ctx.command); if (!cmdinfo) { error << "Unknown command: " << ctx.command << std::endl; return 1; } if (cmdinfo->requires_config && !gConfig().is_config_set()) { error << "Valid dropshell configuration required for command: " << ctx.command << std::endl; info << "Please run 'dropshell edit' to set up the dropshell configuration." << std::endl; return 1; } if (cmdinfo->requires_install && !gConfig().is_agent_installed()) { error << "Dropshell agent not installed for command: " << ctx.command << std::endl; info << "Please run 'dropshell install' to install the local dropshell agent." << std::endl; return 1; } int arg_count = ctx.args.size(); if (arg_count < cmdinfo->min_args || (cmdinfo->max_args != -1 && arg_count > cmdinfo->max_args)) { error << "Invalid number of arguments for command: " << ctx.command << std::endl; debug << "(" << ctx.args.size() << " args provided, " << ctx.command << " requires " << (cmdinfo->min_args) << " to " << (cmdinfo->max_args) << " args)" << std::endl; info << "Usage: " << std::endl; info << " "; info << left_align(cmdinfo->help_usage,30); info << cmdinfo->help_description << std::endl; return 1; } return cmdinfo->handler(ctx); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } } // ------------------------------------------------------------------------------------------------ struct ServerAndServices { std::string server_name; std::vector 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; } 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 & 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 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 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."< additional_args; // for (int i=4; i