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