// Copyright (c) 2021 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ipc { namespace capnp { namespace { BCLog::Level ConvertIPCLogLevel(mp::Log level) { switch (level) { case mp::Log::Trace: return BCLog::Level::Trace; case mp::Log::Debug: return BCLog::Level::Debug; case mp::Log::Info: return BCLog::Level::Info; case mp::Log::Warning: return BCLog::Level::Warning; case mp::Log::Error: return BCLog::Level::Error; case mp::Log::Raise: return BCLog::Level::Error; } // no default case, so the compiler can warn about missing cases // Be conservative and assume that if MP ever adds a new log level, it // should only be shown at our most verbose level. return BCLog::Level::Trace; } mp::Log GetRequestedIPCLogLevel() { if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Trace)) return mp::Log::Trace; if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Debug)) return mp::Log::Debug; // Info, Warning, and Error are logged unconditionally return mp::Log::Info; } void IpcLogFn(mp::LogMessage message) { LogPrintLevel(BCLog::IPC, ConvertIPCLogLevel(message.level), "%s\n", message.message); if (message.level == mp::Log::Raise) throw Exception(message.message); } class CapnpProtocol : public Protocol { public: ~CapnpProtocol() noexcept(true) { m_loop_ref.reset(); if (m_loop_thread.joinable()) m_loop_thread.join(); assert(!m_loop); }; std::unique_ptr connect(int fd, const char* exe_name) override { startLoop(exe_name); return mp::ConnectStream(*m_loop, fd); } void listen(int listen_fd, const char* exe_name, interfaces::Init& init) override { startLoop(exe_name); if (::listen(listen_fd, /*backlog=*/5) != 0) { throw std::system_error(errno, std::system_category()); } mp::ListenConnections(*m_loop, listen_fd, init); } void serve(int fd, const char* exe_name, interfaces::Init& init, const std::function& ready_fn = {}) override { assert(!m_loop); mp::g_thread_context.thread_name = mp::ThreadName(exe_name); mp::LogOptions opts = { .log_fn = IpcLogFn, .log_level = GetRequestedIPCLogLevel() }; m_loop.emplace(exe_name, std::move(opts), &m_context); if (ready_fn) ready_fn(); mp::ServeStream(*m_loop, fd, init); m_parent_connection = &m_loop->m_incoming_connections.back(); m_loop->loop(); m_loop.reset(); } void disconnectIncoming() override { if (!m_loop) return; // Delete incoming connections, except the connection to a parent // process (if there is one), since a parent process should be able to // monitor and control this process, even during shutdown. m_loop->sync([&] { m_loop->m_incoming_connections.remove_if([this](mp::Connection& c) { return &c != m_parent_connection; }); }); } void addCleanup(std::type_index type, void* iface, std::function cleanup) override { mp::ProxyTypeRegister::types().at(type)(iface).cleanup_fns.emplace_back(std::move(cleanup)); } Context& context() override { return m_context; } void startLoop(const char* exe_name) { if (m_loop) return; std::promise promise; m_loop_thread = std::thread([&] { util::ThreadRename("capnp-loop"); mp::LogOptions opts = { .log_fn = IpcLogFn, .log_level = GetRequestedIPCLogLevel() }; m_loop.emplace(exe_name, std::move(opts), &m_context); m_loop_ref.emplace(*m_loop); promise.set_value(); m_loop->loop(); m_loop.reset(); }); promise.get_future().wait(); } Context m_context; std::thread m_loop_thread; //! EventLoop object which manages I/O events for all connections. std::optional m_loop; //! Reference to the same EventLoop. Increments the loop’s refcount on //! creation, decrements on destruction. The loop thread exits when the //! refcount reaches 0. Other IPC objects also hold their own EventLoopRef. std::optional m_loop_ref; //! Connection to parent, if this is a child process spawned by a parent process. mp::Connection* m_parent_connection{nullptr}; }; } // namespace std::unique_ptr MakeCapnpProtocol() { return std::make_unique(); } } // namespace capnp } // namespace ipc