Branch data Line data Source code
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 : 2 : class ProcessImpl : public Process
33 : : {
34 : : public:
35 : 0 : std::tuple<mp::ProcessId, mp::SocketId> spawn(const std::string& new_exe_name, const fs::path& argv0_path) override
36 : : {
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 : : }
44 : 0 : int waitSpawned(mp::ProcessId pid) override { return mp::WaitProcess(pid); }
45 : 0 : bool checkSpawned(int argc, char* argv[], mp::SocketId& socket) override
46 : : {
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 [ # # # # ]: 0 : if (argc != 3 || strcmp(argv[1], "-ipcfd") != 0) {
51 : : return false;
52 : : }
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 : : }
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 : 14 : 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 : : {
78 [ + + + + ]: 27 : if (address == "unix" || address.starts_with("unix:")) {
79 : 11 : fs::path path;
80 [ - + + + ]: 11 : if (address.size() <= 5) {
81 [ - + + - : 12 : path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-")));
+ - + - +
- ]
82 : : } else {
83 [ + - + - : 54 : path = data_dir / fs::PathFromString(address.substr(5));
+ - ]
84 : : }
85 [ - + ]: 11 : std::string path_str = fs::PathToString(path);
86 [ + - ]: 11 : address = strprintf("unix:%s", path_str);
87 [ - + + + ]: 11 : if (path_str.size() >= sizeof(addr.sun_path)) {
88 [ - + + - ]: 2 : error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path)));
89 : 1 : return false;
90 : : }
91 : 10 : memset(&addr, 0, sizeof(addr));
92 : 10 : addr.sun_family = AF_UNIX;
93 : 10 : strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1);
94 : 10 : return true;
95 : 22 : }
96 : :
97 : 3 : error = strprintf("Unrecognized address '%s'", address);
98 : 3 : return false;
99 : : }
100 : :
101 : 11 : mp::SocketId ProcessImpl::connect(const fs::path& data_dir,
102 : : const std::string& dest_exe_name,
103 : : std::string& address)
104 : : {
105 : 11 : struct sockaddr_un addr;
106 [ + - ]: 11 : std::string error;
107 [ + - + + ]: 11 : if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) {
108 [ + - ]: 3 : throw std::invalid_argument(error);
109 : : }
110 : :
111 : 8 : mp::SocketId fd;
112 [ - + ]: 8 : if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
113 [ # # ]: 0 : throw std::system_error(errno, std::system_category());
114 : : }
115 [ + - + + ]: 8 : if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
116 : 5 : return fd;
117 : : }
118 : 3 : int connect_error = errno;
119 [ + - - + ]: 3 : if (::close(fd) != 0) {
120 [ # # # # ]: 0 : LogWarning("Error closing file descriptor %i '%s': %s", fd, address, SysErrorString(errno));
121 : : }
122 [ + - ]: 3 : throw std::system_error(connect_error, std::system_category());
123 : 5 : }
124 : :
125 : 3 : mp::SocketId ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address)
126 : : {
127 : 3 : struct sockaddr_un addr;
128 [ + - ]: 3 : std::string error;
129 [ + - + + ]: 3 : if (!ParseAddress(address, data_dir, exe_name, addr, error)) {
130 [ + - ]: 1 : throw std::invalid_argument(error);
131 : : }
132 : :
133 [ + - ]: 2 : if (addr.sun_family == AF_UNIX) {
134 [ + - ]: 2 : fs::path path = addr.sun_path;
135 [ + - + - : 4 : if (path.has_parent_path()) fs::create_directories(path.parent_path());
+ - ]
136 [ + - - + ]: 2 : if (fs::symlink_status(path).type() == fs::file_type::socket) {
137 [ # # ]: 0 : fs::remove(path);
138 : : }
139 : 2 : }
140 : :
141 : 2 : mp::SocketId fd;
142 [ - + ]: 2 : if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
143 [ # # ]: 0 : throw std::system_error(errno, std::system_category());
144 : : }
145 : :
146 [ + - ]: 2 : if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
147 : 2 : return fd;
148 : : }
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 : : }
153 [ # # ]: 0 : throw std::system_error(bind_error, std::system_category());
154 : 2 : }
155 : : } // namespace
156 : :
157 : 2 : std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
158 : : } // namespace ipc
|