Bitcoin Core  31.0.0
P2P Digital Currency
process.cpp
Go to the documentation of this file.
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 
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  {
36  return mp::SpawnProcess(pid, [&](int fd) {
37  fs::path path = argv0_path;
38  path.remove_filename();
39  path /= fs::PathFromString(new_exe_name);
40  return std::vector<std::string>{fs::PathToString(path), "-ipcfd", strprintf("%i", fd)};
41  });
42  }
43  int waitSpawned(int pid) override { return mp::WaitProcess(pid); }
44  bool checkSpawned(int argc, char* argv[], int& fd) override
45  {
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  if (argc != 3 || strcmp(argv[1], "-ipcfd") != 0) {
50  return false;
51  }
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  const auto maybe_fd{ToIntegral<int32_t>(argv[2])};
59  if (!maybe_fd) {
60  throw std::runtime_error(strprintf("Invalid -ipcfd number '%s'", argv[2]));
61  }
62  fd = *maybe_fd;
63  return true;
64  }
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 {
77  if (address == "unix" || address.starts_with("unix:")) {
78  fs::path path;
79  if (address.size() <= 5) {
80  path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-")));
81  } else {
82  path = data_dir / fs::PathFromString(address.substr(5));
83  }
84  std::string path_str = fs::PathToString(path);
85  address = strprintf("unix:%s", path_str);
86  if (path_str.size() >= sizeof(addr.sun_path)) {
87  error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path)));
88  return false;
89  }
90  memset(&addr, 0, sizeof(addr));
91  addr.sun_family = AF_UNIX;
92  strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1);
93  return true;
94  }
95 
96  error = strprintf("Unrecognized address '%s'", address);
97  return false;
98 }
99 
100 int ProcessImpl::connect(const fs::path& data_dir,
101  const std::string& dest_exe_name,
102  std::string& address)
103 {
104  struct sockaddr_un addr;
105  std::string error;
106  if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) {
107  throw std::invalid_argument(error);
108  }
109 
110  int fd;
111  if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) {
112  throw std::system_error(errno, std::system_category());
113  }
114  if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
115  return fd;
116  }
117  int connect_error = errno;
118  if (::close(fd) != 0) {
119  LogWarning("Error closing file descriptor %i '%s': %s", fd, address, SysErrorString(errno));
120  }
121  throw std::system_error(connect_error, std::system_category());
122 }
123 
124 int ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address)
125 {
126  struct sockaddr_un addr;
127  std::string error;
128  if (!ParseAddress(address, data_dir, exe_name, addr, error)) {
129  throw std::invalid_argument(error);
130  }
131 
132  if (addr.sun_family == AF_UNIX) {
133  fs::path path = addr.sun_path;
134  if (path.has_parent_path()) fs::create_directories(path.parent_path());
135  if (fs::symlink_status(path).type() == fs::file_type::socket) {
136  fs::remove(path);
137  }
138  }
139 
140  int fd;
141  if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == -1) {
142  throw std::system_error(errno, std::system_category());
143  }
144 
145  if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
146  return fd;
147  }
148  int bind_error = errno;
149  if (::close(fd) != 0) {
150  LogWarning("Error closing file descriptor %i: %s", fd, SysErrorString(errno));
151  }
152  throw std::system_error(bind_error, std::system_category());
153 }
154 } // namespace
155 
156 std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
157 } // namespace ipc
#define LogWarning(...)
Definition: log.h:96
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
Definition: string.h:182
int SpawnProcess(int &pid, FdToArgsFn &&fd_to_args)
Spawn a new process that communicates with the current process over a socket pair.
Definition: util.cpp:119
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:17
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:186
Definition: ipc.h:12
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:180
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition: process.cpp:156