Branch data Line data Source code
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 <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 <logging.h>
13 : : #include <tinyformat.h>
14 : : #include <util/fs.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 : 0 : void HandleCtrlC(int)
34 : : {
35 : : // (void)! needed to suppress -Wunused-result warning from GCC
36 [ # # ]: 0 : (void)!write(STDOUT_FILENO, g_ignore_ctrl_c.data(), g_ignore_ctrl_c.size());
37 : 0 : }
38 : : #endif
39 : :
40 : 0 : void IgnoreCtrlC(std::string message)
41 : : {
42 : : #ifndef WIN32
43 : 0 : g_ignore_ctrl_c = std::move(message);
44 : 0 : struct sigaction sa{};
45 : 0 : sa.sa_handler = HandleCtrlC;
46 : 0 : sigemptyset(&sa.sa_mask);
47 : 0 : sa.sa_flags = SA_RESTART;
48 : 0 : sigaction(SIGINT, &sa, nullptr);
49 : : #endif
50 : 0 : }
51 : :
52 : : class IpcImpl : public interfaces::Ipc
53 : : {
54 : : public:
55 : 0 : IpcImpl(const char* exe_name, const char* process_argv0, interfaces::Init& init)
56 : 0 : : m_exe_name(exe_name), m_process_argv0(process_argv0), m_init(init),
57 [ # # # # ]: 0 : m_protocol(ipc::capnp::MakeCapnpProtocol()), m_process(ipc::MakeProcess())
58 : : {
59 : 0 : }
60 : 0 : std::unique_ptr<interfaces::Init> spawnProcess(const char* new_exe_name) override
61 : : {
62 : 0 : int pid;
63 [ # # # # ]: 0 : int fd = m_process->spawn(new_exe_name, m_process_argv0, pid);
64 [ # # ]: 0 : LogDebug(::BCLog::IPC, "Process %s pid %i launched\n", new_exe_name, pid);
65 : 0 : auto init = m_protocol->connect(fd, m_exe_name);
66 [ # # # # ]: 0 : Ipc::addCleanup(*init, [this, new_exe_name, pid] {
67 : 0 : int status = m_process->waitSpawned(pid);
68 [ # # ]: 0 : LogDebug(::BCLog::IPC, "Process %s pid %i exited with status %i\n", new_exe_name, pid, status);
69 : 0 : });
70 : 0 : return init;
71 : 0 : }
72 : 0 : bool startSpawnedProcess(int argc, char* argv[], int& exit_status) override
73 : : {
74 : 0 : exit_status = EXIT_FAILURE;
75 : 0 : int32_t fd = -1;
76 [ # # ]: 0 : if (!m_process->checkSpawned(argc, argv, fd)) {
77 : : return false;
78 : : }
79 : 0 : IgnoreCtrlC(strprintf("[%s] SIGINT received — waiting for parent to shut down.\n", m_exe_name));
80 [ # # ]: 0 : m_protocol->serve(fd, m_exe_name, m_init);
81 : 0 : exit_status = EXIT_SUCCESS;
82 : 0 : return true;
83 : : }
84 : 0 : std::unique_ptr<interfaces::Init> connectAddress(std::string& address) override
85 : : {
86 [ # # # # ]: 0 : if (address.empty() || address == "0") return nullptr;
87 : 0 : int fd;
88 [ # # ]: 0 : 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 : 0 : address = "unix";
94 : 0 : try {
95 [ # # # # ]: 0 : fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
96 [ - - ]: 0 : } 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 [ - - - - ]: 0 : if (e.code() == std::errc::connection_refused || e.code() == std::errc::no_such_file_or_directory) {
99 : 0 : return nullptr;
100 : : }
101 : 0 : throw;
102 : 0 : }
103 : : } else {
104 [ # # ]: 0 : fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
105 : : }
106 : 0 : return m_protocol->connect(fd, m_exe_name);
107 : : }
108 : 0 : void listenAddress(std::string& address) override
109 : : {
110 [ # # ]: 0 : int fd = m_process->bind(gArgs.GetDataDirNet(), m_exe_name, address);
111 : 0 : m_protocol->listen(fd, m_exe_name, m_init);
112 : 0 : }
113 : 0 : void disconnectIncoming() override
114 : : {
115 : 0 : m_protocol->disconnectIncoming();
116 : 0 : }
117 : 0 : void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
118 : : {
119 [ # # ]: 0 : m_protocol->addCleanup(type, iface, std::move(cleanup));
120 : 0 : }
121 : 0 : Context& context() override { return m_protocol->context(); }
122 : : const char* m_exe_name;
123 : : const char* m_process_argv0;
124 : : interfaces::Init& m_init;
125 : : std::unique_ptr<Protocol> m_protocol;
126 : : std::unique_ptr<Process> m_process;
127 : : };
128 : : } // namespace
129 : : } // namespace ipc
130 : :
131 : : namespace interfaces {
132 : 0 : std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
133 : : {
134 : 0 : return std::make_unique<ipc::IpcImpl>(exe_name, process_argv0, init);
135 : : }
136 : : } // namespace interfaces
|