|
4 | 4 |
|
5 | 5 | #include <ipc/process.h>
|
6 | 6 | #include <ipc/protocol.h>
|
| 7 | +#include <logging.h> |
7 | 8 | #include <mp/util.h>
|
8 | 9 | #include <tinyformat.h>
|
9 | 10 | #include <util/fs.h>
|
10 | 11 | #include <util/strencodings.h>
|
| 12 | +#include <util/syserror.h> |
11 | 13 |
|
12 | 14 | #include <cstdint>
|
13 | 15 | #include <cstdlib>
|
| 16 | +#include <errno.h> |
14 | 17 | #include <exception>
|
15 | 18 | #include <iostream>
|
16 | 19 | #include <stdexcept>
|
17 | 20 | #include <string.h>
|
18 |
| -#include <system_error> |
| 21 | +#include <sys/socket.h> |
| 22 | +#include <sys/un.h> |
19 | 23 | #include <unistd.h>
|
20 | 24 | #include <utility>
|
21 | 25 | #include <vector>
|
22 | 26 |
|
| 27 | +using util::RemovePrefixView; |
| 28 | + |
23 | 29 | namespace ipc {
|
24 | 30 | namespace {
|
25 | 31 | class ProcessImpl : public Process
|
@@ -54,7 +60,95 @@ class ProcessImpl : public Process
|
54 | 60 | }
|
55 | 61 | return true;
|
56 | 62 | }
|
| 63 | + int connect(const fs::path& data_dir, |
| 64 | + const std::string& dest_exe_name, |
| 65 | + std::string& address) override; |
| 66 | + int bind(const fs::path& data_dir, const std::string& exe_name, std::string& address) override; |
57 | 67 | };
|
| 68 | + |
| 69 | +static bool ParseAddress(std::string& address, |
| 70 | + const fs::path& data_dir, |
| 71 | + const std::string& dest_exe_name, |
| 72 | + struct sockaddr_un& addr, |
| 73 | + std::string& error) |
| 74 | +{ |
| 75 | + if (address.compare(0, 4, "unix") == 0 && (address.size() == 4 || address[4] == ':')) { |
| 76 | + fs::path path; |
| 77 | + if (address.size() <= 5) { |
| 78 | + path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-"))); |
| 79 | + } else { |
| 80 | + path = data_dir / fs::PathFromString(address.substr(5)); |
| 81 | + } |
| 82 | + std::string path_str = fs::PathToString(path); |
| 83 | + address = strprintf("unix:%s", path_str); |
| 84 | + if (path_str.size() >= sizeof(addr.sun_path)) { |
| 85 | + error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path))); |
| 86 | + return false; |
| 87 | + } |
| 88 | + memset(&addr, 0, sizeof(addr)); |
| 89 | + addr.sun_family = AF_UNIX; |
| 90 | + strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1); |
| 91 | + return true; |
| 92 | + } |
| 93 | + |
| 94 | + error = strprintf("Unrecognized address '%s'", address); |
| 95 | + return false; |
| 96 | +} |
| 97 | + |
| 98 | +int ProcessImpl::connect(const fs::path& data_dir, |
| 99 | + const std::string& dest_exe_name, |
| 100 | + std::string& address) |
| 101 | +{ |
| 102 | + struct sockaddr_un addr; |
| 103 | + std::string error; |
| 104 | + if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) { |
| 105 | + throw std::invalid_argument(error); |
| 106 | + } |
| 107 | + |
| 108 | + int fd; |
| 109 | + if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) { |
| 110 | + throw std::system_error(errno, std::system_category()); |
| 111 | + } |
| 112 | + if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { |
| 113 | + return fd; |
| 114 | + } |
| 115 | + int connect_error = errno; |
| 116 | + if (::close(fd) != 0) { |
| 117 | + LogPrintf("Error closing file descriptor %i '%s': %s\n", fd, address, SysErrorString(errno)); |
| 118 | + } |
| 119 | + throw std::system_error(connect_error, std::system_category()); |
| 120 | +} |
| 121 | + |
| 122 | +int ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address) |
| 123 | +{ |
| 124 | + struct sockaddr_un addr; |
| 125 | + std::string error; |
| 126 | + if (!ParseAddress(address, data_dir, exe_name, addr, error)) { |
| 127 | + throw std::invalid_argument(error); |
| 128 | + } |
| 129 | + |
| 130 | + if (addr.sun_family == AF_UNIX) { |
| 131 | + fs::path path = addr.sun_path; |
| 132 | + if (path.has_parent_path()) fs::create_directories(path.parent_path()); |
| 133 | + if (fs::symlink_status(path).type() == fs::file_type::socket) { |
| 134 | + fs::remove(path); |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + int fd; |
| 139 | + if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) { |
| 140 | + throw std::system_error(errno, std::system_category()); |
| 141 | + } |
| 142 | + |
| 143 | + if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { |
| 144 | + return fd; |
| 145 | + } |
| 146 | + int bind_error = errno; |
| 147 | + if (::close(fd) != 0) { |
| 148 | + LogPrintf("Error closing file descriptor %i: %s\n", fd, SysErrorString(errno)); |
| 149 | + } |
| 150 | + throw std::system_error(bind_error, std::system_category()); |
| 151 | +} |
58 | 152 | } // namespace
|
59 | 153 |
|
60 | 154 | std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
|
|
0 commit comments