Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/ipc/process.cpp
Line
Count
Source
1
// Copyright (c) 2021-present 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 <ipc/process.h>
6
#include <ipc/protocol.h>
7
#include <logging.h>
8
#include <mp/util.h>
9
#include <tinyformat.h>
10
#include <util/fs.h>
11
#include <util/strencodings.h>
12
#include <util/syserror.h>
13
14
#include <cstdint>
15
#include <cstdlib>
16
#include <cstring>
17
#include <cerrno>
18
#include <exception>
19
#include <iostream>
20
#include <stdexcept>
21
#include <sys/socket.h>
22
#include <sys/un.h>
23
#include <unistd.h>
24
#include <utility>
25
#include <vector>
26
27
using util::RemovePrefixView;
28
29
namespace ipc {
30
namespace {
31
class ProcessImpl : public Process
32
{
33
public:
34
    int spawn(const std::string& new_exe_name, const fs::path& argv0_path, int& pid) override
35
0
    {
36
0
        return mp::SpawnProcess(pid, [&](int fd) {
37
0
            fs::path path = argv0_path;
38
0
            path.remove_filename();
39
0
            path /= fs::PathFromString(new_exe_name);
40
0
            return std::vector<std::string>{fs::PathToString(path), "-ipcfd", strprintf("%i", fd)};
41
0
        });
42
0
    }
43
0
    int waitSpawned(int pid) override { return mp::WaitProcess(pid); }
44
    bool checkSpawned(int argc, char* argv[], int& fd) override
45
5
    {
46
        // If this process was not started with a single -ipcfd argument, it is
47
        // not a process spawned by the spawn() call above, so return false and
48
        // do not try to serve requests.
49
5
        if (argc != 3 || strcmp(argv[1], "-ipcfd") != 0) {
50
5
            return false;
51
5
        }
52
        // If a single -ipcfd argument was provided, return true and get the
53
        // file descriptor so Protocol::serve() can be called to handle
54
        // requests from the parent process. The -ipcfd argument is not valid
55
        // in combination with other arguments because the parent process
56
        // should be able to control the child process through the IPC protocol
57
        // without passing information out of band.
58
0
        const auto maybe_fd{ToIntegral<int32_t>(argv[2])};
59
0
        if (!maybe_fd) {
60
0
            throw std::runtime_error(strprintf("Invalid -ipcfd number '%s'", argv[2]));
61
0
        }
62
0
        fd = *maybe_fd;
63
0
        return true;
64
0
    }
65
    int connect(const fs::path& data_dir,
66
                const std::string& dest_exe_name,
67
                std::string& address) override;
68
    int bind(const fs::path& data_dir, const std::string& exe_name, std::string& address) override;
69
};
70
71
static bool ParseAddress(std::string& address,
72
                  const fs::path& data_dir,
73
                  const std::string& dest_exe_name,
74
                  struct sockaddr_un& addr,
75
                  std::string& error)
76
1.11k
{
77
1.11k
    if (address == "unix" || address.starts_with("unix:")) {
78
1.11k
        fs::path path;
79
1.11k
        if (address.size() <= 5) {
80
1.10k
            path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-")));
81
1.10k
        } else {
82
11
            path = data_dir / fs::PathFromString(address.substr(5));
83
11
        }
84
1.11k
        std::string path_str = fs::PathToString(path);
85
1.11k
        address = strprintf("unix:%s", path_str);
86
1.11k
        if (path_str.size() >= sizeof(addr.sun_path)) {
87
1
            error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path)));
88
1
            return false;
89
1
        }
90
1.11k
        memset(&addr, 0, sizeof(addr));
91
1.11k
        addr.sun_family = AF_UNIX;
92
1.11k
        strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1);
93
1.11k
        return true;
94
1.11k
    }
95
96
3
    error = strprintf("Unrecognized address '%s'", address);
97
3
    return false;
98
1.11k
}
99
100
int ProcessImpl::connect(const fs::path& data_dir,
101
                         const std::string& dest_exe_name,
102
                         std::string& address)
103
1.11k
{
104
1.11k
    struct sockaddr_un addr;
105
1.11k
    std::string error;
106
1.11k
    if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) {
107
3
        throw std::invalid_argument(error);
108
3
    }
109
110
1.11k
    int fd;
111
1.11k
    if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) {
112
0
        throw std::system_error(errno, std::system_category());
113
0
    }
114
1.11k
    if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
115
9
        return fd;
116
9
    }
117
1.10k
    int connect_error = errno;
118
1.10k
    if (::close(fd) != 0) {
119
0
        LogWarning("Error closing file descriptor %i '%s': %s", fd, address, SysErrorString(errno));
120
0
    }
121
1.10k
    throw std::system_error(connect_error, std::system_category());
122
1.11k
}
123
124
int ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address)
125
4
{
126
4
    struct sockaddr_un addr;
127
4
    std::string error;
128
4
    if (!ParseAddress(address, data_dir, exe_name, addr, error)) {
129
1
        throw std::invalid_argument(error);
130
1
    }
131
132
3
    if (addr.sun_family == AF_UNIX) {
133
3
        fs::path path = addr.sun_path;
134
3
        if (path.has_parent_path()) fs::create_directories(path.parent_path());
135
3
        if (fs::symlink_status(path).type() == fs::file_type::socket) {
136
0
            fs::remove(path);
137
0
        }
138
3
    }
139
140
3
    int fd;
141
3
    if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) {
142
0
        throw std::system_error(errno, std::system_category());
143
0
    }
144
145
3
    if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
146
3
        return fd;
147
3
    }
148
0
    int bind_error = errno;
149
0
    if (::close(fd) != 0) {
150
0
        LogWarning("Error closing file descriptor %i: %s", fd, SysErrorString(errno));
151
0
    }
152
0
    throw std::system_error(bind_error, std::system_category());
153
3
}
154
} // namespace
155
156
1.11k
std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
157
} // namespace ipc