Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/dash/lint-tidy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ echo "=========================="

cd "${BASE_ROOT_DIR}/build-ci/dashcore-${BUILD_TARGET}"
iwyu_tool.py \
"src/common/init.cpp" \
"src/common/url.cpp" \
"src/compat" \
"src/dbwrapper.cpp" \
Expand Down
4 changes: 3 additions & 1 deletion ci/test/00_setup_env_native_asan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export TEST_RUNNER_EXTRA="--timeout-factor=4" # Increase timeout because saniti
export FUNCTIONAL_TESTS_CONFIG="--exclude wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163)
export RUN_BENCH=true
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++"
export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 \
CPPFLAGS=-DDEBUG_LOCKORDER \
--with-sanitizers=address,float-divide-by-zero,integer,undefined CC=clang CXX=clang++"
3 changes: 2 additions & 1 deletion ci/test/00_setup_env_native_fuzz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export RUN_UNIT_TESTS=false
export RUN_FUNCTIONAL_TESTS=false
export RUN_FUZZ_TESTS=true
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC='clang-19 -ftrivial-auto-var-init=pattern' CXX='clang++-19 -ftrivial-auto-var-init=pattern'"
export BITCOIN_CONFIG="--enable-zmq --enable-fuzz --with-sanitizers=fuzzer,address,undefined,float-divide-by-zero,integer \
CC='clang-19 -ftrivial-auto-var-init=pattern' CXX='clang++-19 -ftrivial-auto-var-init=pattern'"
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ BITCOIN_CORE_H = \
coinjoin/walletman.h \
coins.h \
common/bloom.h \
common/init.h \
common/run_command.h \
common/url.h \
compat/assumptions.h \
Expand Down Expand Up @@ -948,6 +949,7 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \
coins.cpp \
common/bloom.cpp \
common/init.cpp \
common/run_command.cpp \
compressor.cpp \
core_read.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ BITCOIN_TESTS =\
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
test/translation_tests.cpp \
test/txindex_tests.cpp \
test/txpackage_tests.cpp \
test/txreconciliation_tests.cpp \
Expand Down
2 changes: 1 addition & 1 deletion src/bitcoin-chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ int main(int argc, char* argv[])
std::cerr << "Failed to load Chain state from your datadir." << std::endl;
goto epilogue;
} else {
std::tie(status, error) = node::VerifyLoadedChainstate(std::ref(chainman), *evodb, options);
std::tie(status, error) = node::VerifyLoadedChainstate(chainman, *evodb, options);
Comment thread
thepastaclaw marked this conversation as resolved.
if (status != node::ChainstateLoadStatus::SUCCESS) {
std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl;
goto epilogue;
Expand Down
4 changes: 2 additions & 2 deletions src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ static int AppInitRPC(int argc, char* argv[])
}
return EXIT_SUCCESS;
}
if (!CheckDataDirOption()) {
if (!CheckDataDirOption(gArgs)) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
return EXIT_FAILURE;
}
Expand Down Expand Up @@ -837,7 +837,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
if (failedToGetAuthCookie) {
throw std::runtime_error(strprintf(
"Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)",
fs::PathToString(GetConfigFile(gArgs.GetPathArg("-conf", BITCOIN_CONF_FILENAME)))));
fs::PathToString(gArgs.GetConfigFilePath())));
} else {
throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
}
Expand Down
2 changes: 1 addition & 1 deletion src/bitcoin-wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[
// check for printtoconsole, allow -debug
LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", args.GetBoolArg("-debug", false));

if (!CheckDataDirOption()) {
if (!CheckDataDirOption(args)) {
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""));
return EXIT_FAILURE;
}
Expand Down
91 changes: 49 additions & 42 deletions src/bitcoind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <chainparams.h>
#include <clientversion.h>
#include <common/init.h>
#include <common/url.h>
#include <compat/compat.h>
#include <init.h>
Expand Down Expand Up @@ -109,20 +110,32 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)

