Bitcoin Core 31.99.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 <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
29
30namespace ipc {
31namespace {
32class ProcessImpl : public Process
33{
34public:
35 std::tuple<mp::ProcessId, mp::SocketId> spawn(const std::string& new_exe_name, const fs::path& argv0_path) override
36 {
37 return mp::SpawnProcess([&](std::string connect_info) {
38 fs::path path = argv0_path;
39 path.remove_filename();
40 path /= fs::PathFromString(new_exe_name);
41 return std::vector<std::string>{fs::PathToString(path), "-ipcfd", std::move(connect_info)};
42 });
43 }
44 int waitSpawned(mp::ProcessId pid) override { return mp::WaitProcess(pid); }
45 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 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 try {
60 socket = mp::StartSpawned(argv[2]);
61 } catch (const std::exception& e) {
62 throw std::runtime_error(strprintf("Invalid -ipcfd number '%s' (%s)", argv[2], e.what()));
63 }
64 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
72static 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 if (address == "unix" || address.starts_with("unix:")) {
79 fs::path path;
80 if (address.size() <= 5) {
81 path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-")));
82 } else {
83 path = data_dir / fs::PathFromString(address.substr(5));
84 }
85 std::string path_str = fs::PathToString(path);
86 address = strprintf("unix:%s", path_str);
87 if (path_str.size() >= sizeof(addr.sun_path)) {
88 error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path)));
89 return false;
90 }
91 memset(&addr, 0, sizeof(addr));
92 addr.sun_family = AF_UNIX;
93 strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1);
94 return true;
95 }
96
97 error = strprintf("Unrecognized address '%s'", address);
98 return false;
99}
100
101mp::SocketId ProcessImpl::connect(const fs::path& data_dir,
102 const std::string& dest_exe_name,
103 std::string& address)
104{
105 struct sockaddr_un addr;
106 std::string error;
107 if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) {
108 throw std::invalid_argument(error);
109 }
110
111 mp::SocketId fd;
112 if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
113 throw std::system_error(errno, std::system_category());
114 }
115 if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
116 return fd;
117 }
118 int connect_error = errno;
119 if (::close(fd) != 0) {
120 LogWarning("Error closing file descriptor %i '%s': %s", fd, address, SysErrorString(errno));
121 }
122 throw std::system_error(connect_error, std::system_category());
123}
124
125mp::SocketId ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address)
126{
127 struct sockaddr_un addr;
128 std::string error;
129 if (!ParseAddress(address, data_dir, exe_name, addr, error)) {
130 throw std::invalid_argument(error);
131 }
132
133 if (addr.sun_family == AF_UNIX) {
134 fs::path path = addr.sun_path;
135 if (path.has_parent_path()) fs::create_directories(path.parent_path());
136 if (fs::symlink_status(path).type() == fs::file_type::socket) {
137 fs::remove(path);
138 }
139 }
140
141 mp::SocketId fd;
142 if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
143 throw std::system_error(errno, std::system_category());
144 }
145
146 if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
147 return fd;
148 }
149 int bind_error = errno;
150 if (::close(fd) != 0) {
151 LogWarning("Error closing file descriptor %i: %s", fd, SysErrorString(errno));
152 }
153 throw std::system_error(bind_error, std::system_category());
154}
155} // namespace
156
157std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
158} // namespace ipc
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:162
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:185
#define LogWarning(...)
Definition: log.h:126
Definition: ipc.h:13
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition: process.cpp:157
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:186
int ProcessId
Definition: util.h:26
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
int SocketId
Definition: util.h:27
SocketId StartSpawned(const std::string &connect_info)
Definition: util.h:50
constexpr SocketId SocketError
Definition: util.h:28
std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
Definition: string.h:185
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:18
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172