/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 |