mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Compare commits
5 commits
0b72656ecf
...
5b38e62ccb
Author | SHA1 | Date | |
---|---|---|---|
|
5b38e62ccb | ||
|
db845b915f | ||
|
6427d6f175 | ||
|
8ca7049cea | ||
|
cf1c26a3a2 |
14 changed files with 153 additions and 25 deletions
|
@ -589,6 +589,14 @@ void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
|
|||
}
|
||||
}
|
||||
|
||||
void ArgsManager::ClearArgs()
|
||||
{
|
||||
LOCK(cs_args);
|
||||
m_settings = {};
|
||||
m_available_args.clear();
|
||||
m_network_only_args.clear();
|
||||
}
|
||||
|
||||
void ArgsManager::CheckMultipleCLIArgs() const
|
||||
{
|
||||
LOCK(cs_args);
|
||||
|
|
|
@ -359,11 +359,7 @@ protected:
|
|||
/**
|
||||
* Clear available arguments
|
||||
*/
|
||||
void ClearArgs() {
|
||||
LOCK(cs_args);
|
||||
m_available_args.clear();
|
||||
m_network_only_args.clear();
|
||||
}
|
||||
void ClearArgs();
|
||||
|
||||
/**
|
||||
* Check CLI command args
|
||||
|
|
16
src/init.cpp
16
src/init.cpp
|
@ -33,6 +33,7 @@
|
|||
#include <interfaces/ipc.h>
|
||||
#include <interfaces/mining.h>
|
||||
#include <interfaces/node.h>
|
||||
#include <ipc/exception.h>
|
||||
#include <kernel/caches.h>
|
||||
#include <kernel/context.h>
|
||||
#include <key.h>
|
||||
|
@ -298,8 +299,13 @@ void Shutdown(NodeContext& node)
|
|||
StopREST();
|
||||
StopRPC();
|
||||
StopHTTPServer();
|
||||
for (const auto& client : node.chain_clients) {
|
||||
for (auto& client : node.chain_clients) {
|
||||
try {
|
||||
client->flush();
|
||||
} catch (const ipc::Exception& e) {
|
||||
LogDebug(BCLog::IPC, "Chain client did not disconnect cleanly: %s", e.what());
|
||||
client.reset();
|
||||
}
|
||||
}
|
||||
StopMapPort();
|
||||
|
||||
|
@ -374,7 +380,7 @@ void Shutdown(NodeContext& node)
|
|||
}
|
||||
}
|
||||
for (const auto& client : node.chain_clients) {
|
||||
client->stop();
|
||||
if (client) client->stop();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_ZMQ
|
||||
|
@ -398,6 +404,12 @@ void Shutdown(NodeContext& node)
|
|||
|
||||
RemovePidFile(*node.args);
|
||||
|
||||
// If any -ipcbind clients are still connected, disconnect them now so they
|
||||
// do not block shutdown.
|
||||
if (interfaces::Ipc* ipc = node.init->ipc()) {
|
||||
ipc->disconnectIncoming();
|
||||
}
|
||||
|
||||
LogPrintf("%s: done\n", __func__);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,17 +59,20 @@ public:
|
|||
//! true. If this is not a spawned child process, return false.
|
||||
virtual bool startSpawnedProcess(int argc, char* argv[], int& exit_status) = 0;
|
||||
|
||||
//! Connect to a socket address and make a client interface proxy object
|
||||
//! using provided callback. connectAddress returns an interface pointer if
|
||||
//! the connection was established, returns null if address is empty ("") or
|
||||
//! disabled ("0") or if a connection was refused but not required ("auto"),
|
||||
//! and throws an exception if there was an unexpected error.
|
||||
//! Connect to a socket address and return a pointer to its Init interface.
|
||||
//! Returns a non-null pointer if the connection was established, returns
|
||||
//! null if address is empty ("") or disabled ("0") or if a connection was
|
||||
//! refused but not required ("auto"), and throws an exception if there was
|
||||
//! an unexpected error.
|
||||
virtual std::unique_ptr<Init> connectAddress(std::string& address) = 0;
|
||||
|
||||
//! Connect to a socket address and make a client interface proxy object
|
||||
//! using provided callback. Throws an exception if there was an error.
|
||||
//! Listen on a socket address exposing this process's init interface to
|
||||
//! clients. Throws an exception if there was an error.
|
||||
virtual void listenAddress(std::string& address) = 0;
|
||||
|
||||
//! Disconnect any incoming connections that are still connected.
|
||||
virtual void disconnectIncoming() = 0;
|
||||
|
||||
//! Add cleanup callback to remote interface that will run when the
|
||||
//! interface is deleted.
|
||||
template<typename Interface>
|
||||
|
|
|
@ -68,6 +68,13 @@ public:
|
|||
m_loop->loop();
|
||||
m_loop.reset();
|
||||
}
|
||||
void disconnectIncoming() override
|
||||
{
|
||||
if (!m_loop) return;
|
||||
m_loop->sync([&] {
|
||||
m_loop->m_incoming_connections.clear();
|
||||
});
|
||||
}
|
||||
void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
|
||||
{
|
||||
mp::ProxyTypeRegister::types().at(type)(iface).cleanup_fns.emplace_back(std::move(cleanup));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <signal.h>
|
||||
#include <stdexcept>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
@ -26,6 +27,27 @@
|
|||
|
||||
namespace ipc {
|
||||
namespace {
|
||||
#ifndef WIN32
|
||||
std::string g_ignore_ctrl_c;
|
||||
|
||||
void HandleCtrlC(int)
|
||||
{
|
||||
(void)write(STDOUT_FILENO, g_ignore_ctrl_c.data(), g_ignore_ctrl_c.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
void IgnoreCtrlC(std::string message)
|
||||
{
|
||||
#ifndef WIN32
|
||||
g_ignore_ctrl_c = std::move(message);
|
||||
struct sigaction sa{};
|
||||
sa.sa_handler = HandleCtrlC;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
class IpcImpl : public interfaces::Ipc
|
||||
{
|
||||
public:
|
||||
|
@ -53,6 +75,7 @@ public:
|
|||
if (!m_process->checkSpawned(argc, argv, fd)) {
|
||||
return false;
|
||||
}
|
||||
IgnoreCtrlC(strprintf("[%s] SIGINT received — waiting for parent to shut down.\n", m_exe_name));
|
||||
m_protocol->serve(fd, m_exe_name, m_init);
|
||||
exit_status = EXIT_SUCCESS;
|
||||
return true;
|
||||
|
@ -86,6 +109,10 @@ public:
|
|||
int fd = m_process->bind(gArgs.GetDataDirNet(), m_exe_name, address);
|
||||
m_protocol->listen(fd, m_exe_name, m_init);
|
||||
}
|
||||
void disconnectIncoming() override
|
||||
{
|
||||
m_protocol->disconnectIncoming();
|
||||
}
|
||||
void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
|
||||
{
|
||||
m_protocol->addCleanup(type, iface, std::move(cleanup));
|
||||
|
|
|
@ -58,6 +58,9 @@ public:
|
|||
//! clients and servers independently.
|
||||
virtual void serve(int fd, const char* exe_name, interfaces::Init& init, const std::function<void()>& ready_fn = {}) = 0;
|
||||
|
||||
//! Disconnect any incoming connections that are still connected.
|
||||
virtual void disconnectIncoming() = 0;
|
||||
|
||||
//! Add cleanup callback to interface that will run when the interface is
|
||||
//! deleted.
|
||||
virtual void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) = 0;
|
||||
|
|
|
@ -121,6 +121,13 @@ public:
|
|||
m_reachable.clear();
|
||||
}
|
||||
|
||||
void Reset() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
AssertLockNotHeld(m_mutex);
|
||||
LOCK(m_mutex);
|
||||
m_reachable = DefaultNets();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Contains(Network net) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
AssertLockNotHeld(m_mutex);
|
||||
|
@ -142,9 +149,9 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
mutable Mutex m_mutex;
|
||||
|
||||
std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){
|
||||
static std::unordered_set<Network> DefaultNets()
|
||||
{
|
||||
return {
|
||||
NET_UNROUTABLE,
|
||||
NET_IPV4,
|
||||
NET_IPV6,
|
||||
|
@ -155,6 +162,10 @@ private:
|
|||
};
|
||||
};
|
||||
|
||||
mutable Mutex m_mutex;
|
||||
std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){DefaultNets()};
|
||||
};
|
||||
|
||||
extern ReachableNets g_reachable_nets;
|
||||
|
||||
/**
|
||||
|
|
|
@ -323,6 +323,12 @@ void SetRPCWarmupStatus(const std::string& newStatus)
|
|||
rpcWarmupStatus = newStatus;
|
||||
}
|
||||
|
||||
void SetRPCWarmupStarting()
|
||||
{
|
||||
LOCK(g_rpc_warmup_mutex);
|
||||
fRPCInWarmup = true;
|
||||
}
|
||||
|
||||
void SetRPCWarmupFinished()
|
||||
{
|
||||
LOCK(g_rpc_warmup_mutex);
|
||||
|
|
|
@ -30,6 +30,7 @@ void RpcInterruptionPoint();
|
|||
*/
|
||||
void SetRPCWarmupStatus(const std::string& newStatus);
|
||||
/* Mark warmup as done. RPC calls will be processed from now on. */
|
||||
void SetRPCWarmupStarting();
|
||||
void SetRPCWarmupFinished();
|
||||
|
||||
/* returns the current warmup state. */
|
||||
|
|
|
@ -63,6 +63,7 @@ add_executable(test_bitcoin
|
|||
net_peer_eviction_tests.cpp
|
||||
net_tests.cpp
|
||||
netbase_tests.cpp
|
||||
node_init_tests.cpp
|
||||
node_warnings_tests.cpp
|
||||
orphanage_tests.cpp
|
||||
pcp_tests.cpp
|
||||
|
|
|
@ -702,6 +702,7 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
|
|||
|
||||
BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
|
||||
{
|
||||
g_reachable_nets.Reset();
|
||||
BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4));
|
||||
BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6));
|
||||
BOOST_CHECK(g_reachable_nets.Contains(NET_ONION));
|
||||
|
|
49
src/test/node_init_tests.cpp
Normal file
49
src/test/node_init_tests.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2025 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 <init.h>
|
||||
#include <interfaces/init.h>
|
||||
#include <rpc/server.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
using node::NodeContext;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(node_init_tests, BasicTestingSetup)
|
||||
|
||||
//! Custom implementation of interfaces::Init for testing.
|
||||
class TestInit : public interfaces::Init
|
||||
{
|
||||
public:
|
||||
TestInit(NodeContext& node) : m_node(node)
|
||||
{
|
||||
InitContext(m_node);
|
||||
m_node.init = this;
|
||||
}
|
||||
std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
|
||||
std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override
|
||||
{
|
||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||
}
|
||||
NodeContext& m_node;
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(init_test)
|
||||
{
|
||||
// Reset logging, config file path, rpc state, reachable nets to avoid errors in AppInitMain
|
||||
LogInstance().DisconnectTestLogger();
|
||||
m_node.args->SetConfigFilePath({});
|
||||
SetRPCWarmupStarting();
|
||||
g_reachable_nets.Reset();
|
||||
|
||||
// Run through initialization and shutdown code.
|
||||
TestInit init{m_node};
|
||||
BOOST_CHECK(AppInitInterfaces(m_node));
|
||||
BOOST_CHECK(AppInitMain(m_node));
|
||||
Interrupt(m_node);
|
||||
Shutdown(m_node);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -214,7 +214,10 @@ BasicTestingSetup::~BasicTestingSetup()
|
|||
} else {
|
||||
fs::remove_all(m_path_root);
|
||||
}
|
||||
// Clear all arguments except for -datadir, which GUI tests currently rely
|
||||
// on to be set even after the testing setup is destroyed.
|
||||
gArgs.ClearArgs();
|
||||
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
||||
}
|
||||
|
||||
ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
|
||||
|
|
Loading…
Add table
Reference in a new issue