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