From 5076d20fdb70a4bfafc4bdfe8293e347cb6bfa78 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 13 Feb 2025 16:00:15 -0500 Subject: [PATCH] util: Add cross-platform ExecVp and GetExePath functions These functions are just meant to serve the needs of the bitcoin wrapper executable, and are intentionally not very general purpose so they can be simple. --- src/util/CMakeLists.txt | 1 + src/util/exec.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ src/util/exec.h | 23 +++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/util/exec.cpp create mode 100644 src/util/exec.h diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 4999dbf13f0..0ae9a08eebb 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(bitcoin_util STATIC EXCLUDE_FROM_ALL bytevectorhash.cpp chaintype.cpp check.cpp + exec.cpp exception.cpp feefrac.cpp fs.cpp diff --git a/src/util/exec.cpp b/src/util/exec.cpp new file mode 100644 index 00000000000..0f13d1d4af3 --- /dev/null +++ b/src/util/exec.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2025 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#endif + +namespace util { +int ExecVp(const char* file, char* const argv[]) +{ +#ifndef WIN32 + return execvp(file, argv); +#else + std::vector escaped_args; + std::wstring_convert> converter; + for (char* const* arg_ptr{argv}; *arg_ptr; ++arg_ptr) { + subprocess::util::quote_argument(converter.from_bytes(*arg_ptr), escaped_args.emplace_back(), false); + } + + std::vector new_argv; + new_argv.reserve(escaped_args.size() + 1); + for (const auto& s : escaped_args) new_argv.push_back(s.c_str()); + new_argv.push_back(nullptr); + return _wexecvp(converter.from_bytes(file).c_str(), new_argv.data()); +#endif +} + +fs::path GetExePath(std::string_view argv0) +{ + // Try to figure out where executable is located. This does a simplified + // search that won't work perfectly on every platform and doesn't need to, + // as it is only currently being used in a convenience wrapper binary to try + // to prioritize locally built or installed executables over system + // executables. + const fs::path argv0_path{fs::PathFromString(std::string{argv0})}; + fs::path path{argv0_path}; + std::error_code ec; +#ifndef WIN32 + // If argv0 doesn't contain a path separator, it was invoked from the system + // PATH and can be searched for there. + if (!argv0_path.has_parent_path()) { + if (const char* path_env = std::getenv("PATH")) { + size_t start{0}, end{0}; + for (std::string_view paths{path_env}; end != std::string_view::npos; start = end + 1) { + end = paths.find(':', start); + fs::path candidate = fs::path(paths.substr(start, end - start)) / argv0_path; + if (fs::is_regular_file(candidate, ec)) { + path = candidate; + break; + } + } + } + } +#else + wchar_t module_path[MAX_PATH]; + if (GetModuleFileNameW(nullptr, module_path, MAX_PATH) > 0) { + path = fs::path{module_path}; + } +#endif + return path; +} + +} // namespace util diff --git a/src/util/exec.h b/src/util/exec.h new file mode 100644 index 00000000000..4c207265fa0 --- /dev/null +++ b/src/util/exec.h @@ -0,0 +1,23 @@ +// Copyright (c) 2025 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTIL_EXEC_H +#define BITCOIN_UTIL_EXEC_H + +#include + +#include + +namespace util { +//! Cross-platform wrapper for POSIX execvp function. +//! Arguments and return value are the same as for POSIX execvp, and the argv +//! array should consist of null terminated strings and be null terminated +//! itself, like the POSIX function. +int ExecVp(const char* file, char* const argv[]); +//! Return path to current executable assuming it was invoked with argv0. +//! If path could not be determined, returns an empty path. +fs::path GetExePath(std::string_view argv0); +} // namespace util + +#endif // BITCOIN_UTIL_EXEC_H