#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/dash.conf are parsed in qt/bitcoin.cpp's main()
ArgsManager& args = *Assert(node.args);
SetupServerArgs(args);
std::string error;
if (!args.ParseParameters(argc, argv, error)) {
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
}

if (args.IsArgSet("-printcrashinfo")) return true;

if (auto error = common::InitConfig(args)) {
return InitError(error->message, error->details);
Comment thread
thepastaclaw marked this conversation as resolved.
}

// 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 dashd -h for a list of options.", argv[i])));
}
}
return true;
}

static bool ProcessInitCommands(ArgsManager& args)
{
if (args.IsArgSet("-printcrashinfo")) {
std::cout << GetCrashInfoStrFromSerializedStr(args.GetArg("-printcrashinfo", "")) << std::endl;
return true;
Expand All @@ -144,6 +157,14 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
return true;
}

return false;
}

static bool AppInit(NodeContext& node)
{
bool fRet = false;
ArgsManager& args = *Assert(node.args);

CoreContext context{node};
#if HAVE_DECL_FORK
// Communication with parent after daemonizing. This is used for signalling in the following ways:
Expand All @@ -155,37 +176,12 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
#endif
try
{
if (!CheckDataDirOption()) {
return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""))));
}
if (!args.ReadConfigFiles(error, true)) {
return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error)));
}
// Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(args.GetChainName());
} catch (const std::exception& e) {
return InitError(Untranslated(strprintf("%s\n", e.what())));
}

// 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 dashd -h for a list of options.\n", argv[i])));
}
}

if (!gArgs.InitSettings(error)) {
InitError(Untranslated(error));
return false;
}

// -server defaults to true for dashd but not for the GUI so do this here
args.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
InitLogging(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
return false;
}
Expand Down Expand Up @@ -215,7 +211,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
}
break;
case -1: // Error happened.
return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", SysErrorString(errno))));
return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
default: { // Parent: wait and exit.
int token = daemon_ep.TokenRead();
if (token) { // Success
Expand All @@ -227,7 +223,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
}
}
#else
return InitError(Untranslated("-daemon is not supported on this operating system\n"));
return InitError(Untranslated("-daemon is not supported on this operating system"));
#endif // HAVE_DECL_FORK
}
// Lock data directory after daemonization
Expand All @@ -248,12 +244,6 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
daemon_ep.Close();
}
#endif
if (fRet) {
WaitForShutdown();
}
Interrupt(node);
Shutdown(node);

return fRet;
}

Expand All @@ -279,5 +269,22 @@ MAIN_FUNCTION
// Connect dashd signal handlers
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);
Comment thread
thepastaclaw marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid shutdown before owning the pid file

When AppInit(node) returns false before AppInitMain() calls CreatePidFile()—for example, a second dashd fails AppInitLockDataDirectory() because another process holds the datadir—this unconditional Shutdown(node) still runs. Shutdown() unconditionally removes GetPidFile(*node.args), so the failed second start can delete the running daemon's dashd.pid; fresh evidence in this revision is that this call remains unconditional and the tree has no pid-ownership guard.

AGENTS.md reference: AGENTS.md:L169-L171

Useful? React with 👍 / 👎.


return node.exit_status;
}
74 changes: 74 additions & 0 deletions src/common/init.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2023 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 <common/init.h>
#include <chainparams.h>
#include <util/fs.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/translation.h>

#include <algorithm>
#include <exception>
#include <optional>

