#include "server/server.hpp" #include "util/exception_utils.hpp" #include "util/log.hpp" #include "util/meminfo.hpp" #include "util/version.hpp" #include "osrm/engine_config.hpp" #include "osrm/exception.hpp" #include "osrm/osrm.hpp" #include "osrm/storage_config.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 boost::function0 console_ctrl_function; BOOL WINAPI console_ctrl_handler(DWORD ctrl_type) { switch (ctrl_type) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT: console_ctrl_function(); return TRUE; default: return FALSE; } } #endif using namespace osrm; const static unsigned INIT_OK_START_ENGINE = 0; const static unsigned INIT_OK_DO_NOT_START_ENGINE = 1; const static unsigned INIT_FAILED = -1; namespace osrm::engine { std::istream &operator>>(std::istream &in, EngineConfig::Algorithm &algorithm) { std::string token; in >> token; boost::to_lower(token); if (token == "ch" || token == "corech") algorithm = EngineConfig::Algorithm::CH; else if (token == "mld") algorithm = EngineConfig::Algorithm::MLD; else throw util::RuntimeError(token, ErrorCode::UnknownAlgorithm, SOURCE_REF); return in; } } // namespace osrm // generate boost::program_options object for the routing part inline unsigned generateServerProgramOptions(const int argc, const char *argv[], boost::filesystem::path &base_path, std::string &ip_address, int &ip_port, bool &trial, EngineConfig &config, int &requested_thread_num) { using boost::filesystem::path; using boost::program_options::value; const auto hardware_threads = std::max(1, std::thread::hardware_concurrency()); // declare a group of options that will be allowed only on command line boost::program_options::options_description generic_options("Options"); generic_options.add_options() // ("version,v", "Show version") // ("help,h", "Show this help message") // ("verbosity,l", #ifdef NDEBUG boost::program_options::value(&config.verbosity)->default_value("INFO"), #else boost::program_options::value(&config.verbosity)->default_value("DEBUG"), #endif std::string("Log verbosity level: " + util::LogPolicy::GetLevels()).c_str()) // ("trial", value(&trial)->implicit_value(true), "Quit after initialization"); // declare a group of options that will be allowed on command line boost::program_options::options_description config_options("Configuration"); config_options.add_options() // ("ip,i", value(&ip_address)->default_value("0.0.0.0"), "IP address") // ("port,p", value(&ip_port)->default_value(5000), "TCP/IP port") // ("threads,t", value(&requested_thread_num)->default_value(hardware_threads), "Number of threads to use") // ("shared-memory,s", value(&config.use_shared_memory)->implicit_value(true)->default_value(false), "Load data from shared memory") // ("memory_file", value(&config.memory_file), "DEPRECATED: Will behave the same as --mmap.")( "mmap,m", value(&config.use_mmap)->implicit_value(true)->default_value(false), "Map datafiles directly, do not use any additional memory.") // ("dataset-name", value(&config.dataset_name), "Name of the shared memory dataset to connect to.") // ("algorithm,a", value(&config.algorithm) ->default_value(EngineConfig::Algorithm::CH, "CH"), "Algorithm to use for the data. Can be CH, CoreCH, MLD.") // ("max-viaroute-size", value(&config.max_locations_viaroute)->default_value(500), "Max. locations supported in viaroute query") // ("max-trip-size", value(&config.max_locations_trip)->default_value(100), "Max. locations supported in trip query") // ("max-table-size", value(&config.max_locations_distance_table)->default_value(100), "Max. locations supported in distance table query") // ("max-matching-size", value(&config.max_locations_map_matching)->default_value(100), "Max. locations supported in map matching query") // ("max-nearest-size", value(&config.max_results_nearest)->default_value(100), "Max. results supported in nearest query") // ("max-alternatives", value(&config.max_alternatives)->default_value(3), "Max. number of alternatives supported in the MLD route query") // ("max-matching-radius", value(&config.max_radius_map_matching)->default_value(-1.0), "Max. radius size supported in map matching query. Default: unlimited."); // hidden options, will be allowed on command line, but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); hidden_options.add_options()( "base,b", value(&base_path), "base path to .osrm file"); // positional option boost::program_options::positional_options_description positional_options; positional_options.add("base", 1); // combine above options for parsing boost::program_options::options_description cmdline_options; cmdline_options.add(generic_options).add(config_options).add(hidden_options); const auto *executable = argv[0]; boost::program_options::options_description visible_options( boost::filesystem::path(executable).filename().string() + " []"); visible_options.add(generic_options).add(config_options); // parse command line options boost::program_options::variables_map option_variables; try { boost::program_options::store(boost::program_options::command_line_parser(argc, argv) .options(cmdline_options) .positional(positional_options) .run(), option_variables); } catch (const boost::program_options::error &e) { util::Log(logERROR) << e.what(); return INIT_FAILED; } if (option_variables.count("version")) { std::cout << OSRM_VERSION << std::endl; return INIT_OK_DO_NOT_START_ENGINE; } if (option_variables.count("help")) { std::cout << visible_options; return INIT_OK_DO_NOT_START_ENGINE; } boost::program_options::notify(option_variables); if (!config.use_shared_memory && option_variables.count("base")) { return INIT_OK_START_ENGINE; } else if (config.use_shared_memory && !option_variables.count("base")) { return INIT_OK_START_ENGINE; } else if (config.use_shared_memory && option_variables.count("base")) { util::Log(logWARNING) << "Shared memory settings conflict with path settings."; } // Adjust number of threads to hardware concurrency requested_thread_num = std::min(hardware_threads, requested_thread_num); std::cout << visible_options; return INIT_OK_DO_NOT_START_ENGINE; } int main(int argc, const char *argv[]) try { util::LogPolicy::GetInstance().Unmute(); bool trial_run = false; std::string ip_address; int ip_port; EngineConfig config; boost::filesystem::path base_path; int requested_thread_num = 1; const unsigned init_result = generateServerProgramOptions( argc, argv, base_path, ip_address, ip_port, trial_run, config, requested_thread_num); if (init_result == INIT_OK_DO_NOT_START_ENGINE) { return EXIT_SUCCESS; } if (init_result == INIT_FAILED) { return EXIT_FAILURE; } util::LogPolicy::GetInstance().SetLevel(config.verbosity); if (!base_path.empty()) { config.storage_config = storage::StorageConfig(base_path); } if (!config.use_shared_memory && !config.storage_config.IsValid()) { util::Log(logERROR) << "Required files are missing, cannot continue"; return EXIT_FAILURE; } if (!config.IsValid()) { if (base_path.empty() != config.use_shared_memory) { util::Log(logWARNING) << "Path settings and shared memory conflicts."; } return EXIT_FAILURE; } util::Log() << "starting up engines, " << OSRM_VERSION; if (config.use_shared_memory) { util::Log() << "Loading from shared memory"; } util::Log() << "Threads: " << requested_thread_num; util::Log() << "IP address: " << ip_address; util::Log() << "IP port: " << ip_port; #ifndef _WIN32 int sig = 0; sigset_t wait_mask; sigemptyset(&wait_mask); sigaddset(&wait_mask, SIGINT); sigaddset(&wait_mask, SIGQUIT); sigaddset(&wait_mask, SIGTERM); pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); // only block necessary signals #endif auto service_handler = std::make_unique(config); auto routing_server = server::Server::CreateServer(ip_address, ip_port, requested_thread_num); routing_server->RegisterServiceHandler(std::move(service_handler)); if (trial_run) { util::Log() << "trial run, quitting after successful initialization"; } else { std::packaged_task server_task([&] { routing_server->Run(); return 0; }); auto future = server_task.get_future(); std::thread server_thread(std::move(server_task)); #ifndef _WIN32 util::Log() << "running and waiting for requests"; if (std::getenv("SIGNAL_PARENT_WHEN_READY")) { kill(getppid(), SIGUSR1); } sigwait(&wait_mask, &sig); util::Log() << "received signal " << sig; #else // Set console control handler to allow server to be stopped. console_ctrl_function = std::bind(&server::Server::Stop, routing_server); SetConsoleCtrlHandler(console_ctrl_handler, TRUE); util::Log() << "running and waiting for requests"; routing_server->Run(); #endif util::Log() << "initiating shutdown"; routing_server->Stop(); util::Log() << "stopping threads"; auto status = future.wait_for(std::chrono::seconds(2)); if (status == std::future_status::ready) { server_thread.join(); } else { util::Log(logWARNING) << "Didn't exit within 2 seconds. Hard abort!"; std::exit(EXIT_FAILURE); } } util::Log() << "freeing objects"; routing_server.reset(); util::Log() << "shutdown completed"; } catch (const osrm::RuntimeError &e) { util::Log(logERROR) << e.what(); return e.GetCode(); } catch (const std::bad_alloc &e) { util::DumpMemoryStats(); util::Log(logWARNING) << "[exception] " << e.what(); util::Log(logWARNING) << "Please provide more memory or consider using a larger swapfile"; return EXIT_FAILURE; } #ifdef _WIN32 catch (const std::exception &e) { util::Log(logERROR) << "[exception] " << e.what(); return EXIT_FAILURE; } #endif