Skip to content

Commit c92fd63

Browse files
committed
Merge bitcoin/bitcoin#27708: Return EXIT_FAILURE on post-init fatal errors
61c569a refactor: decouple early return commands from AppInit (furszy) 4927167 gui: return EXIT_FAILURE on post-init fatal errors (furszy) 3b2c61e Return EXIT_FAILURE on post-init fatal errors (furszy) 3c06926 refactor: index: use `AbortNode` in fatal error helper (Sebastian Falbesoner) 9ddf7e0 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: ACK 61c569a ryanofsky: Code review ACK 61c569a pinheadmz: ACK 61c569a theStack: Code-review ACK 61c569a Tree-SHA512: 18a59c3acc1c6d12cbc74a20a401e89659740c6477fccb59070c9f97922dfe588468e9e5eef56c5f395762187c34179a5e3954aa5b844787fa13da2e666c63d3
2 parents 361a0c0 + 61c569a commit c92fd63

File tree

14 files changed

+85
-56
lines changed

14 files changed

+85
-56
lines changed

src/bitcoind.cpp

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,30 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
112112

113113
#endif
114114

115-
static bool AppInit(NodeContext& node, int argc, char* argv[])
115+
static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
116116
{
117-
bool fRet = false;
118-
119-
util::ThreadSetInternalName("init");
120-
121117
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
122-
ArgsManager& args = *Assert(node.args);
123118
SetupServerArgs(args);
124119
std::string error;
125120
if (!args.ParseParameters(argc, argv, error)) {
126121
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
127122
}
128123

124+
if (auto error = common::InitConfig(args)) {
125+
return InitError(error->message, error->details);
126+
}
127+
128+
// Error out when loose non-argument tokens are encountered on command line
129+
for (int i = 1; i < argc; i++) {
130+
if (!IsSwitchChar(argv[i][0])) {
131+
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
132+
}
133+
}
134+
return true;
135+
}
136+
137+
static bool ProcessInitCommands(ArgsManager& args)
138+
{
129139
// Process help and version before taking care about datadir
130140
if (HelpRequested(args) || args.IsArgSet("-version")) {
131141
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
@@ -142,6 +152,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
142152
return true;
143153
}
144154

155+
return false;
156+
}
157+
158+
static bool AppInit(NodeContext& node)
159+
{
160+
bool fRet = false;
161+
ArgsManager& args = *Assert(node.args);
162+
145163
#if HAVE_DECL_FORK
146164
// Communication with parent after daemonizing. This is used for signalling in the following ways:
147165
// - 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[])
153171
std::any context{&node};
154172
try
155173
{
156-
if (auto error = common::InitConfig(args)) {
157-
return InitError(error->message, error->details);
158-
}
159-
160-
// Error out when loose non-argument tokens are encountered on command line
161-
for (int i = 1; i < argc; i++) {
162-
if (!IsSwitchChar(argv[i][0])) {
163-
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
164-
}
165-
}
166-
167174
// -server defaults to true for bitcoind but not for the GUI so do this here
168175
args.SoftSetBoolArg("-server", true);
169176
// Set this early so that parameter interactions go to console
170177
InitLogging(args);
171178
InitParameterInteraction(args);
172-
if (!AppInitBasicSetup(args)) {
179+
if (!AppInitBasicSetup(args, node.exit_status)) {
173180
// InitError will have been called with detailed error, which ends up on console
174181
return false;
175182
}
@@ -236,12 +243,6 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
236243
}
237244
#endif
238245
SetSyscallSandboxPolicy(SyscallSandboxPolicy::SHUTOFF);
239-
if (fRet) {
240-
WaitForShutdown();
241-
}
242-
Interrupt(node);
243-
Shutdown(node);
244-
245246
return fRet;
246247
}
247248

@@ -264,5 +265,22 @@ MAIN_FUNCTION
264265
// Connect bitcoind signal handlers
265266
noui_connect();
266267

267-
return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
268+
util::ThreadSetInternalName("init");
269+
270+
// Interpret command line arguments
271+
ArgsManager& args = *Assert(node.args);
272+
if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE;
273+
// Process early info return commands such as -help or -version
274+
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
275+
276+
// Start application
277+
if (AppInit(node)) {
278+
WaitForShutdown();
279+
} else {
280+
node.exit_status = EXIT_FAILURE;
281+
}
282+
Interrupt(node);
283+
Shutdown(node);
284+
285+
return node.exit_status;
268286
}

src/index/base.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,7 @@ constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
3333
template <typename... Args>
3434
static void FatalError(const char* fmt, const Args&... args)
3535
{
36-
std::string strMessage = tfm::format(fmt, args...);
37-
SetMiscWarning(Untranslated(strMessage));
38-
LogPrintf("*** %s\n", strMessage);
39-
InitError(_("A fatal internal error occurred, see debug.log for details"));
40-
StartShutdown();
36+
AbortNode(tfm::format(fmt, args...));
4137
}
4238

4339
CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)

src/init.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
800800
std::terminate();
801801
};
802802

