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