interfaces.cpp raw
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 <common/args.h>
6 #include <common/system.h>
7 #include <interfaces/init.h>
8 #include <interfaces/ipc.h>
9 #include <ipc/capnp/protocol.h>
10 #include <ipc/process.h>
11 #include <ipc/protocol.h>
12 #include <tinyformat.h>
13 #include <util/fs.h>
14 #include <util/log.h>
15
16 #include <csignal>
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <functional>
21 #include <memory>
22 #include <stdexcept>
23 #include <string>
24 #include <unistd.h>
25 #include <utility>
26 #include <vector>
27
28 namespace ipc {
29 namespace {
30 #ifndef WIN32
31 std::string g_ignore_ctrl_c;
32
33 void HandleCtrlC(int)
34 {
35 // (void)! needed to suppress -Wunused-result warning from GCC
36 (void)!write(STDOUT_FILENO, g_ignore_ctrl_c.data(), g_ignore_ctrl_c.size());
37 }
38 #endif
39
40 void IgnoreCtrlC(std::string message)
41 {
42 #ifndef WIN32
43 g_ignore_ctrl_c = std::move(message);
44 struct sigaction sa{};
45 sa.sa_handler = HandleCtrlC;
46 sigemptyset(&sa.sa_mask);
47 sa.sa_flags = SA_RESTART;
48 sigaction(SIGINT, &sa, nullptr);
49 #endif
50 }
51
52 class IpcImpl : public interfaces::Ipc
53 {
54 public:
55 IpcImpl(const char* exe_name, const char* process_argv0, interfaces::Init& init)
56 : m_exe_name(exe_name), m_process_argv0(process_argv0), m_init(init),
57 m_protocol(ipc::capnp::MakeCapnpProtocol()), m_process(ipc::MakeProcess())
58 {
59 }
60 std::unique_ptr<interfaces::Init> spawnProcess(const char* new_exe_name) override
61 {
62 int pid;
63 int fd = m_process->spawn(new_exe_name, m_process_argv0, pid);
64 LogDebug(::BCLog::IPC, "Process %s pid %i launched\n", new_exe_name, pid);
65 auto init = m_protocol->connect(fd, m_exe_name);
66 Ipc::addCleanup(*init, [this, new_exe_name, pid] {
67 int status = m_process->waitSpawned(pid);
68 LogDebug(::BCLog::IPC, "Process %s pid %i exited with status %i\n", new_exe_name, pid, status);
69 });
70 return init;
71 }
72 bool startSpawnedProcess(int argc, char* argv[], int& exit_status) override
73 {
74 exit_status = EXIT_FAILURE;
75 int32_t fd = -1;
76 if (!m_process->checkSpawned(argc, argv, fd)) {
77 return false;
78 }
79 IgnoreCtrlC(strprintf("[%s] SIGINT received — waiting for parent to shut down.\n", m_exe_name));
80 m_protocol->serve(fd, m_exe_name, m_init);
81 exit_status = EXIT_SUCCESS;
82 return true;
83 }
84 std::unique_ptr<interfaces::Init> connectAddress(std::string& address) override
85 {
86 if (address.empty() || address == "0") return nullptr;
87 int fd;
88 if (address == "auto") {
89 // Treat "auto" the same as "unix" except don't treat it an as error
90 // if the connection is not accepted. Just return null so the caller
91 // can work offline without a connection, or spawn a new
92 // bitcoin-node process and connect to it.
93 address = "unix";
94 try {
95 fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
96 } catch (const std::system_error& e) {
97 // If connection type is auto and socket path isn't accepting connections, or doesn't exist, catch the error and return null;
98 if (e.code() == std::errc::connection_refused || e.code() == std::errc::no_such_file_or_directory || e.code() == std::errc::not_a_directory) {
99 return nullptr;
100 }
101 throw;
102 } catch (const std::invalid_argument&) {
103 // Catch 'Unix address path "..." exceeded maximum socket path length' error
104 return nullptr;
105 }
106 } else {
107 fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
108 }
109 return m_protocol->connect(fd, m_exe_name);
110 }
111 void listenAddress(std::string& address) override
112 {
113 int fd = m_process->bind(gArgs.GetDataDirNet(), m_exe_name, address);
114 m_protocol->listen(fd, m_exe_name, m_init);
115 }
116 void disconnectIncoming() override
117 {
118 m_protocol->disconnectIncoming();
119 }
120 void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
121 {
122 m_protocol->addCleanup(type, iface, std::move(cleanup));
123 }
124 Context& context() override { return m_protocol->context(); }
125 const char* m_exe_name;
126 const char* m_process_argv0;
127 interfaces::Init& m_init;
128 std::unique_ptr<Protocol> m_protocol;
129 std::unique_ptr<Process> m_process;
130 };
131 } // namespace
132 } // namespace ipc
133
134 namespace interfaces {
135 std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
136 {
137 return std::make_unique<ipc::IpcImpl>(exe_name, process_argv0, init);
138 }
139 } // namespace interfaces
140