From dc7f3cee229ebc13f776c50f042d8d0766e6aa67 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 5 Jan 2023 13:05:18 -0800 Subject: [PATCH] Replace cxxopts with CLI11 - Simiplifies CLI code for future modification - filesystem library linked in cmake check_for_std_filesystem file --- .gitmodules | 6 +- cmake/check_for_std_filesystem.cmake | 2 +- daemon/lokinet-vpn.cpp | 195 +++-- daemon/lokinet.cpp | 1031 +++++++++++++------------- external/CLI11 | 1 + external/CMakeLists.txt | 4 +- external/cxxopts | 1 - external/readme.md | 4 +- llarp/CMakeLists.txt | 2 +- llarp/context.cpp | 5 +- 10 files changed, 634 insertions(+), 617 deletions(-) create mode 160000 external/CLI11 delete mode 160000 external/cxxopts diff --git a/.gitmodules b/.gitmodules index 7ba32fba6..837e3a657 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "external/nlohmann"] path = external/nlohmann url = https://github.com/nlohmann/json.git -[submodule "external/cxxopts"] - path = external/cxxopts - url = https://github.com/jarro2783/cxxopts.git [submodule "external/ghc-filesystem"] path = external/ghc-filesystem url = https://github.com/gulrak/filesystem.git @@ -39,3 +36,6 @@ [submodule "gui"] path = gui url = https://github.com/oxen-io/lokinet-gui.git +[submodule "external/CLI11"] + path = external/CLI11 + url = https://github.com/CLIUtils/CLI11.git diff --git a/cmake/check_for_std_filesystem.cmake b/cmake/check_for_std_filesystem.cmake index 84248d07e..432b6b471 100644 --- a/cmake/check_for_std_filesystem.cmake +++ b/cmake/check_for_std_filesystem.cmake @@ -47,5 +47,5 @@ else() set(GHC_FILESYSTEM_WITH_INSTALL OFF CACHE INTERNAL "") add_subdirectory(external/ghc-filesystem) target_link_libraries(filesystem INTERFACE ghc_filesystem) - target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM) + target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM CLI11_HAS_FILESYSTEM=0) endif() diff --git a/daemon/lokinet-vpn.cpp b/daemon/lokinet-vpn.cpp index 3cdc7955d..b6512ed1a 100644 --- a/daemon/lokinet-vpn.cpp +++ b/daemon/lokinet-vpn.cpp @@ -1,12 +1,15 @@ #include #include #include -#include #include #include #include #include +#include +#include +#include + #ifdef _WIN32 // add the unholy windows headers for iphlpapi #include @@ -53,25 +56,28 @@ OMQ_Request( namespace { - template - constexpr bool is_optional = false; - template - constexpr bool is_optional> = true; - - // Extracts a value from a cxxopts result and assigns it into `value` if present. The value can - // either be a plain value or a std::optional. If not present, `value` is not touched. - template - void - extract_option(const cxxopts::ParseResult& r, const std::string& name, T& value) + + struct command_line_options { - if (r.count(name)) - { - if constexpr (is_optional) - value = r[name].as(); - else - value = r[name].as(); - } - } + // bool options + bool verbose = false; + bool help = false; + bool vpnUp = false; + bool vpnDown = false; + bool printStatus = false; + bool killDaemon = false; + + // string options + std::string exitAddress; + std::string rpc; + std::string endpoint = "default"; + std::string token; + std::optional range; + + // oxenmq + oxenmq::address rpcURL{"tcp://127.0.0.1:1190"}; + oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn; + }; // Takes a code, prints a message, and returns the code. Intended use is: // return exit_error(1, "blah: {}", 42); @@ -98,119 +104,104 @@ namespace int main(int argc, char* argv[]) { - cxxopts::Options opts("lokinet-vpn", "LokiNET vpn control utility"); - - // clang-format off - opts.add_options() - ("v,verbose", "Verbose", cxxopts::value()) - ("h,help", "help", cxxopts::value()) - ("kill", "kill the daemon", cxxopts::value()) - ("up", "put vpn up", cxxopts::value()) - ("down", "put vpn down", cxxopts::value()) - ("exit", "specify exit node address", cxxopts::value()) - ("rpc", "rpc url for lokinet", cxxopts::value()) - ("endpoint", "endpoint to use", cxxopts::value()) - ("token", "exit auth token to use", cxxopts::value()) - ("auth", "exit auth token to use", cxxopts::value()) - ("status", "print status and exit", cxxopts::value()) - ("range", "ip range to map", cxxopts::value()) - ; - // clang-format on - oxenmq::address rpcURL("tcp://127.0.0.1:1190"); - std::string exitAddress; - std::string endpoint = "default"; - std::string token; - std::optional range; - oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn; - bool goUp = false; - bool goDown = false; - bool printStatus = false; - bool killDaemon = false; + CLI::App cli{"lokiNET vpn control utility", "lokinet-vpn"}; + command_line_options options{}; + + // flags: boolean values in command_line_options struct + cli.add_flag("v,--verbose", options.verbose, "Verbose"); + cli.add_flag("--up", options.vpnUp, "Put VPN up"); + cli.add_flag("--down", options.vpnDown, "Put VPN down"); + cli.add_flag("--status", options.printStatus, "Print VPN status and exit"); + cli.add_flag("k,--kill", options.killDaemon, "Kill lokinet daemon"); + + // options: string values in command_line_options struct + cli.add_option("--exit", options.exitAddress, "Specify exit node address")->capture_default_str(); + cli.add_option("--endpoint", options.endpoint, "Endpoint to use")->capture_default_str(); + cli.add_option("--token", options.token, "Exit auth token to use")->capture_default_str(); + + // options: oxenmq values in command_line_options struct + cli.add_option("--rpc", options.rpc, "Specify RPC URL for lokinet")->capture_default_str(); + cli.add_option( + "--log-level", options.logLevel, "Log verbosity level, see log levels for accepted values") + ->type_name("LEVEL") + ->capture_default_str(); + try { - const auto result = opts.parse(argc, argv); - - if (result.count("help") > 0) - { - std::cout << opts.help() << std::endl; - return 0; - } - - if (result.count("verbose") > 0) - { - logLevel = oxenmq::LogLevel::debug; - } - goUp = result.count("up") > 0; - goDown = result.count("down") > 0; - printStatus = result.count("status") > 0; - killDaemon = result.count("kill") > 0; - - extract_option(result, "rpc", rpcURL); - extract_option(result, "exit", exitAddress); - extract_option(result, "endpoint", endpoint); - extract_option(result, "token", token); - extract_option(result, "auth", token); - extract_option(result, "range", range); + cli.parse(argc, argv); + } + catch (const CLI::ParseError& e) + { + return cli.exit(e); } - catch (const cxxopts::option_not_exists_exception& ex) + + try { - return exit_error(2, "{}\n{}", ex.what(), opts.help()); + if (options.verbose) + options.logLevel = oxenmq::LogLevel::debug; } - catch (std::exception& ex) + catch (const CLI::OptionNotFound& e) { - return exit_error(2, "{}", ex.what()); + cli.exit(e); } + catch (const CLI::Error& e) + { + cli.exit(e); + }; - int num_commands = goUp + goDown + printStatus + killDaemon; + int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon; - if (num_commands == 0) - return exit_error(3, "One of --up/--down/--status/--kill must be specified"); - if (num_commands != 1) - return exit_error(3, "Only one of --up/--down/--status/--kill may be specified"); + switch (numCommands) + { + case 0: + return exit_error(3, "One of --up/--down/--status/--kill must be specified"); + case 1: + return exit_error(3, "Only one of --up/--down/--status/--kill may be specified"); + default: + break; + } - if (goUp and exitAddress.empty()) - return exit_error("no exit address provided"); + if (options.vpnUp and options.exitAddress.empty()) + return exit_error("No exit address provided, must specify --exit
"); oxenmq::OxenMQ omq{ [](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) { std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl; }, - logLevel}; + options.logLevel}; omq.start(); std::promise connectPromise; - const auto connID = omq.connect_remote( - rpcURL, + const auto connectionID = omq.connect_remote( + options.rpc, [&connectPromise](auto) { connectPromise.set_value(true); }, [&connectPromise](auto, std::string_view msg) { - std::cout << "failed to connect to lokinet RPC: " << msg << std::endl; + std::cout << "Failed to connect to lokinet RPC: " << msg << std::endl; connectPromise.set_value(false); }); auto ftr = connectPromise.get_future(); if (not ftr.get()) - { return 1; - } - if (killDaemon) + if (options.killDaemon) { - if (not OMQ_Request(omq, connID, "llarp.halt")) - return exit_error("call to llarp.halt failed"); + if (not OMQ_Request(omq, connectionID, "llarp.halt")) + return exit_error("Call to llarp.halt failed"); return 0; } - if (printStatus) + if (options.printStatus) { - const auto maybe_status = OMQ_Request(omq, connID, "llarp.status"); + const auto maybe_status = OMQ_Request(omq, connectionID, "llarp.status"); if (not maybe_status) return exit_error("call to llarp.status failed"); try { - const auto& ep = maybe_status->at("result").at("services").at(endpoint); + const auto& ep = maybe_status->at("result").at("services").at(options.endpoint); const auto exitMap = ep.at("exitMap"); if (exitMap.empty()) { @@ -230,13 +221,13 @@ main(int argc, char* argv[]) } return 0; } - if (goUp) + if (options.vpnUp) { - nlohmann::json opts{{"exit", exitAddress}, {"token", token}}; - if (range) - opts["range"] = *range; + nlohmann::json opts{{"exit", options.exitAddress}, {"token", options.token}}; + if (options.range) + opts["range"] = *options.range; - auto maybe_result = OMQ_Request(omq, connID, "llarp.exit", std::move(opts)); + auto maybe_result = OMQ_Request(omq, connectionID, "llarp.exit", std::move(opts)); if (not maybe_result) return exit_error("could not add exit"); @@ -247,12 +238,12 @@ main(int argc, char* argv[]) return exit_error("{}", err_it.value()); } } - if (goDown) + if (options.vpnDown) { nlohmann::json opts{{"unmap", true}}; - if (range) - opts["range"] = *range; - if (not OMQ_Request(omq, connID, "llarp.exit", std::move(opts))) + if (options.range) + opts["range"] = *options.range; + if (not OMQ_Request(omq, connectionID, "llarp.exit", std::move(opts))) return exit_error("failed to unmap exit"); } diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 2683ccab0..454d18bb8 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -15,628 +15,651 @@ #include -#include #include #include +#include #include -int -lokinet_main(int, char**); - -#ifdef _WIN32 -extern "C" LONG FAR PASCAL -win32_signal_handler(EXCEPTION_POINTERS*); -extern "C" VOID FAR PASCAL -win32_daemon_entry(DWORD, LPTSTR*); - -VOID -insert_description(); - -#endif - -static auto logcat = llarp::log::Cat("main"); -std::shared_ptr ctx; -std::promise exit_code; - -void -handle_signal(int sig) -{ - llarp::log::info(logcat, "Handling signal {}", sig); - if (ctx) - ctx->loop->call([sig] { ctx->HandleSignal(sig); }); - else - std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl; -} +#include +#include +#include -#ifdef _WIN32 -int -startWinsock() +namespace { - WSADATA wsockd; - int err; - err = ::WSAStartup(MAKEWORD(2, 2), &wsockd); - if (err) + struct command_line_options { - perror("Failed to start Windows Sockets"); - return err; + // bool options + bool help = false; + bool version = false; + bool generate = false; + bool router = false; + bool force = false; + bool configOnly = false; + bool overwrite = false; + + // string options + std::string configPath; + + // windows options + bool win_install = false; + bool win_remove = false; + }; + + // windows-specific function declarations + int + startWinsock(); + void + install_win32_daemon(); + void + uninstall_win32_daemon(); + + // operational function definitions + int + lokinet_main(int, char**); + void + handle_signal(int sig); + static void + run_main_context(std::optional confFile, const llarp::RuntimeOptions opts); + + // variable declarations + static auto logcat = llarp::log::Cat("main"); + std::shared_ptr ctx; + std::promise exit_code; + + // operational function definitions + void + handle_signal(int sig) + { + llarp::log::info(logcat, "Handling signal {}", sig); + if (ctx) + ctx->loop->call([sig] { ctx->HandleSignal(sig); }); + else + std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl; } - ::CreateMutex(nullptr, FALSE, "lokinet_win32_daemon"); - return 0; -} -extern "C" BOOL FAR PASCAL -handle_signal_win32(DWORD fdwCtrlType) -{ - UNREFERENCED_PARAMETER(fdwCtrlType); - handle_signal(SIGINT); - return TRUE; // probably unreachable -} - -void -install_win32_daemon() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - std::array szPath{}; + // Windows specific code +#ifdef _WIN32 + extern "C" LONG FAR PASCAL + win32_signal_handler(EXCEPTION_POINTERS*); + extern "C" VOID FAR PASCAL + win32_daemon_entry(DWORD, LPTSTR*); + VOID + insert_description(); - if (!GetModuleFileName(nullptr, szPath.data(), MAX_PATH)) + extern "C" BOOL FAR PASCAL + handle_signal_win32(DWORD fdwCtrlType) { - llarp::LogError("Cannot install service ", GetLastError()); - return; - } - - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights + UNREFERENCED_PARAMETER(fdwCtrlType); + handle_signal(SIGINT); + return TRUE; // probably unreachable + }; - if (nullptr == schSCManager) + int + startWinsock() { - llarp::LogError("OpenSCManager failed ", GetLastError()); - return; + WSADATA wsockd; + int err; + err = ::WSAStartup(MAKEWORD(2, 2), &wsockd); + if (err) + { + perror("Failed to start Windows Sockets"); + return err; + } + ::CreateMutex(nullptr, FALSE, "lokinet_win32_daemon"); + return 0; } - // Create the service - schService = CreateService( - schSCManager, // SCM database - strdup("lokinet"), // name of service - "Lokinet for Windows", // service name to display - SERVICE_ALL_ACCESS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type - SERVICE_ERROR_NORMAL, // error control type - szPath.data(), // path to service's binary - nullptr, // no load ordering group - nullptr, // no tag identifier - nullptr, // no dependencies - nullptr, // LocalSystem account - nullptr); // no password - - if (schService == nullptr) + void + install_win32_daemon() { - llarp::LogError("CreateService failed ", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } - else - llarp::LogInfo("Service installed successfully"); + SC_HANDLE schSCManager; + SC_HANDLE schService; + std::array szPath{}; - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); - insert_description(); -} + if (!GetModuleFileName(nullptr, szPath.data(), MAX_PATH)) + { + llarp::LogError("Cannot install service ", GetLastError()); + return; + } -VOID -insert_description() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; - SERVICE_DESCRIPTION sd; - LPTSTR szDesc = strdup( - "LokiNET is a free, open source, private, " - "decentralized, \"market based sybil resistant\" " - "and IP based onion routing network"); - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (nullptr == schSCManager) - { - llarp::LogError("OpenSCManager failed ", GetLastError()); - return; - } + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (nullptr == schSCManager) + { + llarp::LogError("OpenSCManager failed ", GetLastError()); + return; + } - // Get a handle to the service. - schService = OpenService( - schSCManager, // SCM database - "lokinet", // name of service - SERVICE_CHANGE_CONFIG); // need change config access + // Create the service + schService = CreateService( + schSCManager, // SCM database + strdup("lokinet"), // name of service + "Lokinet for Windows", // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath.data(), // path to service's binary + nullptr, // no load ordering group + nullptr, // no tag identifier + nullptr, // no dependencies + nullptr, // LocalSystem account + nullptr); // no password + + if (schService == nullptr) + { + llarp::LogError("CreateService failed ", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + else + llarp::LogInfo("Service installed successfully"); - if (schService == nullptr) - { - llarp::LogError("OpenService failed ", GetLastError()); + CloseServiceHandle(schService); CloseServiceHandle(schSCManager); - return; + insert_description(); } - // Change the service description. - sd.lpDescription = szDesc; - - if (!ChangeServiceConfig2( - schService, // handle to service - SERVICE_CONFIG_DESCRIPTION, // change: description - &sd)) // new description + VOID + insert_description() { - llarp::LogError("ChangeServiceConfig2 failed"); - } - else - llarp::LogInfo("Service description updated successfully."); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} + SC_HANDLE schSCManager; + SC_HANDLE schService; + SERVICE_DESCRIPTION sd; + LPTSTR szDesc = strdup( + "LokiNET is a free, open source, private, " + "decentralized, \"market based sybil resistant\" " + "and IP based onion routing network"); + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (nullptr == schSCManager) + { + llarp::LogError("OpenSCManager failed ", GetLastError()); + return; + } -void -uninstall_win32_daemon() -{ - SC_HANDLE schSCManager; - SC_HANDLE schService; + // Get a handle to the service. + schService = OpenService( + schSCManager, // SCM database + "lokinet", // name of service + SERVICE_CHANGE_CONFIG); // need change config access - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights + if (schService == nullptr) + { + llarp::LogError("OpenService failed ", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } - if (nullptr == schSCManager) - { - llarp::LogError("OpenSCManager failed ", GetLastError()); - return; - } + // Change the service description. + sd.lpDescription = szDesc; - // Get a handle to the service. - schService = OpenService( - schSCManager, // SCM database - "lokinet", // name of service - 0x10000); // need delete access + if (!ChangeServiceConfig2( + schService, // handle to service + SERVICE_CONFIG_DESCRIPTION, // change: description + &sd)) // new description + { + llarp::LogError("ChangeServiceConfig2 failed"); + } + else + llarp::LogInfo("Service description updated successfully."); - if (schService == nullptr) - { - llarp::LogError("OpenService failed ", GetLastError()); + CloseServiceHandle(schService); CloseServiceHandle(schSCManager); - return; } - // Delete the service. - if (!DeleteService(schService)) + void + uninstall_win32_daemon() { - llarp::LogError("DeleteService failed ", GetLastError()); - } - else - llarp::LogInfo("Service deleted successfully\n"); + SC_HANDLE schSCManager; + SC_HANDLE schService; - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); -} -#endif + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights -/// this sets up, configures and runs the main context -static void -run_main_context(std::optional confFile, const llarp::RuntimeOptions opts) -{ - llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); - try - { - std::shared_ptr conf; - if (confFile) - { - llarp::LogInfo("Using config file: ", *confFile); - conf = std::make_shared(confFile->parent_path()); - } - else - { - conf = std::make_shared(llarp::GetDefaultDataDir()); - } - if (not conf->Load(confFile, opts.isSNode)) + if (nullptr == schSCManager) { - llarp::LogError("failed to parse configuration"); - exit_code.set_value(1); + llarp::LogError("OpenSCManager failed ", GetLastError()); return; } - ctx = std::make_shared(); - ctx->Configure(std::move(conf)); + // Get a handle to the service. + schService = OpenService( + schSCManager, // SCM database + "lokinet", // name of service + 0x10000); // need delete access - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); -#ifndef _WIN32 - signal(SIGHUP, handle_signal); - signal(SIGUSR1, handle_signal); -#endif - - try + if (schService == nullptr) { - ctx->Setup(opts); - } - catch (llarp::util::bind_socket_error& ex) - { - llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what())); - exit_code.set_value(1); + llarp::LogError("OpenService failed ", GetLastError()); + CloseServiceHandle(schSCManager); return; } - catch (std::exception& ex) + + // Delete the service. + if (!DeleteService(schService)) { - llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what())); - exit_code.set_value(1); - return; + llarp::LogError("DeleteService failed ", GetLastError()); } - llarp::util::SetThreadName("llarp-mainloop"); + else + llarp::LogInfo("Service deleted successfully\n"); - auto result = ctx->Run(opts); - exit_code.set_value(result); - } - catch (std::exception& e) - { - llarp::LogError("Fatal: caught exception while running: ", e.what()); - exit_code.set_exception(std::current_exception()); - } - catch (...) - { - llarp::LogError("Fatal: caught non-standard exception while running"); - exit_code.set_exception(std::current_exception()); + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); } -} - -#ifdef _WIN32 - -/// minidump generation for windows jizz -/// will make a coredump when there is an unhandled exception -LONG -GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) -{ - const auto flags = - (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo); - - std::stringstream ss; - ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp"; - const std::string fname = ss.str(); - HANDLE hDumpFile; - SYSTEMTIME stLocalTime; - GetLocalTime(&stLocalTime); - MINIDUMP_EXCEPTION_INFORMATION ExpParam{}; - - hDumpFile = CreateFile( - fname.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_WRITE | FILE_SHARE_READ, - 0, - CREATE_ALWAYS, - 0, - 0); - - ExpParam.ExceptionPointers = pExceptionPointers; - ExpParam.ClientPointers = TRUE; - - MiniDumpWriteDump( - GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, flags, &ExpParam, NULL, NULL); - - return 1; -} -#endif + /// minidump generation for windows jizz + /// will make a coredump when there is an unhandled exception + LONG + GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) + { + const auto flags = + (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo); + + std::stringstream ss; + ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp"; + const std::string fname = ss.str(); + HANDLE hDumpFile; + SYSTEMTIME stLocalTime; + GetLocalTime(&stLocalTime); + MINIDUMP_EXCEPTION_INFORMATION ExpParam{}; + + hDumpFile = CreateFile( + fname.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, + 0, + CREATE_ALWAYS, + 0, + 0); + + ExpParam.ExceptionPointers = pExceptionPointers; + ExpParam.ClientPointers = TRUE; + + MiniDumpWriteDump( + GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, flags, &ExpParam, NULL, NULL); -int -main(int argc, char* argv[]) -{ - // Set up a default, stderr logging for very early logging; we'll replace this later once we read - // the desired log info from config. - llarp::log::add_sink(llarp::log::Type::Print, "stderr"); - llarp::log::reset_level(llarp::log::Level::info); + return 1; + } - llarp::logRingBuffer = std::make_shared(100); - llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); + VOID FAR PASCAL + SvcCtrlHandler(DWORD dwCtrl) + { + // Handle the requested control code. -#ifndef _WIN32 - return lokinet_main(argc, argv); -#else - SERVICE_TABLE_ENTRY DispatchTable[] = { - {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; + switch (dwCtrl) + { + case SERVICE_CONTROL_STOP: + // tell service we are stopping + llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP"); + llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping); + handle_signal(SIGINT); + return; + + case SERVICE_CONTROL_INTERROGATE: + // report status + llarp::log::debug(logcat, "Got win32 service interrogate signal"); + llarp::sys::service_manager->report_changed_state(); + return; + + default: + llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl); + break; + } + } - // Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't - // return until the service enters STOPPED state. - if (StartServiceCtrlDispatcher(DispatchTable)) - return 0; + // The win32 daemon entry point is where we go when invoked as a windows service; we do the + // required service dance and then pretend we were invoked via main(). + VOID FAR PASCAL + win32_daemon_entry(DWORD, LPTSTR* argv) + { + // Register the handler function for the service + auto* svc = dynamic_cast(llarp::sys::service_manager); + svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler); - auto error = GetLastError(); + if (svc->handle == nullptr) + { + llarp::LogError("failed to register daemon control handler"); + return; + } - // We'll get this error if not invoked as a service, which is fine: we can just run directly - if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) - { - llarp::sys::service_manager->disable(); - return lokinet_main(argc, argv); + // we hard code the args to lokinet_main. + // we yoink argv[0] (lokinet.exe path) and pass in the new args. + std::array args = { + reinterpret_cast(argv[0]), + reinterpret_cast(strdup("c:\\programdata\\lokinet\\lokinet.ini")), + reinterpret_cast(0)}; + lokinet_main(args.size() - 1, args.data()); } - else - { - llarp::log::critical( - logcat, "Error launching service: {}", std::system_category().message(error)); - return 1; - } -#endif -} -int -lokinet_main(int argc, char** argv) -{ - if (auto result = Lokinet_INIT()) - return result; +#endif - llarp::RuntimeOptions opts; - opts.showBanner = false; + int + lokinet_main(int argc, char** argv) + { + if (auto result = Lokinet_INIT()) + return result; -#ifdef _WIN32 - if (startWinsock()) - return -1; - SetConsoleCtrlHandler(handle_signal_win32, TRUE); -#endif + llarp::RuntimeOptions opts; + opts.showBanner = false; - cxxopts::Options options( - "lokinet", - "LokiNET is a free, open source, private, " - "decentralized, \"market based sybil resistant\" " - "and IP based onion routing network"); - // clang-format off - options.add_options() #ifdef _WIN32 - ("install", "install win32 daemon to SCM", cxxopts::value()) - ("remove", "remove win32 daemon from SCM", cxxopts::value()) + if (startWinsock()) + return -1; + SetConsoleCtrlHandler(handle_signal_win32, TRUE); #endif - ("h,help", "print this help message", cxxopts::value()) - ("version", "print version string", cxxopts::value()) - ("g,generate", "generate default configuration and exit", cxxopts::value()) - ("r,router", "run in routing mode instead of client only mode", cxxopts::value()) - ("f,force", "force writing config even if it already exists", cxxopts::value()) - ("config", "path to lokinet.ini configuration file", cxxopts::value()) - ; - // clang-format on - - options.parse_positional("config"); - - bool genconfigOnly = false; - bool overwrite = false; - std::optional configFile; - try - { - auto result = options.parse(argc, argv); - if (result.count("help")) - { - std::cout << options.help() << std::endl; - return 0; - } - if (result.count("version")) + CLI::App cli{ + "LokiNET is a free, open source, private, decentralized, market-based sybil resistant and " + "IP " + "based onion routing network", + "lokinet"}; + command_line_options options{}; + + // flags: boolean values in command_line_options struct + cli.add_flag("--version", options.version, "Lokinet version"); + cli.add_flag("g,--generate", options.generate, "Generate default configuration and exit"); + cli.add_flag( + "r,--router", options.router, "Run lokinet in routing mode instead of client-only mode"); + cli.add_flag("f,--force", options.force, "Force writing config even if file exists"); + + // options: string + cli.add_option("--config", options.configPath, "Path to lokinet.ini configuration file") + ->capture_default_str(); + + if constexpr (llarp::platform::is_windows) { - std::cout << llarp::VERSION_FULL << std::endl; - return 0; - } -#ifdef _WIN32 - if (result.count("install")) - { - install_win32_daemon(); - return 0; + cli.add_flag("--install", options.win_install, "Install win32 daemon to SCM"); + cli.add_flag("--remove", options.win_remove, "Remove win32 daemon from SCM"); } - if (result.count("remove")) + try { - uninstall_win32_daemon(); - return 0; + cli.parse(argc, argv); } -#endif - if (result.count("generate") > 0) + catch (const CLI::ParseError& e) { - genconfigOnly = true; + return cli.exit(e); } - if (result.count("router") > 0) - { - opts.isSNode = true; - } + std::optional configFile; - if (result.count("force") > 0) + try { - overwrite = true; - } + if (options.version) + { + std::cout << llarp::VERSION_FULL << std::endl; + return 0; + } - if (result.count("config") > 0) - { - auto arg = result["config"].as(); - if (!arg.empty()) + if constexpr (llarp::platform::is_windows) { - configFile = arg; + if (options.win_install) + { + install_win32_daemon(); + return 0; + } + if (options.win_remove) + { + uninstall_win32_daemon(); + return 0; + } + } + + if (options.configOnly) + { + configFile = options.configPath; } } - } - catch (const cxxopts::option_not_exists_exception& ex) - { - std::cerr << ex.what(); - std::cout << options.help() << std::endl; - return 1; - } + catch (const CLI::OptionNotFound& e) + { + cli.exit(e); + } + catch (const CLI::Error& e) + { + cli.exit(e); + }; - if (configFile.has_value()) - { - // when we have an explicit filepath - fs::path basedir = configFile->parent_path(); - if (genconfigOnly) + if (configFile.has_value()) { - try + // when we have an explicit filepath + fs::path basedir = configFile->parent_path(); + if (options.configOnly) { - llarp::ensureConfig(basedir, *configFile, overwrite, opts.isSNode); + try + { + llarp::ensureConfig(basedir, *configFile, options.overwrite, opts.isSNode); + } + catch (std::exception& ex) + { + llarp::LogError("cannot generate config at ", *configFile, ": ", ex.what()); + return 1; + } } - catch (std::exception& ex) + else { - llarp::LogError("cannot generate config at ", *configFile, ": ", ex.what()); - return 1; + try + { + if (!fs::exists(*configFile)) + { + llarp::LogError("Config file not found ", *configFile); + return 1; + } + } + catch (std::exception& ex) + { + llarp::LogError("cannot check if ", *configFile, " exists: ", ex.what()); + return 1; + } } } else { try { - if (!fs::exists(*configFile)) - { - llarp::LogError("Config file not found ", *configFile); - return 1; - } + llarp::ensureConfig( + llarp::GetDefaultDataDir(), + llarp::GetDefaultConfigPath(), + options.overwrite, + opts.isSNode); } catch (std::exception& ex) { - llarp::LogError("cannot check if ", *configFile, " exists: ", ex.what()); + llarp::LogError("cannot ensure config: ", ex.what()); return 1; } + configFile = llarp::GetDefaultConfigPath(); } - } - else - { + + if (options.configOnly) + return 0; + +#ifdef _WIN32 + SetUnhandledExceptionFilter(&GenerateDump); +#endif + + std::thread main_thread{[configFile, opts] { run_main_context(configFile, opts); }}; + auto ftr = exit_code.get_future(); + + do + { + // do periodic non lokinet related tasks here + if (ctx and ctx->IsUp() and not ctx->LooksAlive()) + { + auto deadlock_cat = llarp::log::Cat("deadlock"); + for (const auto& wtf : + {"you have been visited by the mascott of the deadlocked router.", + "⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠄⠄⠄⠄", + "⠄⠄⠄⠄⠄⢀⣀⣀⡀⠄⠄⠄⡠⢲⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠄⠄", + "⠄⠄⠄⠔⣈⣀⠄⢔⡒⠳⡴⠊⠄⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣧⠄⠄", + "⠄⢜⡴⢑⠖⠊⢐⣤⠞⣩⡇⠄⠄⠄⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠄⠝⠛⠋⠐", + "⢸⠏⣷⠈⠄⣱⠃⠄⢠⠃⠐⡀⠄⠄⠄⠄⠙⠻⢿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠸⠄⠄⠄⠄", + "⠈⣅⠞⢁⣿⢸⠘⡄⡆⠄⠄⠈⠢⡀⠄⠄⠄⠄⠄⠄⠉⠙⠛⠛⠛⠉⠉⡀⠄⠡⢀⠄⣀", + "⠄⠙⡎⣹⢸⠄⠆⢘⠁⠄⠄⠄⢸⠈⠢⢄⡀⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠃⠄⠄⠄⠄⠄", + "⠄⠄⠑⢿⠈⢆⠘⢼⠄⠄⠄⠄⠸⢐⢾⠄⡘⡏⠲⠆⠠⣤⢤⢤⡤⠄⣖⡇⠄⠄⠄⠄⠄", + "⣴⣶⣿⣿⣣⣈⣢⣸⠄⠄⠄⠄⡾⣷⣾⣮⣤⡏⠁⠘⠊⢠⣷⣾⡛⡟⠈⠄⠄⠄⠄⠄⠄", + "⣿⣿⣿⣿⣿⠉⠒⢽⠄⠄⠄⠄⡇⣿⣟⣿⡇⠄⠄⠄⠄⢸⣻⡿⡇⡇⠄⠄⠄⠄⠄⠄⠄", + "⠻⣿⣿⣿⣿⣄⠰⢼⠄⠄⠄⡄⠁⢻⣍⣯⠃⠄⠄⠄⠄⠈⢿⣻⠃⠈⡆⡄⠄⠄⠄⠄⠄", + "⠄⠙⠿⠿⠛⣿⣶⣤⡇⠄⠄⢣⠄⠄⠈⠄⢠⠂⠄⠁⠄⡀⠄⠄⣀⠔⢁⠃⠄⠄⠄⠄⠄", + "⠄⠄⠄⠄⠄⣿⣿⣿⣿⣾⠢⣖⣶⣦⣤⣤⣬⣤⣤⣤⣴⣶⣶⡏⠠⢃⠌⠄⠄⠄⠄⠄⠄", + "⠄⠄⠄⠄⠄⠿⠿⠟⠛⡹⠉⠛⠛⠿⠿⣿⣿⣿⣿⣿⡿⠂⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄", + "⠠⠤⠤⠄⠄⣀⠄⠄⠄⠑⠠⣤⣀⣀⣀⡘⣿⠿⠙⠻⡍⢀⡈⠂⠄⠄⠄⠄⠄⠄⠄⠄⠄", + "⠄⠄⠄⠄⠄⠄⠑⠠⣠⣴⣾⣿⣿⣿⣿⣿⣿⣇⠉⠄⠻⣿⣷⣄⡀⠄⠄⠄⠄⠄⠄⠄⠄", + "file a bug report now or be cursed with this " + "annoying image in your syslog for all time."}) + { + llarp::log::critical(deadlock_cat, wtf); + llarp::log::flush(); + } + llarp::sys::service_manager->failed(); + std::abort(); + } + } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); + + main_thread.join(); + + int code = 0; + try { - llarp::ensureConfig( - llarp::GetDefaultDataDir(), llarp::GetDefaultConfigPath(), overwrite, opts.isSNode); + code = ftr.get(); } - catch (std::exception& ex) + catch (const std::exception& e) { - llarp::LogError("cannot ensure config: ", ex.what()); - return 1; + std::cerr << "main thread threw exception: " << e.what() << std::endl; + code = 1; + } + catch (...) + { + std::cerr << "main thread threw non-standard exception" << std::endl; + code = 2; } - configFile = llarp::GetDefaultConfigPath(); - } - if (genconfigOnly) - { - return 0; + llarp::log::flush(); + llarp::sys::service_manager->stopped(); + if (ctx) + { + ctx.reset(); + } + return code; } -#ifdef _WIN32 - SetUnhandledExceptionFilter(&GenerateDump); -#endif - - std::thread main_thread{[configFile, opts] { run_main_context(configFile, opts); }}; - auto ftr = exit_code.get_future(); - - do + // this sets up, configures and runs the main context + static void + run_main_context(std::optional confFile, const llarp::RuntimeOptions opts) { - // do periodic non lokinet related tasks here - if (ctx and ctx->IsUp() and not ctx->LooksAlive()) + llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO)); + try { - auto deadlock_cat = llarp::log::Cat("deadlock"); - for (const auto& wtf : - {"you have been visited by the mascott of the deadlocked router.", - "⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠄⠄⠄⠄", - "⠄⠄⠄⠄⠄⢀⣀⣀⡀⠄⠄⠄⡠⢲⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠄⠄", - "⠄⠄⠄⠔⣈⣀⠄⢔⡒⠳⡴⠊⠄⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣧⠄⠄", - "⠄⢜⡴⢑⠖⠊⢐⣤⠞⣩⡇⠄⠄⠄⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠄⠝⠛⠋⠐", - "⢸⠏⣷⠈⠄⣱⠃⠄⢠⠃⠐⡀⠄⠄⠄⠄⠙⠻⢿⣿⣿⣿⣿⣿⣿⣿⡿⠛⠸⠄⠄⠄⠄", - "⠈⣅⠞⢁⣿⢸⠘⡄⡆⠄⠄⠈⠢⡀⠄⠄⠄⠄⠄⠄⠉⠙⠛⠛⠛⠉⠉⡀⠄⠡⢀⠄⣀", - "⠄⠙⡎⣹⢸⠄⠆⢘⠁⠄⠄⠄⢸⠈⠢⢄⡀⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠃⠄⠄⠄⠄⠄", - "⠄⠄⠑⢿⠈⢆⠘⢼⠄⠄⠄⠄⠸⢐⢾⠄⡘⡏⠲⠆⠠⣤⢤⢤⡤⠄⣖⡇⠄⠄⠄⠄⠄", - "⣴⣶⣿⣿⣣⣈⣢⣸⠄⠄⠄⠄⡾⣷⣾⣮⣤⡏⠁⠘⠊⢠⣷⣾⡛⡟⠈⠄⠄⠄⠄⠄⠄", - "⣿⣿⣿⣿⣿⠉⠒⢽⠄⠄⠄⠄⡇⣿⣟⣿⡇⠄⠄⠄⠄⢸⣻⡿⡇⡇⠄⠄⠄⠄⠄⠄⠄", - "⠻⣿⣿⣿⣿⣄⠰⢼⠄⠄⠄⡄⠁⢻⣍⣯⠃⠄⠄⠄⠄⠈⢿⣻⠃⠈⡆⡄⠄⠄⠄⠄⠄", - "⠄⠙⠿⠿⠛⣿⣶⣤⡇⠄⠄⢣⠄⠄⠈⠄⢠⠂⠄⠁⠄⡀⠄⠄⣀⠔⢁⠃⠄⠄⠄⠄⠄", - "⠄⠄⠄⠄⠄⣿⣿⣿⣿⣾⠢⣖⣶⣦⣤⣤⣬⣤⣤⣤⣴⣶⣶⡏⠠⢃⠌⠄⠄⠄⠄⠄⠄", - "⠄⠄⠄⠄⠄⠿⠿⠟⠛⡹⠉⠛⠛⠿⠿⣿⣿⣿⣿⣿⡿⠂⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄", - "⠠⠤⠤⠄⠄⣀⠄⠄⠄⠑⠠⣤⣀⣀⣀⡘⣿⠿⠙⠻⡍⢀⡈⠂⠄⠄⠄⠄⠄⠄⠄⠄⠄", - "⠄⠄⠄⠄⠄⠄⠑⠠⣠⣴⣾⣿⣿⣿⣿⣿⣿⣇⠉⠄⠻⣿⣷⣄⡀⠄⠄⠄⠄⠄⠄⠄⠄", - "file a bug report now or be cursed with this " - "annoying image in your syslog for all time."}) + std::shared_ptr conf; + if (confFile) { - llarp::log::critical(deadlock_cat, wtf); - llarp::log::flush(); + llarp::LogInfo("Using config file: ", *confFile); + conf = std::make_shared(confFile->parent_path()); + } + else + { + conf = std::make_shared(llarp::GetDefaultDataDir()); + } + if (not conf->Load(confFile, opts.isSNode)) + { + llarp::LogError("failed to parse configuration"); + exit_code.set_value(1); + return; } - llarp::sys::service_manager->failed(); - std::abort(); - } - } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); - main_thread.join(); + ctx = std::make_shared(); + ctx->Configure(std::move(conf)); - int code = 0; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); - try - { - code = ftr.get(); - } - catch (const std::exception& e) - { - std::cerr << "main thread threw exception: " << e.what() << std::endl; - code = 1; - } - catch (...) - { - std::cerr << "main thread threw non-standard exception" << std::endl; - code = 2; - } +#ifndef _WIN32 + signal(SIGHUP, handle_signal); + signal(SIGUSR1, handle_signal); +#endif - llarp::log::flush(); - llarp::sys::service_manager->stopped(); - if (ctx) - { - ctx.reset(); + try + { + ctx->Setup(opts); + } + catch (llarp::util::bind_socket_error& ex) + { + llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what())); + exit_code.set_value(1); + return; + } + catch (std::exception& ex) + { + llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what())); + exit_code.set_value(1); + return; + } + llarp::util::SetThreadName("llarp-mainloop"); + + auto result = ctx->Run(opts); + exit_code.set_value(result); + } + catch (std::exception& e) + { + llarp::LogError("Fatal: caught exception while running: ", e.what()); + exit_code.set_exception(std::current_exception()); + } + catch (...) + { + llarp::LogError("Fatal: caught non-standard exception while running"); + exit_code.set_exception(std::current_exception()); + } } - return code; -} -#ifdef _WIN32 +} // namespace -VOID FAR PASCAL -SvcCtrlHandler(DWORD dwCtrl) +int +main(int argc, char* argv[]) { - // Handle the requested control code. + // Set up a default, stderr logging for very early logging; we'll replace this later once we read + // the desired log info from config. + llarp::log::add_sink(llarp::log::Type::Print, "stderr"); + llarp::log::reset_level(llarp::log::Level::info); - switch (dwCtrl) - { - case SERVICE_CONTROL_STOP: - // tell service we are stopping - llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP"); - llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping); - handle_signal(SIGINT); - return; + llarp::logRingBuffer = std::make_shared(100); + llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); - case SERVICE_CONTROL_INTERROGATE: - // report status - llarp::log::debug(logcat, "Got win32 service interrogate signal"); - llarp::sys::service_manager->report_changed_state(); - return; +#ifndef _WIN32 + return lokinet_main(argc, argv); +#else + SERVICE_TABLE_ENTRY DispatchTable[] = { + {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; - default: - llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl); - break; - } -} + // Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't + // return until the service enters STOPPED state. + if (StartServiceCtrlDispatcher(DispatchTable)) + return 0; -// The win32 daemon entry point is where we go when invoked as a windows service; we do the required -// service dance and then pretend we were invoked via main(). -VOID FAR PASCAL -win32_daemon_entry(DWORD, LPTSTR* argv) -{ - // Register the handler function for the service - auto* svc = dynamic_cast(llarp::sys::service_manager); - svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler); + auto error = GetLastError(); - if (svc->handle == nullptr) + // We'll get this error if not invoked as a service, which is fine: we can just run directly + if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { - llarp::LogError("failed to register daemon control handler"); - return; + llarp::sys::service_manager->disable(); + return lokinet_main(argc, argv); + } + else + { + llarp::log::critical( + logcat, "Error launching service: {}", std::system_category().message(error)); + return 1; } - - // we hard code the args to lokinet_main. - // we yoink argv[0] (lokinet.exe path) and pass in the new args. - std::array args = { - reinterpret_cast(argv[0]), - reinterpret_cast(strdup("c:\\programdata\\lokinet\\lokinet.ini")), - reinterpret_cast(0)}; - lokinet_main(args.size() - 1, args.data()); -} #endif +} \ No newline at end of file diff --git a/external/CLI11 b/external/CLI11 new file mode 160000 index 000000000..4c7c8ddc4 --- /dev/null +++ b/external/CLI11 @@ -0,0 +1 @@ +Subproject commit 4c7c8ddc45d2ef74584e5cd945f7a4d27c987748 diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index d235c1ec2..9b33431f9 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -26,7 +26,6 @@ if(SUBMODULE_CHECK) message(STATUS "Checking submodules") check_submodule(nlohmann) - check_submodule(cxxopts) check_submodule(ghc-filesystem) check_submodule(oxen-logging fmt spdlog) check_submodule(pybind11) @@ -36,6 +35,7 @@ if(SUBMODULE_CHECK) check_submodule(uvw) check_submodule(cpr) check_submodule(ngtcp2) + check_submodule(CLI11) endif() endif() @@ -78,7 +78,7 @@ if(WITH_HIVE) add_subdirectory(pybind11 EXCLUDE_FROM_ALL) endif() -add_subdirectory(cxxopts EXCLUDE_FROM_ALL) +system_or_submodule(CLI11 CLI11 CLI11>=2.2.0 CLI11) if(WITH_PEERSTATS) add_library(sqlite_orm INTERFACE) diff --git a/external/cxxopts b/external/cxxopts deleted file mode 160000 index c74846a89..000000000 --- a/external/cxxopts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c74846a891b3cc3bfa992d588b1295f528d43039 diff --git a/external/readme.md b/external/readme.md index 681e6e005..528495fd7 100644 --- a/external/readme.md +++ b/external/readme.md @@ -1,7 +1,7 @@ directory for git submodules +* CLI11: cli argument parser * cpr: curl for people, used by lokinet-bootstrap toolchain (to be removed) -* cxxopts: cli argument parser (to be removed) * ghc-filesystem: `std::filesystem` shim lib for older platforms (like macos) * ngtcp2: quic implementation * nlohmann: json parser @@ -10,4 +10,4 @@ directory for git submodules * oxen-mq: zmq wrapper library for threadpool and rpc * pybind11: for pybind modules * sqlite_orm: for peer stats db -* uvw: libuv header only library for main event loop +* uvw: libuv header only library for main event loop \ No newline at end of file diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index ce7eb7142..bb4b3bef2 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -278,7 +278,7 @@ endif() target_link_libraries(lokinet-amalgum PRIVATE libunbound) target_link_libraries(lokinet-amalgum PUBLIC - cxxopts + CLI11 oxenc::oxenc lokinet-platform lokinet-config diff --git a/llarp/context.cpp b/llarp/context.cpp index 1901afc6e..990419a10 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -14,7 +14,10 @@ #include -#include +#include +#include +#include + #include #include