Bitcoin Core
31.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
ipc
libmultiprocess
src
mp
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
args
ArgsManager & args
Definition
bitcoind.cpp:277
util.h
fs
Filesystem operations and types.
Definition
walletcontroller.h:36
mp
Functions to serialize / deserialize common bitcoin types.
Definition
common-types.h:57
mp::FdToArgsFn
std::function< std::vector< std::string >(int fd)> FdToArgsFn
Callback type used by SpawnProcess below.
Definition
util.h:253
mp::WaitProcess
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition
util.cpp:186
mp::ThreadName
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
Definition
util.cpp:64
mp::SpawnProcess
int SpawnProcess(int &pid, FdToArgsFn &&fd_to_args)
Definition
util.cpp:119
mp::ExecProcess
void ExecProcess(const std::vector< std::string > &args)
Definition
util.cpp:174
mp::LogEscape
std::string LogEscape(const kj::StringTree &string, size_t max_size)
Definition
util.cpp:95
Generated on
for Bitcoin Core by
1.17.0