mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
Merge bitcoin/bitcoin#27708: Return EXIT_FAILURE on post-init fatal errors
61c569ab60
refactor: decouple early return commands from AppInit (furszy)4927167f85
gui: return EXIT_FAILURE on post-init fatal errors (furszy)3b2c61e819
Return EXIT_FAILURE on post-init fatal errors (furszy)3c06926cf2
refactor: index: use `AbortNode` in fatal error helper (Sebastian Falbesoner)9ddf7e03a3
move ThreadImport ABC error to use AbortNode (furszy) Pull request description: It seems odd to return `EXIT_SUCCESS` when the node aborted execution due a fatal internal error or any post-init problem that triggers an unrequested shutdown. e.g. blocks or coins db I/O errors, disconnect block failure, failure during thread import (external blocks loading process error), among others. ACKs for top commit: TheCharlatan: ACK61c569ab60
ryanofsky: Code review ACK61c569ab60
pinheadmz: ACK61c569ab60
theStack: Code-review ACK61c569ab60
Tree-SHA512: 18a59c3acc1c6d12cbc74a20a401e89659740c6477fccb59070c9f97922dfe588468e9e5eef56c5f395762187c34179a5e3954aa5b844787fa13da2e666c63d3
This commit is contained in:
commit
c92fd63886
14 changed files with 85 additions and 56 deletions
|
@ -112,20 +112,30 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static bool AppInit(NodeContext& node, int argc, char* argv[])
|
static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
|
||||||
{
|
{
|
||||||
bool fRet = false;
|
|
||||||
|
|
||||||
util::ThreadSetInternalName("init");
|
|
||||||
|
|
||||||
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
|
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
|
||||||
ArgsManager& args = *Assert(node.args);
|
|
||||||
SetupServerArgs(args);
|
SetupServerArgs(args);
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!args.ParseParameters(argc, argv, error)) {
|
if (!args.ParseParameters(argc, argv, error)) {
|
||||||
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
|
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto error = common::InitConfig(args)) {
|
||||||
|
return InitError(error->message, error->details);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out when loose non-argument tokens are encountered on command line
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!IsSwitchChar(argv[i][0])) {
|
||||||
|
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ProcessInitCommands(ArgsManager& args)
|
||||||
|
{
|
||||||
// Process help and version before taking care about datadir
|
// Process help and version before taking care about datadir
|
||||||
if (HelpRequested(args) || args.IsArgSet("-version")) {
|
if (HelpRequested(args) || args.IsArgSet("-version")) {
|
||||||
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
|
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
|
||||||
|
@ -142,6 +152,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AppInit(NodeContext& node)
|
||||||
|
{
|
||||||
|
bool fRet = false;
|
||||||
|
ArgsManager& args = *Assert(node.args);
|
||||||
|
|
||||||
#if HAVE_DECL_FORK
|
#if HAVE_DECL_FORK
|
||||||
// Communication with parent after daemonizing. This is used for signalling in the following ways:
|
// Communication with parent after daemonizing. This is used for signalling in the following ways:
|
||||||
// - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
|
// - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
|
||||||
|
@ -153,23 +171,12 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
||||||
std::any context{&node};
|
std::any context{&node};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (auto error = common::InitConfig(args)) {
|
|
||||||
return InitError(error->message, error->details);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error out when loose non-argument tokens are encountered on command line
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if (!IsSwitchChar(argv[i][0])) {
|
|
||||||
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -server defaults to true for bitcoind but not for the GUI so do this here
|
// -server defaults to true for bitcoind but not for the GUI so do this here
|
||||||
args.SoftSetBoolArg("-server", true);
|
args.SoftSetBoolArg("-server", true);
|
||||||
// Set this early so that parameter interactions go to console
|
// Set this early so that parameter interactions go to console
|
||||||
InitLogging(args);
|
InitLogging(args);
|
||||||
InitParameterInteraction(args);
|
InitParameterInteraction(args);
|
||||||
if (!AppInitBasicSetup(args)) {
|
if (!AppInitBasicSetup(args, node.exit_status)) {
|
||||||
// InitError will have been called with detailed error, which ends up on console
|
// InitError will have been called with detailed error, which ends up on console
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -236,12 +243,6 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
|
SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
|
||||||
if (fRet) {
|
|
||||||
WaitForShutdown();
|
|
||||||
}
|
|
||||||
Interrupt(node);
|
|
||||||
Shutdown(node);
|
|
||||||
|
|
||||||
return fRet;
|
return fRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,5 +265,22 @@ MAIN_FUNCTION
|
||||||
// Connect bitcoind signal handlers
|
// Connect bitcoind signal handlers
|
||||||
noui_connect();
|
noui_connect();
|
||||||
|
|
||||||
return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
|
util::ThreadSetInternalName("init");
|
||||||
|
|
||||||
|
// Interpret command line arguments
|
||||||
|
ArgsManager& args = *Assert(node.args);
|
||||||
|
if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE;
|
||||||
|
// Process early info return commands such as -help or -version
|
||||||
|
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
// Start application
|
||||||
|
if (AppInit(node)) {
|
||||||
|
WaitForShutdown();
|
||||||
|
} else {
|
||||||
|
node.exit_status = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
Interrupt(node);
|
||||||
|
Shutdown(node);
|
||||||
|
|
||||||
|
return node.exit_status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,7 @@ constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void FatalError(const char* fmt, const Args&... args)
|
static void FatalError(const char* fmt, const Args&... args)
|
||||||
{
|
{
|
||||||
std::string strMessage = tfm::format(fmt, args...);
|
AbortNode(tfm::format(fmt, args...));
|
||||||
SetMiscWarning(Untranslated(strMessage));
|
|
||||||
LogPrintf("*** %s\n", strMessage);
|
|
||||||
InitError(_("A fatal internal error occurred, see debug.log for details"));
|
|
||||||
StartShutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
|
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
|
||||||
|
|
|
@ -800,7 +800,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
|
||||||
std::terminate();
|
std::terminate();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool AppInitBasicSetup(const ArgsManager& args)
|
bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
|
||||||
{
|
{
|
||||||
// ********************************************************* Step 1: setup
|
// ********************************************************* Step 1: setup
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -814,7 +814,7 @@ bool AppInitBasicSetup(const ArgsManager& args)
|
||||||
// Enable heap terminate-on-corruption
|
// Enable heap terminate-on-corruption
|
||||||
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
|
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
|
||||||
#endif
|
#endif
|
||||||
if (!InitShutdownState()) {
|
if (!InitShutdownState(exit_status)) {
|
||||||
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
|
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ void InitParameterInteraction(ArgsManager& args);
|
||||||
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
|
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
|
||||||
* @pre Parameters should be parsed and config file should be read.
|
* @pre Parameters should be parsed and config file should be read.
|
||||||
*/
|
*/
|
||||||
bool AppInitBasicSetup(const ArgsManager& args);
|
bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status);
|
||||||
/**
|
/**
|
||||||
* Initialization: parameter interaction.
|
* Initialization: parameter interaction.
|
||||||
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
|
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
|
||||||
|
|
|
@ -80,6 +80,9 @@ public:
|
||||||
//! Get warnings.
|
//! Get warnings.
|
||||||
virtual bilingual_str getWarnings() = 0;
|
virtual bilingual_str getWarnings() = 0;
|
||||||
|
|
||||||
|
//! Get exit status.
|
||||||
|
virtual int getExitStatus() = 0;
|
||||||
|
|
||||||
// Get log flags.
|
// Get log flags.
|
||||||
virtual uint32_t getLogCategories() = 0;
|
virtual uint32_t getLogCategories() = 0;
|
||||||
|
|
||||||
|
|
|
@ -928,8 +928,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
|
||||||
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
|
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
if (!chainstate->ActivateBestChain(state, nullptr)) {
|
if (!chainstate->ActivateBestChain(state, nullptr)) {
|
||||||
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
|
AbortNode(strprintf("Failed to connect best block (%s)", state.ToString()));
|
||||||
StartShutdown();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
#include <kernel/context.h>
|
#include <kernel/context.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -65,6 +67,7 @@ struct NodeContext {
|
||||||
std::unique_ptr<CScheduler> scheduler;
|
std::unique_ptr<CScheduler> scheduler;
|
||||||
std::function<void()> rpc_interruption_point = [] {};
|
std::function<void()> rpc_interruption_point = [] {};
|
||||||
std::unique_ptr<KernelNotifications> notifications;
|
std::unique_ptr<KernelNotifications> notifications;
|
||||||
|
std::atomic<int> exit_status{EXIT_SUCCESS};
|
||||||
|
|
||||||
//! Declare default constructor and destructor that are not inline, so code
|
//! Declare default constructor and destructor that are not inline, so code
|
||||||
//! instantiating the NodeContext struct doesn't need to #include class
|
//! instantiating the NodeContext struct doesn't need to #include class
|
||||||
|
|
|
@ -89,10 +89,11 @@ public:
|
||||||
void initLogging() override { InitLogging(args()); }
|
void initLogging() override { InitLogging(args()); }
|
||||||
void initParameterInteraction() override { InitParameterInteraction(args()); }
|
void initParameterInteraction() override { InitParameterInteraction(args()); }
|
||||||
bilingual_str getWarnings() override { return GetWarnings(true); }
|
bilingual_str getWarnings() override { return GetWarnings(true); }
|
||||||
|
int getExitStatus() override { return Assert(m_context)->exit_status.load(); }
|
||||||
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
|
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
|
||||||
bool baseInitialize() override
|
bool baseInitialize() override
|
||||||
{
|
{
|
||||||
if (!AppInitBasicSetup(args())) return false;
|
if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false;
|
||||||
if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false;
|
if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false;
|
||||||
|
|
||||||
m_context->kernel = std::make_unique<kernel::Context>();
|
m_context->kernel = std::make_unique<kernel::Context>();
|
||||||
|
@ -105,7 +106,10 @@ public:
|
||||||
}
|
}
|
||||||
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
|
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
|
||||||
{
|
{
|
||||||
return AppInitMain(*m_context, tip_info);
|
if (AppInitMain(*m_context, tip_info)) return true;
|
||||||
|
// Error during initialization, set exit status before continue
|
||||||
|
m_context->exit_status.store(EXIT_FAILURE);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
void appShutdown() override
|
void appShutdown() override
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <qt/bitcoin.h>
|
#include <qt/bitcoin.h>
|
||||||
|
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
|
#include <node/context.h>
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
#include <common/init.h>
|
#include <common/init.h>
|
||||||
#include <common/system.h>
|
#include <common/system.h>
|
||||||
|
@ -397,9 +398,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
|
||||||
{
|
{
|
||||||
qDebug() << __func__ << ": Initialization result: " << success;
|
qDebug() << __func__ << ": Initialization result: " << success;
|
||||||
|
|
||||||
// Set exit result.
|
if (success) {
|
||||||
returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
if(success) {
|
|
||||||
delete m_splash;
|
delete m_splash;
|
||||||
m_splash = nullptr;
|
m_splash = nullptr;
|
||||||
|
|
||||||
|
@ -653,7 +652,6 @@ int GuiMain(int argc, char* argv[])
|
||||||
app.InitPruneSetting(prune_MiB);
|
app.InitPruneSetting(prune_MiB);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rv = EXIT_SUCCESS;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
app.createWindow(networkStyle.data());
|
app.createWindow(networkStyle.data());
|
||||||
|
@ -666,10 +664,9 @@ int GuiMain(int argc, char* argv[])
|
||||||
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
|
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
|
||||||
#endif
|
#endif
|
||||||
app.exec();
|
app.exec();
|
||||||
rv = app.getReturnValue();
|
|
||||||
} else {
|
} else {
|
||||||
// A dialog with detailed error will have been shown by InitError()
|
// A dialog with detailed error will have been shown by InitError()
|
||||||
rv = EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
PrintExceptionContinue(&e, "Runaway exception");
|
PrintExceptionContinue(&e, "Runaway exception");
|
||||||
|
@ -678,5 +675,5 @@ int GuiMain(int argc, char* argv[])
|
||||||
PrintExceptionContinue(nullptr, "Runaway exception");
|
PrintExceptionContinue(nullptr, "Runaway exception");
|
||||||
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
|
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
|
||||||
}
|
}
|
||||||
return rv;
|
return app.node().getExitStatus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,6 @@ public:
|
||||||
/// Request core initialization
|
/// Request core initialization
|
||||||
void requestInitialize();
|
void requestInitialize();
|
||||||
|
|
||||||
/// Get process return value
|
|
||||||
int getReturnValue() const { return returnValue; }
|
|
||||||
|
|
||||||
/// Get window identifier of QMainWindow (BitcoinGUI)
|
/// Get window identifier of QMainWindow (BitcoinGUI)
|
||||||
WId getMainWinId() const;
|
WId getMainWinId() const;
|
||||||
|
|
||||||
|
@ -104,7 +101,6 @@ private:
|
||||||
PaymentServer* paymentServer{nullptr};
|
PaymentServer* paymentServer{nullptr};
|
||||||
WalletController* m_wallet_controller{nullptr};
|
WalletController* m_wallet_controller{nullptr};
|
||||||
#endif
|
#endif
|
||||||
int returnValue{0};
|
|
||||||
const PlatformStyle* platformStyle{nullptr};
|
const PlatformStyle* platformStyle{nullptr};
|
||||||
std::unique_ptr<QWidget> shutdownWindow;
|
std::unique_ptr<QWidget> shutdownWindow;
|
||||||
SplashScreen* m_splash = nullptr;
|
SplashScreen* m_splash = nullptr;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <node/interface_ui.h>
|
#include <node/interface_ui.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/tokenpipe.h>
|
#include <util/tokenpipe.h>
|
||||||
#include <warnings.h>
|
#include <warnings.h>
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static std::atomic<int>* g_exit_status{nullptr};
|
||||||
|
|
||||||
bool AbortNode(const std::string& strMessage, bilingual_str user_message)
|
bool AbortNode(const std::string& strMessage, bilingual_str user_message)
|
||||||
{
|
{
|
||||||
SetMiscWarning(Untranslated(strMessage));
|
SetMiscWarning(Untranslated(strMessage));
|
||||||
|
@ -28,6 +31,7 @@ bool AbortNode(const std::string& strMessage, bilingual_str user_message)
|
||||||
user_message = _("A fatal internal error occurred, see debug.log for details");
|
user_message = _("A fatal internal error occurred, see debug.log for details");
|
||||||
}
|
}
|
||||||
InitError(user_message);
|
InitError(user_message);
|
||||||
|
Assert(g_exit_status)->store(EXIT_FAILURE);
|
||||||
StartShutdown();
|
StartShutdown();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -44,8 +48,9 @@ static TokenPipeEnd g_shutdown_r;
|
||||||
static TokenPipeEnd g_shutdown_w;
|
static TokenPipeEnd g_shutdown_w;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool InitShutdownState()
|
bool InitShutdownState(std::atomic<int>& exit_status)
|
||||||
{
|
{
|
||||||
|
g_exit_status = &exit_status;
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
std::optional<TokenPipe> pipe = TokenPipe::Make();
|
std::optional<TokenPipe> pipe = TokenPipe::Make();
|
||||||
if (!pipe) return false;
|
if (!pipe) return false;
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
|
|
||||||
#include <util/translation.h> // For bilingual_str
|
#include <util/translation.h> // For bilingual_str
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
/** Abort with a message */
|
/** Abort with a message */
|
||||||
bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{});
|
bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{});
|
||||||
|
|
||||||
/** Initialize shutdown state. This must be called before using either StartShutdown(),
|
/** Initialize shutdown state. This must be called before using either StartShutdown(),
|
||||||
* AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe.
|
* AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe.
|
||||||
*/
|
*/
|
||||||
bool InitShutdownState();
|
bool InitShutdownState(std::atomic<int>& exit_status);
|
||||||
|
|
||||||
/** Request shutdown of the application. */
|
/** Request shutdown of the application. */
|
||||||
void StartShutdown();
|
void StartShutdown();
|
||||||
|
|
|
@ -40,7 +40,7 @@ class AbortNodeTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# Check that node0 aborted
|
# Check that node0 aborted
|
||||||
self.log.info("Waiting for crash")
|
self.log.info("Waiting for crash")
|
||||||
self.nodes[0].wait_until_stopped(timeout=5)
|
self.nodes[0].wait_until_stopped(timeout=5, expect_error=True)
|
||||||
self.log.info("Node crashed - now verifying restart fails")
|
self.log.info("Node crashed - now verifying restart fails")
|
||||||
self.nodes[0].assert_start_raises_init_error()
|
self.nodes[0].assert_start_raises_init_error()
|
||||||
|
|
||||||
|
|
|
@ -365,7 +365,7 @@ class TestNode():
|
||||||
if wait_until_stopped:
|
if wait_until_stopped:
|
||||||
self.wait_until_stopped()
|
self.wait_until_stopped()
|
||||||
|
|
||||||
def is_node_stopped(self):
|
def is_node_stopped(self, expected_ret_code=None):
|
||||||
"""Checks whether the node has stopped.
|
"""Checks whether the node has stopped.
|
||||||
|
|
||||||
Returns True if the node has stopped. False otherwise.
|
Returns True if the node has stopped. False otherwise.
|
||||||
|
@ -377,8 +377,13 @@ class TestNode():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# process has stopped. Assert that it didn't return an error code.
|
# process has stopped. Assert that it didn't return an error code.
|
||||||
assert return_code == 0, self._node_msg(
|
# unless 'expected_ret_code' is provided.
|
||||||
"Node returned non-zero exit code (%d) when stopping" % return_code)
|
if expected_ret_code is not None:
|
||||||
|
assert return_code == expected_ret_code, self._node_msg(
|
||||||
|
"Node returned unexpected exit code (%d) vs (%d) when stopping" % (return_code, expected_ret_code))
|
||||||
|
else:
|
||||||
|
assert return_code == 0, self._node_msg(
|
||||||
|
"Node returned non-zero exit code (%d) when stopping" % return_code)
|
||||||
self.running = False
|
self.running = False
|
||||||
self.process = None
|
self.process = None
|
||||||
self.rpc_connected = False
|
self.rpc_connected = False
|
||||||
|
@ -386,8 +391,9 @@ class TestNode():
|
||||||
self.log.debug("Node stopped")
|
self.log.debug("Node stopped")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
|
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False):
|
||||||
wait_until_helper(self.is_node_stopped, timeout=timeout, timeout_factor=self.timeout_factor)
|
expected_ret_code = 1 if expect_error else None # Whether node shutdown return EXIT_FAILURE or EXIT_SUCCESS
|
||||||
|
wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code), timeout=timeout, timeout_factor=self.timeout_factor)
|
||||||
|
|
||||||
def replace_in_config(self, replacements):
|
def replace_in_config(self, replacements):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Reference in a new issue