From dc7f3cee229ebc13f776c50f042d8d0766e6aa67 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 5 Jan 2023 13:05:18 -0800 Subject: [PATCH 1/2] 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 From 3d0fe8ecb716b5e989aca340543756d2eb1e55f2 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 6 Jan 2023 16:28:22 -0400 Subject: [PATCH 2/2] Rename 'package' target on macos to 'dmg' to avoid cpack conflict CLI11 uses cpack, which makes us fail to configure on macos because of the 'package' target. Renaming it to 'dmg' should avoid the conflict. --- cmake/macos.cmake | 2 +- contrib/mac.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/macos.cmake b/cmake/macos.cmake index 7510ba1fe..c72248359 100644 --- a/cmake/macos.cmake +++ b/cmake/macos.cmake @@ -150,7 +150,7 @@ if(BUILD_PACKAGE) "${lokinet_installer}" COMMAND ./seticon lokinet.icns "${lokinet_installer}.dmg" ) - add_custom_target(package DEPENDS "${lokinet_installer}.dmg") + add_custom_target(dmg DEPENDS "${lokinet_installer}.dmg") endif() diff --git a/contrib/mac.sh b/contrib/mac.sh index 0a51fe672..d6217d082 100755 --- a/contrib/mac.sh +++ b/contrib/mac.sh @@ -19,7 +19,7 @@ fi cd build-mac rm -rf Lokinet\ * -ninja -j${JOBS:-1} package +ninja -j${JOBS:-1} dmg cd .. echo -e "Build complete, your app is here:\n"