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