Repository: bitcoin/bitcoin
File: src/util/subprocess.h (C/C++)

Code before:
    this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
    this->read_from_child_ = _fileno(this->output());

    util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
    this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
    this->err_read_ = _fileno(this->error());
#else

    if (write_to_child_ != -1)  input(fdopen(write_to_child_, "wb"));
    if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
    if (err_read_ != -1)        error(fdopen(err_read_, "rb"));

    auto handles = {input(), output(), error()};

    for (auto& h : handles) {
      if (h == nullptr) continue;
      setvbuf(h, nullptr, _IONBF, BUFSIZ);
    }
  #endif
  }

  inline int Communication::send(const char* msg, size_t length)
  {
    if (stream_->input() == nullptr) return -1;
    return std::fwrite(msg, sizeof(char), length, stream_->input());
  }

  inline int Communication::send(const std::vector<char>& msg)
  {
    return send(msg.data(), msg.size());
  }

  inline std::pair<OutBuffer, ErrBuffer>
  Communication::communicate(const char* msg, size_t length)
  {
    // Optimization from subprocess.py
    // If we are using one pipe, or no pipe
    // at all, using select() or threads is unnecessary.
    auto hndls = {stream_->input(), stream_->output(), stream_->error()};
    int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
    const int len_conv = length;

    if (count >= 2) {
      OutBuffer obuf;
      ErrBuffer ebuf;
      if (stream_->input()) {
        if (msg) {
          int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
          if (wbytes < len_conv) {
            if (errno != EPIPE && errno != EINVAL) {
              throw OSError("fwrite error", errno);

Review comment (suggestion):
Nothing prevents another thread from opening a new file after all these `close()` calls have completed and before the `execvp()` call below. So I guess this is best effort attempt?

A sure way would be to use `O_CLOEXEC` when opening a file: https://www.man7.org/linux/man-pages/man2/open.2.html.

Code after:
#ifdef __linux__
        // For Linux, enumerate /proc/<pid>/fd.
        try {
          std::vector<int> fds_to_close;
          for (const auto& it : fs::directory_iterator(strprintf("/proc/%d/fd", getpid()))) {
            auto fd{ToIntegral<uint64_t>(it.path().filename().native())};
            if (!fd || *fd > std::numeric_limits<int>::max()) continue;
            if (*fd <= 2) continue;  // leave std{in,out,err} alone
            if (*fd == static_cast<uint64_t>(err_wr_pipe_)) continue;
            fds_to_close.push_back(*fd);
          }
          for (const int fd : fds_to_close) {
            close(fd);
          }
        } catch (const fs::filesystem_error &e) {
          throw OSError("/proc/<pid>/fd iteration failed", e.code().value());
        }
#else
        // On other operating systems, iterate over all file descriptor slots
        // and try to close them all.
        int max_fd = sysconf(_SC_OPEN_MAX);
        if (max_fd == -1) throw OSError("sysconf failed", errno);

        for (int i = 3; i < max_fd; i++) {
          if (i == err_wr_pipe_) continue;
          close(i);
        }
#endif
      }

      // Replace the current image with the executable
      sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());

      if (sys_ret == -1) throw OSError("execve failed", errno);

    } catch (const OSError& exp) {
      // Just write the exception message
      // TODO: Give back stack trace ?
      std::string err_msg(exp.what());
      //ATTN: Can we do something on error here ?
      util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
    }

    // Calling application would not get this
    // exit failure
    _exit (EXIT_FAILURE);
#endif
  }


  inline void Streams::setup_comm_channels()