From 3f4b88a2c62eb8cd5cb0502d7904cf7ad85c74a7 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 30 Jul 2025 15:10:25 -0500 Subject: [PATCH] [bug] UI is unable to launch repertory.exe on Windows when absolute path contains spaces #55 --- CHANGELOG.md | 5 ++ config.sh | 6 +- repertory/repertory/src/ui/handlers.cpp | 110 ++++++++++++++---------- 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3674096..9ab724c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## v2.0.7-release + +### Issues +* \#55 [bug] UI is unable to launch `repertory.exe` on Windows when absolute path contains spaces + ## v2.0.6-release ### Issues diff --git a/config.sh b/config.sh index da28760a..1d60f196 100755 --- a/config.sh +++ b/config.sh @@ -10,9 +10,9 @@ PROJECT_DESC="Mount utility for Sia and S3" PROJECT_MAJOR_VERSION=2 PROJECT_MINOR_VERSION=0 -PROJECT_REVISION_VERSION=6 -PROJECT_RELEASE_NUM=1 -PROJECT_RELEASE_ITER=release +PROJECT_REVISION_VERSION=7 +PROJECT_RELEASE_NUM=0 +PROJECT_RELEASE_ITER=rc PROJECT_APP_LIST=(${PROJECT_NAME}) diff --git a/repertory/repertory/src/ui/handlers.cpp b/repertory/repertory/src/ui/handlers.cpp index b5e2edf7..47fe87d0 100644 --- a/repertory/repertory/src/ui/handlers.cpp +++ b/repertory/repertory/src/ui/handlers.cpp @@ -38,6 +38,7 @@ #include #include #include +namespace bp1 = boost::process::v1; namespace { [[nodiscard]] auto decrypt(std::string_view data, std::string_view password) @@ -113,6 +114,41 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) server_(server) { REPERTORY_USES_FUNCTION_NAME(); +#if defined(_WIN32) + if (config_->get_hidden()) { + ::ShowWindow(::GetConsoleWindow(), SW_HIDE); + } +#endif // defined(_WIN32) + + if (not config_->get_launch_only()) { +#if defined(_WIN32) + system( + fmt::format( + R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")", + config_->get_api_port()) + .c_str()); +#elif defined(__linux__) + system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")", + config_->get_api_port()) + .c_str()); +#else // error + build fails here +#endif + } + + std::uint16_t port{}; + if (not utils::get_next_available_port(config_->get_api_port(), port)) { + fmt::println("failed to detect if port is available|{}", + config_->get_api_port()); + return; + } + + if (port != config_->get_api_port()) { + fmt::println("failed to listen on port|{}|next available|{}", + config_->get_api_port(), port); + return; + } + server_->set_socket_options([](auto &&sock) { #if defined(_WIN32) int enable{1}; @@ -244,32 +280,6 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server) #endif // !defined(_WIN32) std::signal(SIGTERM, quit_handler); -#if defined(_WIN32) - system(fmt::format( - R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")", - config_->get_api_port()) - .c_str()); -#elif defined(__linux__) - system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")", - config_->get_api_port()) - .c_str()); -#else // error - build fails here -#endif - - std::uint16_t port{}; - if (not utils::get_next_available_port(config_->get_api_port(), port)) { - fmt::println("failed to detect if port is available|{}", - config_->get_api_port()); - return; - } - - if (port != config_->get_api_port()) { - fmt::println("failed to listen on port|{}|next available|{}", - config_->get_api_port(), port); - return; - } - event_system::instance().start(); nonce_thread_ = @@ -711,19 +721,37 @@ auto handlers::launch_process(provider_type prov, std::string_view name, recur_mutex_lock inst_lock(inst_mtx); if (background) { #if defined(_WIN32) - std::array path{}; - ::GetSystemDirectoryA(path.data(), path.size()); + args.insert(args.begin(), "--hidden"); - args.insert(args.begin(), utils::path::combine(path.data(), {"cmd.exe"})); - args.insert(std::next(args.begin()), "/c"); - args.insert(std::next(args.begin(), 2U), "start"); - args.insert(std::next(args.begin(), 3U), ""); - args.insert(std::next(args.begin(), 4U), "/MIN"); - args.insert(std::next(args.begin(), 5U), repertory_binary_); + auto cmdline = fmt::format("{}{}{}", '"', repertory_binary_, '"'); + for (const auto &arg : args) { + cmdline += " "; + if (arg.find_first_of(" \t") != std::string::npos) { + cmdline += fmt::format("{}{}{}", '"', arg, '"'); + } else { + cmdline += arg; + } + } + + STARTUPINFOA start_info{}; + start_info.cb = sizeof(start_info); + start_info.dwFlags = STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_SHOWMINNOACTIVE; + + PROCESS_INFORMATION proc_info{}; + auto result = ::CreateProcessA( + nullptr, &cmdline[0U], nullptr, nullptr, FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, nullptr, + utils::path::get_parent_path(repertory_binary_).c_str(), &start_info, + &proc_info); + + if (result) { + ::CloseHandle(proc_info.hProcess); + ::CloseHandle(proc_info.hThread); + } #else // !defined(_WIN32) - args.insert(args.begin(), "-f"); args.insert(args.begin(), repertory_binary_); -#endif // defined(_WIN32) + args.insert(std::next(args.begin()), "-f"); std::vector exec_args; exec_args.reserve(args.size() + 1U); @@ -732,10 +760,6 @@ auto handlers::launch_process(provider_type prov, std::string_view name, } exec_args.push_back(nullptr); -#if defined(_WIN32) - _spawnv(_P_DETACH, exec_args.at(0U), - const_cast(exec_args.data())); -#else // !defined(_WIN32) auto pid = fork(); if (pid < 0) { exit(1); @@ -769,10 +793,8 @@ auto handlers::launch_process(provider_type prov, std::string_view name, return {}; } - boost::process::v1::ipstream out; - boost::process::v1::child proc(repertory_binary_, - boost::process::v1::args(args), - boost::process::v1::std_out > out); + bp1::ipstream out; + bp1::child proc(repertory_binary_, bp1::args(args), bp1::std_out > out); std::string data; std::string line;