exec.cpp raw

   1  // Copyright (c) 2025-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <util/exec.h>
   6  
   7  #include <util/fs.h>
   8  #ifdef WIN32
   9  #include <util/subprocess.h>
  10  #endif
  11  
  12  #include <cstdlib>
  13  #include <string>
  14  #include <system_error>
  15  
  16  #ifdef WIN32
  17  #include <codecvt>
  18  #include <locale>
  19  #include <process.h>
  20  #include <windows.h>
  21  #else
  22  #include <unistd.h>
  23  #endif
  24  
  25  namespace util {
  26  int ExecVp(const char* file, char* const argv[])
  27  {
  28  #ifndef WIN32
  29      return execvp(file, argv);
  30  #else
  31      std::vector<std::wstring> escaped_args;
  32      std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
  33      for (char* const* arg_ptr{argv}; *arg_ptr; ++arg_ptr) {
  34          subprocess::util::quote_argument(converter.from_bytes(*arg_ptr), escaped_args.emplace_back(), false);
  35      }
  36  
  37      std::vector<const wchar_t*> new_argv;
  38      new_argv.reserve(escaped_args.size() + 1);
  39      for (const auto& s : escaped_args) new_argv.push_back(s.c_str());
  40      new_argv.push_back(nullptr);
  41      return _wexecvp(converter.from_bytes(file).c_str(), new_argv.data());
  42  #endif
  43  }
  44  
  45  fs::path GetExePath(std::string_view argv0)
  46  {
  47      // Try to figure out where executable is located. This does a simplified
  48      // search that won't work perfectly on every platform and doesn't need to,
  49      // as it is only currently being used in a convenience wrapper binary to try
  50      // to prioritize locally built or installed executables over system
  51      // executables.
  52      const fs::path argv0_path{fs::PathFromString(std::string{argv0})};
  53      fs::path path{argv0_path};
  54      std::error_code ec;
  55  #ifndef WIN32
  56      // If argv0 doesn't contain a path separator, it was invoked from the system
  57      // PATH and can be searched for there.
  58      if (!argv0_path.has_parent_path()) {
  59          if (const char* path_env = std::getenv("PATH")) {
  60              size_t start{0}, end{0};
  61              for (std::string_view paths{path_env}; end != std::string_view::npos; start = end + 1) {
  62                  end = paths.find(':', start);
  63                  fs::path candidate = fs::path(paths.substr(start, end - start)) / argv0_path;
  64                  if (fs::is_regular_file(candidate, ec)) {
  65                      path = candidate;
  66                      break;
  67                  }
  68              }
  69          }
  70      }
  71  #else
  72      wchar_t module_path[MAX_PATH];
  73      if (GetModuleFileNameW(nullptr, module_path, MAX_PATH) > 0) {
  74          path = fs::path{module_path};
  75      }
  76  #endif
  77      return path;
  78  }
  79  
  80  } // namespace util
  81