From 630c3463d89c68438d1618e414de6558217d3835 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sat, 22 Mar 2025 15:27:50 -0500 Subject: [PATCH] prevent overlapping api ports --- repertory/librepertory/include/app_config.hpp | 3 +- repertory/librepertory/src/app_config.cpp | 14 +- repertory/repertory/include/cli/mount.hpp | 215 ++++++++---------- repertory/repertory/src/ui/handlers.cpp | 3 + .../repertory_test/src/app_config_test.cpp | 2 +- web/repertory/lib/models/mount_list.dart | 14 +- 6 files changed, 119 insertions(+), 132 deletions(-) diff --git a/repertory/librepertory/include/app_config.hpp b/repertory/librepertory/include/app_config.hpp index 359eb5da..db4be5f0 100644 --- a/repertory/librepertory/include/app_config.hpp +++ b/repertory/librepertory/include/app_config.hpp @@ -43,8 +43,7 @@ public: [[nodiscard]] static auto default_remote_api_port(const provider_type &prov) -> std::uint16_t; - [[nodiscard]] static auto default_rpc_port(const provider_type &prov) - -> std::uint16_t; + [[nodiscard]] static auto default_rpc_port() -> std::uint16_t; [[nodiscard]] static auto get_provider_display_name(const provider_type &prov) -> std::string; diff --git a/repertory/librepertory/src/app_config.cpp b/repertory/librepertory/src/app_config.cpp index cff3f9ea..157c5c93 100644 --- a/repertory/librepertory/src/app_config.cpp +++ b/repertory/librepertory/src/app_config.cpp @@ -67,7 +67,7 @@ app_config::app_config(const provider_type &prov, std::string_view data_directory) : prov_(prov), api_password_(utils::generate_random_string(default_api_password_size)), - api_port_(default_rpc_port(prov)), + api_port_(default_rpc_port()), api_user_(std::string{REPERTORY}), config_changed_(false), download_timeout_secs_(default_download_timeout_secs), @@ -743,17 +743,7 @@ auto app_config::default_remote_api_port(const provider_type &prov) return PROVIDER_REMOTE_PORTS.at(static_cast(prov)); } -auto app_config::default_rpc_port(const provider_type &prov) -> std::uint16_t { - static const std::array(provider_type::unknown)> - PROVIDER_RPC_PORTS = { - 10000U, - 10010U, - 10100U, - 10002U, - }; - return PROVIDER_RPC_PORTS.at(static_cast(prov)); -} +auto app_config::default_rpc_port() -> std::uint16_t { return 10000U; } auto app_config::get_api_password() const -> std::string { return api_password_; diff --git a/repertory/repertory/include/cli/mount.hpp b/repertory/repertory/include/cli/mount.hpp index a87cdd16..6a85ea23 100644 --- a/repertory/repertory/include/cli/mount.hpp +++ b/repertory/repertory/include/cli/mount.hpp @@ -29,8 +29,6 @@ #include "types/repertory.hpp" #include "utils/cli_utils.hpp" #include "utils/com_init_wrapper.hpp" -#include "utils/file_utils.hpp" -#include "utils/string.hpp" #if defined(_WIN32) #include "drives/winfsp/remotewinfsp/remote_client.hpp" @@ -62,125 +60,112 @@ mount(std::vector args, std::string data_directory, lock_data lock(prov, unique_id); const auto res = lock.grab_lock(); if (res == lock_result::locked) { - ret = exit_code::mount_active; std::cerr << app_config::get_provider_display_name(prov) << " mount is already active" << std::endl; - } else if (res == lock_result::success) { - const auto generate_config = utils::cli::has_option( - args, utils::cli::options::generate_config_option); - if (generate_config) { - app_config config(prov, data_directory); - if (prov == provider_type::remote) { - auto cfg = config.get_remote_config(); - cfg.host_name_or_ip = remote_host; - cfg.api_port = remote_port; - config.set_remote_config(cfg); - } else if (prov == provider_type::sia && - config.get_sia_config().bucket.empty()) { - [[maybe_unused]] auto bucket = - config.set_value_by_name("SiaConfig.Bucket", unique_id); - } + return exit_code::mount_active; + } - std::cout << "Generated " << app_config::get_provider_display_name(prov) - << " Configuration" << std::endl; - std::cout << config.get_config_file_path() << std::endl; - ret = utils::file::file(config.get_config_file_path()).exists() - ? exit_code::success - : exit_code::file_creation_failed; - } else { -#if defined(_WIN32) - if (utils::cli::has_option(args, utils::cli::options::hidden_option)) { - ::ShowWindow(::GetConsoleWindow(), SW_HIDE); - } -#endif // defined(_WIN32) - auto drive_args = - utils::cli::parse_drive_options(args, prov, data_directory); - app_config config(prov, data_directory); -#if defined(_WIN32) - if (config.get_enable_mount_manager() && - not utils::is_process_elevated()) { - utils::com_init_wrapper cw; - if (not lock.set_mount_state(true, "elevating", -1)) { - std::cerr << "failed to set mount state" << std::endl; - } - lock.release(); - - mount_result = utils::run_process_elevated(args); - lock_data lock2(prov, unique_id); - if (lock2.grab_lock() == lock_result::success) { - if (not lock2.set_mount_state(false, "", -1)) { - std::cerr << "failed to set mount state" << std::endl; - } - lock2.release(); - } - - return exit_code::mount_result; - } -#endif // defined(_WIN32) - std::cout << "Initializing " - << app_config::get_provider_display_name(prov) - << (unique_id.empty() ? "" - : (prov == provider_type::remote) - ? " [" + remote_host + ':' + - std::to_string(remote_port) + ']' - : " [" + unique_id + ']') - << " Drive" << std::endl; - if (prov == provider_type::remote) { - std::uint16_t port{0U}; - if (utils::get_next_available_port(config.get_api_port(), port)) { - auto cfg = config.get_remote_config(); - cfg.host_name_or_ip = remote_host; - cfg.api_port = remote_port; - config.set_remote_config(cfg); - config.set_api_port(port); - - try { - remote_drive drive( - config, - [&config]() -> std::unique_ptr { - return std::unique_ptr( - new remote_client(config)); - }, - lock); - if (not lock.set_mount_state(true, "", -1)) { - std::cerr << "failed to set mount state" << std::endl; - } - mount_result = drive.mount(drive_args); - ret = exit_code::mount_result; - } catch (const std::exception &e) { - std::cerr << "FATAL: " << e.what() << std::endl; - ret = exit_code::startup_exception; - } - } else { - std::cerr << "FATAL: Unable to get available port" << std::endl; - ret = exit_code::startup_exception; - } - } else { - if (prov == provider_type::sia && - config.get_sia_config().bucket.empty()) { - [[maybe_unused]] auto bucket = - config.set_value_by_name("SiaConfig.Bucket", unique_id); - } - - try { - auto provider = create_provider(prov, config); - repertory_drive drive(config, lock, *provider); - if (not lock.set_mount_state(true, "", -1)) { - std::cerr << "failed to set mount state" << std::endl; - } - mount_result = drive.mount(drive_args); - ret = exit_code::mount_result; - } catch (const std::exception &e) { - std::cerr << "FATAL: " << e.what() << std::endl; - ret = exit_code::startup_exception; - } - } - } - } else { + if (res != lock_result::success) { ret = exit_code::lock_failed; } - return ret; +#if defined(_WIN32) + if (utils::cli::has_option(args, utils::cli::options::hidden_option)) { + ::ShowWindow(::GetConsoleWindow(), SW_HIDE); + } +#endif // defined(_WIN32) + + auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory); + app_config config(prov, data_directory); + { + std::uint16_t port{}; + if (not utils::get_next_available_port(config.get_api_port(), port)) { + std::cerr << "FATAL: Unable to get available port" << std::endl; + return exit_code::startup_exception; + } + + config.set_api_port(port); + } + +#if defined(_WIN32) + if (config.get_enable_mount_manager() && not utils::is_process_elevated()) { + utils::com_init_wrapper wrapper; + if (not lock.set_mount_state(true, "elevating", -1)) { + std::cerr << "failed to set mount state" << std::endl; + } + lock.release(); + + mount_result = utils::run_process_elevated(args); + lock_data prov_lock(prov, unique_id); + if (prov_lock.grab_lock() == lock_result::success) { + if (not prov_lock.set_mount_state(false, "", -1)) { + std::cerr << "failed to set mount state" << std::endl; + } + prov_lock.release(); + } + + return exit_code::mount_result; + } +#endif // defined(_WIN32) + + std::cout << "Initializing " << app_config::get_provider_display_name(prov) + << (unique_id.empty() ? "" + : (prov == provider_type::remote) + ? " [" + remote_host + ':' + std::to_string(remote_port) + + ']' + : " [" + unique_id + ']') + << " Drive" << std::endl; + if (prov == provider_type::remote) { + std::uint16_t port{}; + if (not utils::get_next_available_port(config.get_remote_config().api_port, + port)) { + std::cerr << "FATAL: Unable to get available port" << std::endl; + return exit_code::startup_exception; + } + + auto remote_cfg = config.get_remote_config(); + remote_cfg.host_name_or_ip = remote_host; + remote_cfg.api_port = remote_port; + config.set_remote_config(remote_cfg); + + try { + remote_drive drive( + config, + [&config]() -> std::unique_ptr { + return std::unique_ptr(new remote_client(config)); + }, + lock); + if (not lock.set_mount_state(true, "", -1)) { + std::cerr << "failed to set mount state" << std::endl; + } + + mount_result = drive.mount(drive_args); + return exit_code::mount_result; + } catch (const std::exception &e) { + std::cerr << "FATAL: " << e.what() << std::endl; + } + + return exit_code::startup_exception; + } + + if (prov == provider_type::sia && config.get_sia_config().bucket.empty()) { + [[maybe_unused]] auto bucket = + config.set_value_by_name("SiaConfig.Bucket", unique_id); + } + + try { + auto provider = create_provider(prov, config); + repertory_drive drive(config, lock, *provider); + if (not lock.set_mount_state(true, "", -1)) { + std::cerr << "failed to set mount state" << std::endl; + } + mount_result = drive.mount(drive_args); + return exit_code::mount_result; + } catch (const std::exception &e) { + std::cerr << "FATAL: " << e.what() << std::endl; + } + + return exit_code::startup_exception; } } // namespace repertory::cli::actions diff --git a/repertory/repertory/src/ui/handlers.cpp b/repertory/repertory/src/ui/handlers.cpp index 0de3484e..737051ba 100644 --- a/repertory/repertory/src/ui/handlers.cpp +++ b/repertory/repertory/src/ui/handlers.cpp @@ -505,6 +505,9 @@ void handlers::handle_post_mount(const httplib::Request &req, return; } + static std::mutex mount_mtx; + mutex_lock lock(mount_mtx); + launch_process(prov, name, {location}, true); config_->set_mount_location(prov, name, location); } diff --git a/repertory/repertory_test/src/app_config_test.cpp b/repertory/repertory_test/src/app_config_test.cpp index 7f394578..29a8c8f7 100644 --- a/repertory/repertory_test/src/app_config_test.cpp +++ b/repertory/repertory_test/src/app_config_test.cpp @@ -128,7 +128,7 @@ std::atomic app_config_test::idx{0U}; static void defaults_tests(const json &json_data, provider_type prov) { json json_defaults = { - {JSON_API_PORT, app_config::default_rpc_port(prov)}, + {JSON_API_PORT, app_config::default_rpc_port()}, {JSON_API_USER, std::string{REPERTORY}}, {JSON_DOWNLOAD_TIMEOUT_SECS, default_download_timeout_secs}, {JSON_DATABASE_TYPE, database_type::rocksdb}, diff --git a/web/repertory/lib/models/mount_list.dart b/web/repertory/lib/models/mount_list.dart index 2b6f2745..099ae3e4 100644 --- a/web/repertory/lib/models/mount_list.dart +++ b/web/repertory/lib/models/mount_list.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -111,10 +112,19 @@ class MountList with ChangeNotifier { Future add( String type, String name, - Map mountConfig, + Map settings, ) async { var ret = false; + var apiPort = settings['ApiPort'] ?? 10000; + for (var mount in _mountList) { + var port = mount.mountConfig.settings['ApiPort'] as int?; + if (port != null) { + apiPort = max(apiPort, port + 1); + } + } + settings["ApiPort"] = apiPort; + displayError() { if (constants.navigatorKey.currentContext == null) { return; @@ -129,7 +139,7 @@ class MountList with ChangeNotifier { try { final auth = await _auth.createAuth(); final map = await convertAllToString( - jsonDecode(jsonEncode(mountConfig)), + jsonDecode(jsonEncode(settings)), _auth.key, ); final response = await http.post(