Bitcoin Core 29.99.0
P2P Digital Currency
process.cpp
Go to the documentation of this file.
1// Copyright (c) 2021-2022 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 <errno.h>
17#include <exception>
18#include <iostream>
19#include <stdexcept>
20#include <string.h>
21#include <sys/socket.h>
22#include <sys/un.h>
23#include <unistd.h>
24#include <utility>
25#include <vector>
26
28
29namespace ipc {
30namespace {
31class ProcessImpl : public Process
32{
33public:
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
71static 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
100int 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 LogPrintf("Error closing file descriptor %i '%s': %s\n", fd, address, SysErrorString(errno));
120 }
121 throw std::system_error(connect_error, std::system_category());
122}
123
124int 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 LogPrintf("Error closing file descriptor %i: %s\n", fd, SysErrorString(errno));
151 }
152 throw std::system_error(bind_error, std::system_category());
153}
154} // namespace
155
156std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
157} // namespace ipc
static bool create_directories(const std::filesystem::path &p)
Create directory (and if necessary its parents), unless the leaf directory already exists or is a sym...
Definition: fs.h:190
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:151
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:174
#define LogPrintf(...)
Definition: logging.h:266
Definition: ipc.h:12
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition: process.cpp:156
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:145
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:103
std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
Definition: string.h:169
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:19
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172