diff options
Diffstat (limited to 'server/src/main.cpp')
| -rw-r--r-- | server/src/main.cpp | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/server/src/main.cpp b/server/src/main.cpp new file mode 100644 index 0000000..a40b0b0 --- /dev/null +++ b/server/src/main.cpp | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | #include <malloc.h> // for malloc_trim(3) | ||
| 2 | #include <boost/asio.hpp> | ||
| 3 | |||
| 4 | import std; | ||
| 5 | import routemon; | ||
| 6 | |||
| 7 | namespace chrono = std::chrono; | ||
| 8 | namespace net = boost::asio; | ||
| 9 | |||
| 10 | enum class exit_status { | ||
| 11 | failure, | ||
| 12 | bad_usage, | ||
| 13 | }; | ||
| 14 | |||
| 15 | auto real_main(std::span<char const*> args) -> exit_status { | ||
| 16 | auto sink = routemon::log::make_sink(routemon::log::level::info); | ||
| 17 | auto l = routemon::log::logger{sink}; | ||
| 18 | |||
| 19 | if (args.size() != 2) { | ||
| 20 | l.error("Fatal: expected exactly one argument (the configuration file location), got {}", args.size() - 1); | ||
| 21 | return exit_status::bad_usage; | ||
| 22 | } | ||
| 23 | auto const* config_filename = args[1]; | ||
| 24 | |||
| 25 | auto lgen = routemon::locale::make_generator(); | ||
| 26 | auto default_locale = lgen->generate("en_US.UTF-8"); | ||
| 27 | auto locales = { | ||
| 28 | default_locale, | ||
| 29 | lgen->generate("nl_NL.UTF-8"), | ||
| 30 | lgen->generate("de_DE.UTF-8"), | ||
| 31 | lgen->generate("en_GB.UTF-8"), | ||
| 32 | }; | ||
| 33 | auto lsel = routemon::locale::selector{locales, default_locale, lgen}; | ||
| 34 | |||
| 35 | auto ioc = net::io_context{1 /* concurrency hint */}; | ||
| 36 | |||
| 37 | auto config = routemon::config::app{}; | ||
| 38 | try { | ||
| 39 | config = routemon::config::load_file(config_filename); | ||
| 40 | } catch (std::exception const& e) { | ||
| 41 | l.with("filename", std::string_view{config_filename}). | ||
| 42 | error("Failed to load configuration: {}", e.what()); | ||
| 43 | return exit_status::failure; | ||
| 44 | } | ||
| 45 | sink->set_level(config.logger.level); | ||
| 46 | // auto rwgps_client = routemon::rwgps::client{ioc, l, config.rwgps.api_key, config.rwgps.auth_token}; | ||
| 47 | // for (auto route : rwgps_client.get_all_routes()) { | ||
| 48 | // l.info("Route {} (user {}): {} @ {}", route.id, route.user_id, route.name, route.url); | ||
| 49 | // } | ||
| 50 | |||
| 51 | auto dbc = std::shared_ptr<routemon::database::connection>{}; | ||
| 52 | try { | ||
| 53 | dbc = routemon::database::open(config.database.sqlite3_filename); | ||
| 54 | } catch (std::exception const& e) { | ||
| 55 | l.with("filename", config.database.sqlite3_filename). | ||
| 56 | error("Failed to open database: {}", e.what()); | ||
| 57 | return exit_status::failure; | ||
| 58 | } | ||
| 59 | |||
| 60 | l.with("filename", config.situations.datex2_filename). | ||
| 61 | info("Loading situations"); | ||
| 62 | auto const before_load = chrono::steady_clock::now(); | ||
| 63 | auto d2loader = routemon::datex2::loader{}; | ||
| 64 | auto pub = routemon::datex2::situation_publication{}; | ||
| 65 | try { | ||
| 66 | pub = d2loader.load_situation_publication(config.situations.datex2_filename); | ||
| 67 | } catch (std::exception const& e) { | ||
| 68 | l.error("Failed to load DATEX II situations publication: {}", e.what()); | ||
| 69 | return exit_status::failure; | ||
| 70 | } | ||
| 71 | if (!d2loader.warnings().empty()) { | ||
| 72 | auto const& warns = d2loader.warnings(); | ||
| 73 | l.warn("Encountered {} unique warnings while loading DATEX II situations publication", warns.size()); | ||
| 74 | auto i = 0uz; | ||
| 75 | for (auto it = warns.begin(); it != warns.end(); it = warns.upper_bound(*it)) { | ||
| 76 | l.warn("Warning {} (appeared {}×): {}", ++i, warns.count(*it), *it); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | // Processing the feed is by far the most memory-intensive operation | ||
| 80 | // during the run time of this application (at the moment), the | ||
| 81 | // resident set will likely never be this big again. So we ask libc | ||
| 82 | // to return as much memory as possible to the OS. | ||
| 83 | malloc_trim(0); | ||
| 84 | auto const after_load = chrono::steady_clock::now(); | ||
| 85 | auto const dur_load = chrono::duration_cast<chrono::milliseconds>(after_load - before_load); | ||
| 86 | l.info("Loading situations finished in {}", dur_load); | ||
| 87 | |||
| 88 | auto handler = routemon::api::handler{l, std::move(pub)}; | ||
| 89 | auto http_server = routemon::srv::server{l, std::move(lsel), std::move(handler)}; | ||
| 90 | http_server.spawn(ioc); | ||
| 91 | ioc.run(); | ||
| 92 | |||
| 93 | l.error("I/O context stopped"); | ||
| 94 | return exit_status::failure; | ||
| 95 | } | ||
| 96 | |||
| 97 | auto main(int argc, char* argv[]) -> int { | ||
| 98 | if (argc < 0) { | ||
| 99 | std::cout << "Fatal: argument count below zero" << std::endl; | ||
| 100 | return EXIT_FAILURE; | ||
| 101 | } | ||
| 102 | auto res = real_main(std::span{const_cast<char const**>(argv), static_cast<std::size_t>(argc)}); | ||
| 103 | switch (res) { | ||
| 104 | case exit_status::failure: | ||
| 105 | return EXIT_FAILURE; | ||
| 106 | case exit_status::bad_usage: | ||
| 107 | return 2; | ||
| 108 | } | ||
| 109 | } | ||