803-
bool AppInitBasicSetup(const ArgsManager& args)
803+
bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
804804
{
805805
// ********************************************************* Step 1: setup
806806
#ifdef _MSC_VER
@@ -814,7 +814,7 @@ bool AppInitBasicSetup(const ArgsManager& args)
814814
// Enable heap terminate-on-corruption
815815
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
816816
#endif
817-
if (!InitShutdownState()) {
817+
if (!InitShutdownState(exit_status)) {
818818
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
819819
}
820820

src/init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void InitParameterInteraction(ArgsManager& args);
3838
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.
3939
* @pre Parameters should be parsed and config file should be read.
4040
*/
41-
bool AppInitBasicSetup(const ArgsManager& args);
41+
bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status);
4242
/**
4343
* Initialization: parameter interaction.
4444
* @note This can be done before daemonization. Do not call Shutdown() if this function fails.

src/interfaces/node.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class Node
8080
//! Get warnings.
8181
virtual bilingual_str getWarnings() = 0;
8282

83+
//! Get exit status.
84+
virtual int getExitStatus() = 0;
85+
8386
// Get log flags.
8487
virtual uint32_t getLogCategories() = 0;
8588

src/node/blockstorage.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,7 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
928928
for (Chainstate* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
929929
BlockValidationState state;
930930
if (!chainstate->ActivateBestChain(state, nullptr)) {
931-
LogPrintf("Failed to connect best block (%s)\n", state.ToString());
932-
StartShutdown();
931+
AbortNode(strprintf("Failed to connect best block (%s)", state.ToString()));
933932
return;
934933
}
935934
}

src/node/context.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
#include <kernel/context.h>
99

10+
#include <atomic>
1011
#include <cassert>
12+
#include <cstdlib>
1113
#include <functional>
1214
#include <memory>
1315
#include <vector>
@@ -65,6 +67,7 @@ struct NodeContext {
6567
std::unique_ptr<CScheduler> scheduler;
6668
std::function<void()> rpc_interruption_point = [] {};
6769
std::unique_ptr<KernelNotifications> notifications;
70+
std::atomic<int> exit_status{EXIT_SUCCESS};
6871

6972
//! Declare default constructor and destructor that are not inline, so code
7073
//! instantiating the NodeContext struct doesn't need to #include class

src/node/interfaces.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,11 @@ class NodeImpl : public Node
8989
void initLogging() override { InitLogging(args()); }
9090
void initParameterInteraction() override { InitParameterInteraction(args()); }
9191
bilingual_str getWarnings() override { return GetWarnings(true); }
92+
int getExitStatus() override { return Assert(m_context)->exit_status.load(); }
9293
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
9394
bool baseInitialize() override
9495
{
95-
if (!AppInitBasicSetup(args())) return false;
96+
if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false;
9697
if (!AppInitParameterInteraction(args(), /*use_syscall_sandbox=*/false)) return false;
9798

9899
m_context->kernel = std::make_unique<kernel::Context>();
@@ -105,7 +106,10 @@ class NodeImpl : public Node
105106
}
106107
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
107108
{
108-
return AppInitMain(*m_context, tip_info);
109+
if (AppInitMain(*m_context, tip_info)) return true;
110+
// Error during initialization, set exit status before continue
111+
m_context->exit_status.store(EXIT_FAILURE);
112+
return false;
109113
}
110114
void appShutdown() override
111115
{

src/qt/bitcoin.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <qt/bitcoin.h>
1010

1111
#include <chainparams.h>
12+
#include <node/context.h>
1213
#include <common/args.h>
1314
#include <common/init.h>
1415
#include <common/system.h>
@@ -397,9 +398,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
397398
{
398399
qDebug() << __func__ << ": Initialization result: " << success;
399400

400-
// Set exit result.
401-
returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
402-
if(success) {
401+
if (success) {
403402
delete m_splash;
404403
m_splash = nullptr;
405404

@@ -653,7 +652,6 @@ int GuiMain(int argc, char* argv[])
653652
app.InitPruneSetting(prune_MiB);
654653
}
655654

656-
int rv = EXIT_SUCCESS;
657655
try
658656
{
659657
app.createWindow(networkStyle.data());
@@ -666,10 +664,9 @@ int GuiMain(int argc, char* argv[])
666664
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
667665
#endif
668666
app.exec();
669-
rv = app.getReturnValue();
670667
} else {
671668
// A dialog with detailed error will have been shown by InitError()
672-
rv = EXIT_FAILURE;
669+
return EXIT_FAILURE;
673670
}
674671
} catch (const std::exception& e) {
675672
PrintExceptionContinue(&e, "Runaway exception");
@@ -678,5 +675,5 @@ int GuiMain(int argc, char* argv[])
678675
PrintExceptionContinue(nullptr, "Runaway exception");
679676
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
680677
}
681-
return rv;
678+
return app.node().getExitStatus();
682679
}

src/qt/bitcoin.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ class BitcoinApplication: public QApplication
6262
/// Request core initialization
6363
void requestInitialize();
6464

65-
/// Get process return value
66-
int getReturnValue() const { return returnValue; }
67-
6865
/// Get window identifier of QMainWindow (BitcoinGUI)
6966
WId getMainWinId() const;
7067

@@ -104,7 +101,6 @@ public Q_SLOTS:
104101
PaymentServer* paymentServer{nullptr};
105102
WalletController* m_wallet_controller{nullptr};
106103
#endif
107-
int returnValue{0};
108104
const PlatformStyle* platformStyle{nullptr};
109105
std::unique_ptr<QWidget> shutdownWindow;
110106
SplashScreen* m_splash = nullptr;

0 commit comments

Comments
 (0)