From 0cc8b6bc44bea29e24fa4e13d8a9bbe4f1483680 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 25 Oct 2016 11:34:27 +0200 Subject: init: Split up AppInit2 into multiple phases This allows doing some of the steps before e.g. daemonization and some fater. --- src/bitcoind.cpp | 26 +++++++++++++++++++++----- src/init.cpp | 48 ++++++++++++++++++++++++++++++++++-------------- src/init.h | 25 ++++++++++++++++++++++++- src/qt/bitcoin.cpp | 17 ++++++++++++++++- 4 files changed, 95 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 351463c256..a89b8cd4ee 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -128,6 +128,26 @@ bool AppInit(int argc, char* argv[]) fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n"); exit(1); } + // -server defaults to true for bitcoind but not for the GUI so do this here + SoftSetBoolArg("-server", true); + // Set this early so that parameter interactions go to console + InitLogging(); + InitParameterInteraction(); + if (!AppInitBasicSetup()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } + if (!AppInitParameterInteraction()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } + if (!AppInitSanityChecks()) + { + // InitError will have been called with detailed error, which ends up on console + exit(1); + } if (GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON @@ -143,12 +163,8 @@ bool AppInit(int argc, char* argv[]) return false; #endif // HAVE_DECL_DAEMON } - SoftSetBoolArg("-server", true); - // Set this early so that parameter interactions go to console - InitLogging(); - InitParameterInteraction(); - fRet = AppInit2(threadGroup, scheduler); + fRet = AppInitMain(threadGroup, scheduler); } catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); diff --git a/src/init.cpp b/src/init.cpp index e2f25eda74..4f435dc7cf 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -788,10 +788,17 @@ void InitLogging() LogPrintf("Bitcoin version %s\n", FormatFullVersion()); } -/** Initialize bitcoin. - * @pre Parameters should be parsed and config file should be read. - */ -bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) +namespace { // Variables internal to initialization process only + +ServiceFlags nRelevantServices = NODE_NETWORK; +int nMaxConnections; +int nUserMaxConnections; +int nFD; +ServiceFlags nLocalServices = NODE_NETWORK; + +} + +bool AppInitBasicSetup() { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -843,9 +850,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif + return true; +} - // ********************************************************* Step 2: parameter interactions +bool AppInitParameterInteraction() +{ const CChainParams& chainparams = Params(); + // ********************************************************* Step 2: parameter interactions // also see: InitParameterInteraction() @@ -857,12 +868,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); - int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); - int nMaxConnections = std::max(nUserMaxConnections, 0); + nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); - int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections); @@ -977,9 +988,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op - ServiceFlags nLocalServices = NODE_NETWORK; - ServiceFlags nRelevantServices = NODE_NETWORK; - if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); @@ -1028,8 +1036,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } } + return true; +} - // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log +bool AppInitSanityChecks() +{ + // ********************************************************* Step 4: sanity checks // Initialize elliptic curve code ECC_Start(); @@ -1048,11 +1060,19 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) try { static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); - if (!lock.try_lock()) + if (!lock.try_lock()) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME))); + } } catch(const boost::interprocess::interprocess_exception& e) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what())); } + return true; +} + +bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) +{ + const CChainParams& chainparams = Params(); + // ********************************************************* Step 4a: application initialization #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); @@ -1066,7 +1086,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); - LogPrintf("Using data directory %s\n", strDataDir); + LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); diff --git a/src/init.h b/src/init.h index 63e07ccb3c..1b87e0ec52 100644 --- a/src/init.h +++ b/src/init.h @@ -25,7 +25,30 @@ void Shutdown(); void InitLogging(); //!Parameter interaction: change current parameters depending on various rules void InitParameterInteraction(); -bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler); + +/** Initialize bitcoin core: Basic context setup. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInitBasicSetup(); +/** + * Initialization: parameter interaction. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called. + */ +bool AppInitParameterInteraction(); +/** + * Initialization sanity checks: ecc init, sanity checks, dir lock. + * @note This can be done before daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called. + */ +bool AppInitSanityChecks(); +/** + * Bitcoin core main initialization. + * @note This should only be done after daemonization. + * @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called. + */ +bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler); /** The help message mode determines what help message to show */ enum HelpMessageMode { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 9986af4957..5e1a41d5a7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -267,7 +267,22 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running AppInit2 in thread"; - int rv = AppInit2(threadGroup, scheduler); + if (!AppInitBasicSetup()) + { + Q_EMIT initializeResult(false); + return; + } + if (!AppInitParameterInteraction()) + { + Q_EMIT initializeResult(false); + return; + } + if (!AppInitSanityChecks()) + { + Q_EMIT initializeResult(false); + return; + } + int rv = AppInitMain(threadGroup, scheduler); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); -- cgit v1.2.3 From 16ca0bfd2848424de7deae307283d9eb9de8a978 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 25 Oct 2016 12:03:22 +0200 Subject: init: Try to aquire datadir lock before and after daemonization Before daemonization, just probe the data directory lock and print an early error message if possible. After daemonization get the data directory lock again and hold on to it until exit This creates a slight window for a race condition to happen, however this condition is harmless: it will at most make us exit without printing a message to console. $ src/bitcoind -testnet -daemon Bitcoin server starting $ src/bitcoind -testnet -daemon Error: Cannot obtain a lock on data directory /home/orion/.bitcoin/testnet3. Bitcoin Core is probably already running. --- src/init.cpp | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/init.cpp b/src/init.cpp index 4f435dc7cf..b70e6e9158 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1039,18 +1039,8 @@ bool AppInitParameterInteraction() return true; } -bool AppInitSanityChecks() +static bool LockDataDirectory(bool probeOnly) { - // ********************************************************* Step 4: sanity checks - - // Initialize elliptic curve code - ECC_Start(); - globalVerifyHandle.reset(new ECCVerifyHandle()); - - // Sanity check - if (!InitSanityCheck()) - return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); - std::string strDataDir = GetDataDir().string(); // Make sure only a single Bitcoin process is using the data directory. @@ -1063,16 +1053,42 @@ bool AppInitSanityChecks() if (!lock.try_lock()) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME))); } + if (probeOnly) { + lock.unlock(); + } } catch(const boost::interprocess::interprocess_exception& e) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what())); } return true; } +bool AppInitSanityChecks() +{ + // ********************************************************* Step 4: sanity checks + + // Initialize elliptic curve code + ECC_Start(); + globalVerifyHandle.reset(new ECCVerifyHandle()); + + // Sanity check + if (!InitSanityCheck()) + return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); + + // Probe the data directory lock to give an early error message, if possible + return LockDataDirectory(true); +} + bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) { const CChainParams& chainparams = Params(); // ********************************************************* Step 4a: application initialization + // After daemonization get the data directory lock again and hold on to it until exit + // This creates a slight window for a race condition to happen, however this condition is harmless: it + // will at most make us exit without printing a message to console. + if (!LockDataDirectory(false)) { + // Detailed error printed inside LockDataDirectory + return false; + } #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); -- cgit v1.2.3 From deec83fd2cc8af39c28c74161650fbff432502ce Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 29 Nov 2016 12:44:14 +0100 Subject: init: Get rid of fServer flag There is no need to store this flag globally, the variable is only used inside the initialization process. Thanks to Alex Morcos for the idea. --- src/init.cpp | 4 +--- src/util.cpp | 1 - src/util.h | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/init.cpp b/src/init.cpp index b70e6e9158..fb51eb7633 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -931,8 +931,6 @@ bool AppInitParameterInteraction() else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - fServer = GetBoolArg("-server", false); - // block pruning; get the amount of disk space (in MiB) to allot for block & undo files int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; if (nSignedPruneTarget < 0) { @@ -1121,7 +1119,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ - if (fServer) + if (GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers(threadGroup)) diff --git a/src/util.cpp b/src/util.cpp index c20ede6221..332e077627 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -107,7 +107,6 @@ map > mapMultiArgs; bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; -bool fServer = false; string strMiscWarning; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; diff --git a/src/util.h b/src/util.h index bbb9b5db82..e8aa266f28 100644 --- a/src/util.h +++ b/src/util.h @@ -46,7 +46,6 @@ extern std::map > mapMultiArgs; extern bool fDebug; extern bool fPrintToConsole; extern bool fPrintToDebugLog; -extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; extern bool fLogTimeMicros; -- cgit v1.2.3