Coverage Report

Created: 2026-05-06 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/ipc/libmultiprocess/src/mp/util.cpp
Line
Count
Source
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
0
{
42
0
    std::vector<char*> argv;
43
0
    argv.reserve(args.size() + 1);
44
0
    for (const auto& arg : args) {
45
0
        argv.push_back(const_cast<char*>(arg.c_str()));
46
0
    }
47
0
    argv.push_back(nullptr);
48
0
    return argv;
49
0
}
50
51
//! Return highest possible file descriptor.
52
size_t MaxFd()
53
0
{
54
0
    struct rlimit nofile;
55
0
    if (getrlimit(RLIMIT_NOFILE, &nofile) == 0) {
56
0
        return nofile.rlim_cur - 1;
57
0
    } else {
58
0
        return 1023;
59
0
    }
60
0
}
61
62
} // namespace
63
64
std::string ThreadName(const char* exe_name)
65
242
{
66
242
    char thread_name[16] = {0};
67
242
#ifdef HAVE_PTHREAD_GETNAME_NP
68
242
    pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name));
69
242
#endif // HAVE_PTHREAD_GETNAME_NP
70
71
242
    std::ostringstream buffer;
72
242
    buffer << (exe_name ? exe_name : "") << "-" << getpid() << "/";
73
74
242
    if (thread_name[0] != '\0') {
75
242
        buffer << thread_name << "-";
76
242
    }
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
242
#ifdef __linux__
81
242
    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
242
    return std::move(buffer).str();
93
242
}
94
95
std::string LogEscape(const kj::StringTree& string, size_t max_size)
96
144
{
97
144
    std::string result;
98
1.01k
    string.visit([&](const kj::ArrayPtr<const char>& piece) {
99
1.01k
        if (result.size() > max_size) return;
100
8.09k
        for (const char c : piece) {
101
8.09k
            if (c == '\\') {
102
430
                result.append("\\\\");
103
7.66k
            } else if (c < 0x20 || c > 0x7e) {
104
0
                char escape[4];
105
0
                snprintf(escape, sizeof(escape), "\\%02x", static_cast<unsigned char>(c));
106
0
                result.append(escape);
107
7.66k
            } else {
108
7.66k
                result.push_back(c);
109
7.66k
            }
110
8.09k
            if (result.size() > max_size) {
111
8
                result += "...";
112
8
                break;
113
8
            }
114
8.09k
        }
115
986
    });
116
144
    return result;
117
144
}
118
119
int SpawnProcess(int& pid, FdToArgsFn&& fd_to_args)
120
0
{
121
0
    int fds[2];
122
0
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
123
0
        throw std::system_error(errno, std::system_category(), "socketpair");
124
0
    }
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
0
    const std::vector<std::string> args{fd_to_args(fds[0])};
133
0
    const std::vector<char*> argv{MakeArgv(args)};
134
135
0
    pid = fork();
136
0
    if (pid == -1) {
137
0
        throw std::system_error(errno, std::system_category(), "fork");
138
0
    }
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
0
    if (close(fds[pid ? 0 : 1]) != 0) {
143
0
        if (pid) {
144
0
            (void)close(fds[1]);
145
0
            throw std::system_error(errno, std::system_category(), "close");
146
0
        }
147
0
        static constexpr char msg[] = "SpawnProcess(child): close(fds[1]) failed\n";
148
0
        const ssize_t writeResult = ::write(STDERR_FILENO, msg, sizeof(msg) - 1);
149
0
        (void)writeResult;
150
0
        _exit(126);
151
0
    }
152
153
0
    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
0
        const int maxFd = MaxFd();
157
0
        for (int fd = 3; fd < maxFd; ++fd) {
158
0
            if (fd != fds[0]) {
159
0
                close(fd);
160
0
            }
161
0
        }
162
163
0
        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
0
        perror("execvp failed");
169
0
        _exit(127);
170
0
    }
171
0
    return fds[1];
172
0
}
173
174
void ExecProcess(const std::vector<std::string>& args)
175
0
{
176
0
    const std::vector<char*> argv{MakeArgv(args)};
177
0
    if (execvp(argv[0], argv.data()) != 0) {
178
0
        perror("execvp failed");
179
0
        if (errno == ENOENT && !args.empty()) {
180
0
            std::cerr << "Missing executable: " << fs::weakly_canonical(args.front()) << '\n';
181
0
        }
182
0
        _exit(1);
183
0
    }
184
0
}
185
186
int WaitProcess(int pid)
187
0
{
188
0
    int status;
189
0
    if (::waitpid(pid, &status, /*options=*/0) != pid) {
190
0
        throw std::system_error(errno, std::system_category(), "waitpid");
191
0
    }
192
0
    return status;
193
0
}
194
195
} // namespace mp