namespace common {
std::optional<ConfigError> InitConfig(ArgsManager& args, SettingsAbortFn settings_abort_fn)
{
try {
if (!CheckDataDirOption(args)) {
return ConfigError{ConfigStatus::FAILED, strprintf(_("Specified data directory \"%s\" does not exist."), args.GetArg("-datadir", ""))};
}
std::string error;
if (!args.ReadConfigFiles(error, true)) {
return ConfigError{ConfigStatus::FAILED, strprintf(_("Error reading configuration file: %s"), error)};
}

// Check for chain settings (Params() calls are only valid after this clause)
SelectParams(args.GetChainName());

// Create datadir if it does not exist.
const auto base_path{args.GetDataDirBase()};
if (!fs::exists(base_path)) {
// When creating a *new* datadir, also create a "wallets" subdirectory,
// whether or not the wallet is enabled now, so if the wallet is enabled
// in the future, it will use the "wallets" subdirectory for creating
// and listing wallets, rather than the top-level directory where
// wallets could be mixed up with other files. For backwards
// compatibility, wallet code will use the "wallets" subdirectory only
// if it already exists, but never create it itself. There is discussion
// in https://github.com/bitcoin/bitcoin/issues/16220 about ways to
// change wallet code so it would no longer be necessary to create
// "wallets" subdirectories here.
fs::create_directories(base_path / "wallets");
}
const auto net_path{args.GetDataDirNet()};
if (!fs::exists(net_path)) {
fs::create_directories(net_path / "wallets");
}

// Create settings.json if -nosettings was not specified.
if (args.GetSettingsPath()) {
std::vector<std::string> details;
if (!args.ReadSettingsFile(&details)) {
const bilingual_str& message = _("Settings file could not be read");
if (!settings_abort_fn) {
return ConfigError{ConfigStatus::FAILED, message, details};
} else if (settings_abort_fn(message, details)) {
return ConfigError{ConfigStatus::ABORTED, message, details};
} else {
details.clear(); // User chose to ignore the error and proceed.
}
}
if (!args.WriteSettingsFile(&details)) {
const bilingual_str& message = _("Settings file could not be written");
return ConfigError{ConfigStatus::FAILED_WRITE, message, details};
}
}
} catch (const std::exception& e) {
return ConfigError{ConfigStatus::FAILED, Untranslated(e.what())};
}
return {};
}
} // namespace common
39 changes: 39 additions & 0 deletions src/common/init.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2023 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_COMMON_INIT_H
#define BITCOIN_COMMON_INIT_H

#include <util/translation.h>

#include <functional>
#include <optional>
#include <string>
#include <vector>

class ArgsManager;

namespace common {
enum class ConfigStatus {
FAILED, //!< Failed generically.
FAILED_WRITE, //!< Failed to write settings.json
ABORTED, //!< Aborted by user
};

struct ConfigError {
ConfigStatus status;
bilingual_str message{};
std::vector<std::string> details{};
};

//! Callback function to let the user decide whether to abort loading if
//! settings.json file exists and can't be parsed, or to ignore the error and
//! overwrite the file.
using SettingsAbortFn = std::function<bool(const bilingual_str& message, const std::vector<std::string>& details)>;

/* Read config files, and create datadir and settings.json if they don't exist. */
std::optional<ConfigError> InitConfig(ArgsManager& args, SettingsAbortFn settings_abort_fn = nullptr);
} // namespace common

#endif // BITCOIN_COMMON_INIT_H
5 changes: 1 addition & 4 deletions src/index/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};

void BaseIndex::FatalErrorImpl(const std::string& message)
{
SetMiscWarning(Untranslated(message));
LogPrintf("*** %s\n", message);
AbortError(_("A fatal internal error occurred, see debug.log for details"));
StartShutdown();
AbortNode(message);
}

CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash)
Expand Down
6 changes: 3 additions & 3 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ static const char* BITCOIN_PID_FILENAME = "dashd.pid";

static fs::path GetPidFile(const ArgsManager& args)
{
return AbsPathForConfigVal(args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
return AbsPathForConfigVal(args, args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
}

[[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
Expand Down Expand Up @@ -1110,7 +1110,7 @@ std::set<BlockFilterType> g_enabled_filter_types;
std::terminate();
};

bool AppInitBasicSetup(const ArgsManager& args)
bool AppInitBasicSetup(const ArgsManager& args, std::atomic<int>& exit_status)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
Expand All @@ -1124,7 +1124,7 @@ bool AppInitBasicSetup(const ArgsManager& args)
// Enable heap terminate-on-corruption
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
#endif
if (!InitShutdownState()) {
if (!InitShutdownState(exit_status)) {
return InitError(Untranslated("Initializing wait-for-shutdown state failed."));
}

Expand Down
Loading
Loading