aboutsummaryrefslogtreecommitdiff
path: root/src/shared/init.cc
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
commite4483eca01b48b943cd0461e24a74ae1a3139ed4 (patch)
treeed58c3c246e3af1af337697695d780aa31f6ad9a /src/shared/init.cc
parent1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (diff)
Update to most recent version (old initial commit)
Diffstat (limited to 'src/shared/init.cc')
-rw-r--r--src/shared/init.cc182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/shared/init.cc b/src/shared/init.cc
new file mode 100644
index 0000000..3415abb
--- /dev/null
+++ b/src/shared/init.cc
@@ -0,0 +1,182 @@
+#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<void*, BACKTRACE_BUF_SIZE> 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<struct option> make_long_options(const args_t& args) {
+ std::vector<struct option> 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<int>(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<char>(it->val), 3);
+ std::cout << (it->has_arg == required_argument ? "<arg> " : " ");
+ 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