Coverage Report

Created: 2026-07-29 23:27

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