Bitcoin Core  31.0.0
P2P Digital Currency
util.cpp
Go to the documentation of this file.
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 
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(nullptr, &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, sizeof(escape), "\\%02x", static_cast<unsigned char>(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
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
Definition: util.cpp:64
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
int SpawnProcess(int &pid, FdToArgsFn &&fd_to_args)
Spawn a new process that communicates with the current process over a socket pair.
Definition: util.cpp:119
ArgsManager & args
Definition: bitcoind.cpp:277
void ExecProcess(const std::vector< std::string > &args)
Call execvp with vector args.
Definition: util.cpp:174
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:186
auto result
Definition: common-types.h:74
std::string LogEscape(const kj::StringTree &string, size_t max_size)
Escape binary string for use in log so it doesn&#39;t trigger unicode decode errors in python unit tests...
Definition: util.cpp:95
std::function< std::vector< std::string >(int fd)> FdToArgsFn
Callback type used by SpawnProcess below.
Definition: util.h:253