mirror of
https://github.com/EvernodeXRPL/sashimono.git
synced 2026-04-29 15:38:00 +00:00
240 lines
7.7 KiB
C++
240 lines
7.7 KiB
C++
/**
|
|
Entry point for Sashi-cli
|
|
**/
|
|
#include "pchheader.hpp"
|
|
#include "cli-manager.hpp"
|
|
#include "version.hpp"
|
|
|
|
#define DEV_MODE "DEV_MODE"
|
|
#define DATA_DIR "DATA_DIR"
|
|
|
|
std::string data_dir;
|
|
|
|
/**
|
|
* Performs any cleanup on graceful application termination.
|
|
*/
|
|
void deinit()
|
|
{
|
|
cli::deinit();
|
|
}
|
|
|
|
void segfault_handler(int signum)
|
|
{
|
|
std::cout << boost::stacktrace::stacktrace() << std::endl;
|
|
exit(SIGABRT);
|
|
}
|
|
|
|
/**
|
|
* Global exception handler for std exceptions.
|
|
*/
|
|
void std_terminate() noexcept
|
|
{
|
|
const std::exception_ptr exptr = std::current_exception();
|
|
if (exptr != 0)
|
|
{
|
|
try
|
|
{
|
|
std::rethrow_exception(exptr);
|
|
}
|
|
catch (std::exception &ex)
|
|
{
|
|
std::cerr << "std error: " << ex.what() << std::endl;
|
|
}
|
|
catch (...)
|
|
{
|
|
std::cerr << "std error: Terminated due to unknown exception\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "std error: Terminated due to unknown reason\n";
|
|
}
|
|
|
|
std::cerr << boost::stacktrace::stacktrace() << std::endl;
|
|
|
|
exit(1);
|
|
}
|
|
|
|
template <typename executor>
|
|
int execute_cli(executor const &func)
|
|
{
|
|
if (cli::init(data_dir) == -1)
|
|
return -1;
|
|
|
|
const int ret = func();
|
|
cli::deinit();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Parses CLI args and extracts sashimono agent command and parameters given using CLI11 library.
|
|
* @param argc Argument count.
|
|
* @param argv Arguments.
|
|
* @returns 0 on success, -1 on error.
|
|
*/
|
|
int parse_cmd(int argc, char **argv)
|
|
{
|
|
// Initialize CLI.
|
|
CLI::App app("Sashimono CLI");
|
|
app.set_help_all_flag("--help-all", "Expand all help");
|
|
|
|
// Initialize subcommands.
|
|
CLI::App *version = app.add_subcommand("version", "Displays Sashimono CLI version.");
|
|
CLI::App *status = app.add_subcommand("status", "Check socket accessibility.");
|
|
CLI::App *list = app.add_subcommand("list", "List all instances.");
|
|
CLI::App *json = app.add_subcommand("json", "JSON payload. Example: sashi json -m '{\"type\":\"<instruction_type>\", ...}'");
|
|
CLI::App *create = app.add_subcommand("create", "Creates an instance.");
|
|
CLI::App *start = app.add_subcommand("start", "Starts an instance.");
|
|
CLI::App *stop = app.add_subcommand("stop", "Stops an instance.");
|
|
CLI::App *destroy = app.add_subcommand("destroy", "Destroys an instance.");
|
|
CLI::App *attach = app.add_subcommand("attach", "Attachs to the bash of a instance.");
|
|
|
|
// Initialize options.
|
|
std::string json_message;
|
|
json->add_option("-m,--message", json_message, "JSON message");
|
|
|
|
create->group(""); // Hides 'create' command from help-all
|
|
std::string owner, contract_id, image, outbound_ipv6, outbound_net_interface;
|
|
create->add_option("-o,--owner", owner, "Hex (ed-prefixed) public key of the instance owner");
|
|
create->add_option("-c,--contract-id", contract_id, "Contract Id (GUID) of the instance");
|
|
create->add_option("-i,--image", image, "Container image to use");
|
|
create->add_option("-a,--outbound-ipv6", outbound_ipv6, "Outbound IPV6 Address");
|
|
create->add_option("-f,--outbound-net-interface", outbound_net_interface, "Outbound IPV6 Network Interface (Façade)");
|
|
|
|
std::string container_name;
|
|
create->add_option("-n,--name", container_name, "Instance name");
|
|
start->add_option("-n,--name", container_name, "Instance name");
|
|
stop->add_option("-n,--name", container_name, "Instance name");
|
|
destroy->add_option("-n,--name", container_name, "Instance name");
|
|
attach->add_option("-n,--name", container_name, "Instance name");
|
|
|
|
CLI11_PARSE(app, argc, argv);
|
|
|
|
// Take the realpath of sashi cli exec path.
|
|
{
|
|
std::array<char, PATH_MAX> buffer;
|
|
const char *env_var = getenv(DATA_DIR);
|
|
if (env_var != nullptr)
|
|
{
|
|
data_dir = std::string(env_var);
|
|
}
|
|
else if (realpath(argv[0], buffer.data()))
|
|
{
|
|
data_dir = dirname(buffer.data());
|
|
}
|
|
else
|
|
{
|
|
// If real path fails, we get the current dir as exec bin path.
|
|
if (!getcwd(buffer.data(), buffer.size()))
|
|
{
|
|
std::cerr << errno << ": Error in executable path." << std::endl;
|
|
return -1;
|
|
}
|
|
data_dir = buffer.data();
|
|
}
|
|
}
|
|
|
|
// Verifying subcommands.
|
|
|
|
bool is_dev_mode = (getenv(DEV_MODE) != nullptr);
|
|
|
|
if (!is_dev_mode)
|
|
{
|
|
if (create->parsed())
|
|
{
|
|
std::cout << "Command not supported: Run with --help or --help-all for more information." << std::endl;
|
|
return -1;
|
|
}
|
|
if (json->parsed() && !json_message.empty())
|
|
{
|
|
jsoncons::json json_data = jsoncons::json::parse(json_message);
|
|
if (json_data.contains("type") && json_data["type"].as_string() == "create")
|
|
{
|
|
std::cout << "Command not supported: Run with --help or --help-all for more information." << std::endl;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (version->parsed())
|
|
{
|
|
std::cout << "Sashimono CLI version " << version::CLI_VERSION << std::endl;
|
|
return 0;
|
|
}
|
|
else if (status->parsed())
|
|
{
|
|
return execute_cli([]()
|
|
{
|
|
std::cout << cli::ctx.socket_path << std::endl;
|
|
return 0; });
|
|
}
|
|
else if (list->parsed())
|
|
{
|
|
return execute_cli([]()
|
|
{
|
|
if (cli::list() == -1)
|
|
{
|
|
std::cerr << "Failed to list instances." << std::endl;
|
|
return -1;
|
|
}
|
|
return 0; });
|
|
}
|
|
else if (json->parsed() && !json_message.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{
|
|
std::string output;
|
|
if (cli::get_json_output(json_message, output) == -1)
|
|
return -1;
|
|
|
|
std::cout << output << std::endl;
|
|
return 0; });
|
|
}
|
|
else if (create->parsed() && !container_name.empty() && !owner.empty() && !contract_id.empty() && !image.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{ return cli::create(container_name, owner, contract_id, image, outbound_ipv6, outbound_net_interface); });
|
|
}
|
|
else if (start->parsed() && !container_name.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{ return cli::execute_basic("start", container_name); });
|
|
}
|
|
else if (stop->parsed() && !container_name.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{ return cli::execute_basic("stop", container_name); });
|
|
}
|
|
else if (destroy->parsed() && !container_name.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{ return cli::execute_basic("destroy", container_name); });
|
|
}
|
|
else if (attach->parsed() && !container_name.empty())
|
|
{
|
|
return execute_cli([&]()
|
|
{ return cli::docker_exec("attach", container_name); });
|
|
}
|
|
|
|
std::cout << app.help();
|
|
return -1;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Register exception and segfault handlers.
|
|
std::set_terminate(&std_terminate);
|
|
signal(SIGSEGV, &segfault_handler);
|
|
signal(SIGABRT, &segfault_handler);
|
|
|
|
// Disable SIGPIPE to avoid crashing on broken pipe IO.
|
|
{
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGPIPE);
|
|
// sigprocmask is used instead of pthread_sigmask since this is single threaded.
|
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
|
}
|
|
|
|
return parse_cmd(argc, argv);
|
|
} |