#include "shared/init.hh" namespace shared { static void set_signal(const decltype(SIGINT) signal, void (*const callback)(int)) { struct sigaction sa {}; sa.sa_handler = callback; sa.sa_flags = 0; if (sigaction(signal, &sa, nullptr) == -1) { throw std::runtime_error("failed to set signal handler for signal " + std::to_string(signal)); } } static void set_terminate_backtrace() { std::set_terminate([]() { try { throw; } catch (const std::exception& e) { shared::print::fault << shared::print::time << "unhandled exception!\n" << " what: " << e.what() << '\n'; constexpr std::size_t BACKTRACE_BUF_SIZE = 64; std::array bt_buffer{}; const int nptrs = backtrace(std::data(bt_buffer), std::size(bt_buffer)); char** strings = backtrace_symbols(std::data(bt_buffer), nptrs); shared::print::message << "\n backtrace:\n"; for (int i = 0; i < nptrs; ++i) { shared::print::message << " " << strings[i] << '\n'; } free(strings); // controversial } catch (...) { } std::exit(EXIT_FAILURE); }); } void init() { GOOGLE_PROTOBUF_VERIFY_VERSION; set_terminate_backtrace(); set_signal(SIGPIPE, SIG_IGN); set_signal(SIGINT, []([[maybe_unused]] const int signum) { shared::print::warn << " interrupt signal received\n"; shared::should_exit = true; // graceful cleanup }); #ifndef NDEBUG shared::print::debug << "This binary is a debug build and will be slower " "and more verbose than usual\n"; #endif } void try_main(const main_func_t& func, const std::string_view address, const std::string_view port) { try { func(address, port); } catch (const std::exception& e) { shared::print::fault << shared::print::time << "caught exception!\n" << " what: " << e.what() << '\n'; } } const args_t& get_options() { static args_t ret{ {.name = "help", .desc = "display this, quit", .val = "h"}}; return ret; } bool parse_arg(const int&, const char* const) { return false; } static std::vector make_long_options(const args_t& args) { std::vector options{ option{"help", no_argument, nullptr, 'h'}}; std::transform(std::begin(args), std::end(args), std::back_inserter(options), [](const auto& arg) { const auto req = std::string{arg.val}.ends_with(':') ? required_argument : no_argument; return option{arg.name, req, nullptr, *arg.val}; }); return options; } static std::string make_long_vals(const args_t& args) { std::string ret{"h"}; // always include help for (const auto& arg : args) { ret.append(arg.val); } return ret; } static auto get_option_it(const auto& options, const decltype(optopt)& c) { return std::ranges::find_if(options, [&](const auto& opt) { return c == opt.val; }); } static void print_help(const args_t& args, const auto& options) { const auto print_table = [](const auto& val, const int padding) { std::cout << std::left << std::setw(padding) << std::setfill(' ') << val; }; const int max = static_cast(std::strlen( std::ranges::max_element(args, [](const auto& a, const auto& b) { return std::strlen(a.name) < std::strlen(b.name); })->name)); for (const auto& arg : args) { const auto it = get_option_it(options, *arg.val); if (it == std::end(options)) { continue; } std::cout << " "; print_table(std::string{"--"} + arg.name, 3 + max); print_table(std::string{"-"} + static_cast(it->val), 3); std::cout << (it->has_arg == required_argument ? " " : " "); std::cout << ": "; print_table(arg.desc, 10); std::cout << '\n'; } } void parse_args(const int argc, char* const argv[], const args_t& args, const args_callbacks_t& callbacks) { const auto long_options = make_long_options(args); const auto long_vals = make_long_vals(args); for (int option_index = opterr = 0;;) { const auto c = getopt_long(argc, argv, long_vals.c_str(), std::data(long_options), &option_index); if (c == -1) { break; } else if (c == '?') { // parsing error const auto it = get_option_it(long_options, optopt); if (it != std::end(long_options)) { shared::print::fault << " bad argument: \"" << it->name << "\" required " << (it->has_arg == required_argument ? "an" : "no") << " argument\n"; } else { shared::print::fault << " unknown argument: \"" << /*TODO get arg <<*/ "\"\n"; } std::exit(EXIT_FAILURE); } if (c == 'h') { print_help(args, long_options); std::exit(EXIT_SUCCESS); } try { const char* const arg = optarg + (optarg && optarg[0] == '='); for (const auto& callback : callbacks) { if (!callback(c, arg)) { continue; } break; } } catch (const boost::bad_lexical_cast& e) { const auto find_it = get_option_it(long_options, c); shared::print::fault << " bad argument: \"" << (find_it == std::end(long_options) ? "" : find_it->name) << "\" failed type conversion\n"; std::exit(EXIT_FAILURE); } } if (optind < argc) { shared::print::fault << " unrecognised argument(s):"; for (int i = optind; i < argc; ++i) { shared::print::fault << std::string{" "} << argv[i]; } shared::print::fault << '\n'; std::exit(EXIT_FAILURE); } } } // namespace shared