/ src / ipc / libmultiprocess / src / mp / util.cpp
util.cpp
  1  // Copyright (c) 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 <mp/config.h>
  6  #include <mp/util.h>
  7  
  8  #include <cerrno>
  9  #include <cstdio>
 10  #include <filesystem>
 11  #include <iostream>
 12  #include <kj/common.h>
 13  #include <kj/string-tree.h>
 14  #include <pthread.h>
 15  #include <sstream>
 16  #include <string>
 17  #include <sys/types.h>
 18  #include <sys/resource.h>
 19  #include <sys/socket.h>
 20  #include <sys/wait.h>
 21  #include <system_error>
 22  #include <thread> // NOLINT(misc-include-cleaner) // IWYU pragma: keep
 23  #include <unistd.h>
 24  #include <utility>
 25  #include <vector>
 26  
 27  #ifdef __linux__
 28  #include <sys/syscall.h>
 29  #endif
 30  
 31  #ifdef HAVE_PTHREAD_GETTHREADID_NP
 32  #include <pthread_np.h>
 33  #endif // HAVE_PTHREAD_GETTHREADID_NP
 34  
 35  namespace fs = std::filesystem;
 36  
 37  namespace mp {
 38  namespace {
 39  
 40  std::vector<char*> MakeArgv(const std::vector<std::string>& args)
 41  {
 42      std::vector<char*> argv;
 43      argv.reserve(args.size() + 1);
 44      for (const auto& arg : args) {
 45          argv.push_back(const_cast<char*>(arg.c_str()));
 46      }
 47      argv.push_back(nullptr);
 48      return argv;
 49  }
 50  
 51  //! Return highest possible file descriptor.
 52  size_t MaxFd()
 53  {
 54      struct rlimit nofile;
 55      if (getrlimit(RLIMIT_NOFILE, &nofile) == 0) {
 56          return nofile.rlim_cur - 1;
 57      } else {
 58          return 1023;
 59      }
 60  }
 61  
 62  } // namespace
 63  
 64  std::string ThreadName(const char* exe_name)
 65  {
 66      char thread_name[16] = {0};
 67  #ifdef HAVE_PTHREAD_GETNAME_NP
 68      pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name));
 69  #endif // HAVE_PTHREAD_GETNAME_NP
 70  
 71      std::ostringstream buffer;
 72      buffer << (exe_name ? exe_name : "") << "-" << getpid() << "/";
 73  
 74      if (thread_name[0] != '\0') {
 75          buffer << thread_name << "-";
 76      }
 77  
 78      // Prefer platform specific thread ids over the standard C++11 ones because
 79      // the former are shorter and are the same as what gdb prints "LWP ...".
 80  #ifdef __linux__
 81      buffer << syscall(SYS_gettid);
 82  #elif defined(HAVE_PTHREAD_THREADID_NP)
 83      uint64_t tid = 0;
 84      pthread_threadid_np(NULL, &tid);
 85      buffer << tid;
 86  #elif defined(HAVE_PTHREAD_GETTHREADID_NP)
 87      buffer << pthread_getthreadid_np();
 88  #else
 89      buffer << std::this_thread::get_id();
 90  #endif
 91  
 92      return std::move(buffer).str();
 93  }
 94  
 95  std::string LogEscape(const kj::StringTree& string, size_t max_size)
 96  {
 97      std::string result;
 98      string.visit([&](const kj::ArrayPtr<const char>& piece) {
 99          if (result.size() > max_size) return;
100          for (const char c : piece) {
101              if (c == '\\') {
102                  result.append("\\\\");
103              } else if (c < 0x20 || c > 0x7e) {
104                  char escape[4];
105                  snprintf(escape, 4, "\\%02x", c);
106                  result.append(escape);
107              } else {
108                  result.push_back(c);
109              }
110              if (result.size() > max_size) {
111                  result += "...";
112                  break;
113              }
114          }
115      });
116      return result;
117  }
118  
119  int SpawnProcess(int& pid, FdToArgsFn&& fd_to_args)
120  {
121      int fds[2];
122      if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
123          throw std::system_error(errno, std::system_category(), "socketpair");
124      }
125  
126      // Evaluate the callback and build the argv array before forking.
127      //
128      // The parent process may be multi-threaded and holding internal library
129      // locks at fork time. In that case, running code that allocates memory or
130      // takes locks in the child between fork() and exec() can deadlock
131      // indefinitely. Precomputing arguments in the parent avoids this.
132      const std::vector<std::string> args{fd_to_args(fds[0])};
133      const std::vector<char*> argv{MakeArgv(args)};
134  
135      pid = fork();
136      if (pid == -1) {
137          throw std::system_error(errno, std::system_category(), "fork");
138      }
139      // Parent process closes the descriptor for socket 0, child closes the
140      // descriptor for socket 1. On failure, the parent throws, but the child
141      // must _exit(126) (post-fork child must not throw).
142      if (close(fds[pid ? 0 : 1]) != 0) {
143          if (pid) {
144              (void)close(fds[1]);
145              throw std::system_error(errno, std::system_category(), "close");
146          }
147          static constexpr char msg[] = "SpawnProcess(child): close(fds[1]) failed\n";
148          const ssize_t writeResult = ::write(STDERR_FILENO, msg, sizeof(msg) - 1);
149          (void)writeResult;
150          _exit(126);
151      }
152  
153      if (!pid) {
154          // Child process must close all potentially open descriptors, except
155          // socket 0. Do not throw, allocate, or do non-fork-safe work here.
156          const int maxFd = MaxFd();
157          for (int fd = 3; fd < maxFd; ++fd) {
158              if (fd != fds[0]) {
159                  close(fd);
160              }
161          }
162  
163          execvp(argv[0], argv.data());
164          // NOTE: perror() is not async-signal-safe; calling it here in a
165          // post-fork child may deadlock in multithreaded parents.
166          // TODO: Report errors to the parent via a pipe (e.g. write errno)
167          // so callers can get diagnostics without relying on perror().
168          perror("execvp failed");
169          _exit(127);
170      }
171      return fds[1];
172  }
173  
174  void ExecProcess(const std::vector<std::string>& args)
175  {
176      const std::vector<char*> argv{MakeArgv(args)};
177      if (execvp(argv[0], argv.data()) != 0) {
178          perror("execvp failed");
179          if (errno == ENOENT && !args.empty()) {
180              std::cerr << "Missing executable: " << fs::weakly_canonical(args.front()) << '\n';
181          }
182          _exit(1);
183      }
184  }
185  
186  int WaitProcess(int pid)
187  {
188      int status;
189      if (::waitpid(pid, &status, /*options=*/0) != pid) {
190          throw std::system_error(errno, std::system_category(), "waitpid");
191      }
192      return status;
193  }
194  
195  } // namespace mp