forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprocess.cpp
More file actions
158 lines (144 loc) · 5.59 KB
/
Copy pathprocess.cpp
File metadata and controls
158 lines (144 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) 2021-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <ipc/process.h>
#include <ipc/protocol.h>
#include <mp/util.h>
#include <tinyformat.h>
#include <util/fs.h>
#include <util/log.h>
#include <util/strencodings.h>
#include <util/syserror.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <utility>
#include <vector>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
using util::RemovePrefixView;
namespace ipc {
namespace {
class ProcessImpl : public Process
{
public:
std::tuple<mp::ProcessId, mp::SocketId> spawn(const std::string& new_exe_name, const fs::path& argv0_path) override
{
return mp::SpawnProcess([&](std::string connect_info) {
fs::path path = argv0_path;
path.remove_filename();
path /= fs::PathFromString(new_exe_name);
return std::vector<std::string>{fs::PathToString(path), "-ipcfd", std::move(connect_info)};
});
}
int waitSpawned(mp::ProcessId pid) override { return mp::WaitProcess(pid); }
bool checkSpawned(int argc, char* argv[], mp::SocketId& socket) override
{
// If this process was not started with a single -ipcfd argument, it is
// not a process spawned by the spawn() call above, so return false and
// do not try to serve requests.
if (argc != 3 || strcmp(argv[1], "-ipcfd") != 0) {
return false;
}
// If a single -ipcfd argument was provided, return true and get the
// file descriptor so Protocol::serve() can be called to handle
// requests from the parent process. The -ipcfd argument is not valid
// in combination with other arguments because the parent process
// should be able to control the child process through the IPC protocol
// without passing information out of band.
try {
socket = mp::StartSpawned(argv[2]);
} catch (const std::exception& e) {
throw std::runtime_error(strprintf("Invalid -ipcfd number '%s' (%s)", argv[2], e.what()));
}
return true;
}
mp::SocketId connect(const fs::path& data_dir,
const std::string& dest_exe_name,
std::string& address) override;
mp::SocketId bind(const fs::path& data_dir, const std::string& exe_name, std::string& address) override;
};
static bool ParseAddress(std::string& address,
const fs::path& data_dir,
const std::string& dest_exe_name,
struct sockaddr_un& addr,
std::string& error)
{
if (address == "unix" || address.starts_with("unix:")) {
fs::path path;
if (address.size() <= 5) {
path = data_dir / fs::PathFromString(strprintf("%s.sock", RemovePrefixView(dest_exe_name, "bitcoin-")));
} else {
path = data_dir / fs::PathFromString(address.substr(5));
}
std::string path_str = fs::PathToString(path);
address = strprintf("unix:%s", path_str);
if (path_str.size() >= sizeof(addr.sun_path)) {
error = strprintf("Unix address path %s exceeded maximum socket path length", fs::quoted(fs::PathToString(path)));
return false;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path_str.c_str(), sizeof(addr.sun_path)-1);
return true;
}
error = strprintf("Unrecognized address '%s'", address);
return false;
}
mp::SocketId ProcessImpl::connect(const fs::path& data_dir,
const std::string& dest_exe_name,
std::string& address)
{
struct sockaddr_un addr;
std::string error;
if (!ParseAddress(address, data_dir, dest_exe_name, addr, error)) {
throw std::invalid_argument(error);
}
mp::SocketId fd;
if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
throw std::system_error(errno, std::system_category());
}
if (::connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
return fd;
}
int connect_error = errno;
if (::close(fd) != 0) {
LogWarning("Error closing file descriptor %i '%s': %s", fd, address, SysErrorString(errno));
}
throw std::system_error(connect_error, std::system_category());
}
mp::SocketId ProcessImpl::bind(const fs::path& data_dir, const std::string& exe_name, std::string& address)
{
struct sockaddr_un addr;
std::string error;
if (!ParseAddress(address, data_dir, exe_name, addr, error)) {
throw std::invalid_argument(error);
}
if (addr.sun_family == AF_UNIX) {
fs::path path = addr.sun_path;
if (path.has_parent_path()) fs::create_directories(path.parent_path());
if (fs::symlink_status(path).type() == fs::file_type::socket) {
fs::remove(path);
}
}
mp::SocketId fd;
if ((fd = ::socket(addr.sun_family, SOCK_STREAM, 0)) == mp::SocketError) {
throw std::system_error(errno, std::system_category());
}
if (::bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
return fd;
}
int bind_error = errno;
if (::close(fd) != 0) {
LogWarning("Error closing file descriptor %i: %s", fd, SysErrorString(errno));
}
throw std::system_error(bind_error, std::system_category());
}
} // namespace
std::unique_ptr<Process> MakeProcess() { return std::make_unique<ProcessImpl>(); }
} // namespace ipc