Repository: bitcoin/bitcoin
File: src/bitcoin.cpp (C++)

Code before:
                    }
                } else if (arg[i] == '"') {
                    // Escape double quotes with a backslash
                    escaped.push_back('\\');
                    escaped.push_back('"');
                } else {
                    escaped.push_back(arg[i]);
                }
            }
            escaped.push_back('"'); // End with a quote
            escaped_args.emplace_back(std::move(escaped));
            new_argv.push_back((char *)escaped_args.back().c_str());
        }
    }
    new_argv.push_back(nullptr);
    return _execvp(file, new_argv.data());
#endif
}

//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
//! using src, bin and libexec directory paths relative to this executable, where
//! the path to this executable is specified in `wrapper_argv0`.
//!
//! @param args Command line arguments to execute, where first argument should
//!             be a relative path to a bitcoind, bitcoin-qt or other executable
//!             that will be located on the PATH or relative to this executable.
//!
//! @param wrapper_argv0 String containing first command line argument passed to
//!                      main() to run the current executable. This is used to
//!                      help determine the path to the current executable and
//!                      how to look for new executables.
//
//! @note This function doesn't currently print anything but can be debugged
//! from the command line using strace or dtrace like:
//!
//!     strace -e trace=execve -s 10000 build/src/bitcoin ...
//!     dtrace -n 'proc:::exec-success { trace(curpsinfo->pr_psargs); }' -c ...
void ExecCommand(const std::vector<const char*>& args, std::string_view wrapper_argv0)
{
    // Construct argument string for execvp
    std::vector<const char*> exec_args{args};
    exec_args.emplace_back(nullptr);

    // Try to call execvp with given exe path.
    auto try_exec = [&](fs::path exe_path, bool allow_notfound = true) {
        std::string exe_path_str{fs::PathToString(exe_path)};
        exec_args[0] = exe_path_str.c_str();
        if (ExecVp(exec_args[0], (char*const*)exec_args.data()) == -1) {
            if (allow_notfound && errno == ENOENT) return false;
            throw std::system_error(errno, std::system_category(), strprintf("execvp failed to execute '%s'", exec_args[0]));
        }

Review comment (suggestion):
```suggestion
//!             that will be located on the PATH or relative to wrapper_argv0.
```

Code after:
    std::error_code ec;
    fs::path wrapper_dir{fs::weakly_canonical(wrapper_path, ec)};
    if (wrapper_dir.empty()) wrapper_dir = wrapper_path; // Restore previous path if weakly_canonical failed.
    wrapper_dir = wrapper_dir.parent_path();

    // Get path of the executable to be invoked.
    const fs::path arg0{fs::PathFromString(args[0])};

    // Decide whether to fall back to the operating system to search for the
    // specified executable. Avoid doing this if it looks like the wrapper
    // executable was invoked by path, rather than by search, to avoid
    // unintentionally launching system executables in a local build.
    // (https://github.com/bitcoin/bitcoin/pull/31375#discussion_r1861814807)
    const bool fallback_os_search{!fs::PathFromString(std::string{wrapper_argv0}).has_parent_path()};

    // If wrapper is installed in a bin/ directory, look for target executable
    // in libexec/
    (wrapper_dir.filename() == "bin" && try_exec(fs::path{wrapper_dir.parent_path()} / "libexec" / arg0.filename())) ||
#ifdef WIN32
    // Otherwise check the "daemon" subdirectory in a windows install.
    (!wrapper_dir.empty() && try_exec(wrapper_dir / "daemon" / arg0.filename())) ||
#endif
    // Otherwise look for target executable next to current wrapper
    (!wrapper_dir.empty() && try_exec(wrapper_dir / arg0.filename(), fallback_os_search)) ||
    // Otherwise just look on the system path.
    (fallback_os_search && try_exec(arg0.filename(), false));
}