Skip to content

Commit

Permalink
Add <datadir>/settings.json persistent settings storage.
Browse files Browse the repository at this point in the history
Persistent settings are used in followup PRs bitcoin#15936 to unify gui settings
between bitcoin-qt and bitcoind, and bitcoin#15937 to add a load_on_startup flag to
the loadwallet RPC and maintain a dynamic list of wallets that should be loaded
on startup that also can be shared between bitcoind and bitcoin-qt.
  • Loading branch information
ryanofsky committed May 7, 2019
1 parent 9f5e8ba commit 70675c3
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 4 deletions.
3 changes: 2 additions & 1 deletion doc/files.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Filename | Description
--------------------|----------------------------------------------------------------------------------------------------------------------------
banlist.dat | stores the IPs/Subnets of banned nodes
bitcoin.conf | contains configuration settings for bitcoind or bitcoin-qt
bitcoin.conf | user-defined settings for bitcoind and bitcoin-qt; file is read-only and must be created manually
bitcoind.pid | stores the process id of bitcoind while running
blocks/blk000??.dat | block data (custom, 128 MiB per file); since 0.8.0
blocks/rev000??.dat | block undo data (custom); since 0.8.0 (format changed since pre-0.8)
Expand All @@ -14,6 +14,7 @@ fee_estimates.dat | stores statistics used to estimate minimum transaction fee
indexes/txindex/* | optional transaction index database (LevelDB); since 0.17.0
mempool.dat | dump of the mempool's transactions; since 0.14.0
peers.dat | peer IP address database (custom format); since 0.7.0
setting.json | read-write settings set in GUI or RPC interfaces that augment manual settings from bitcoin.conf
wallet.dat | personal wallet (BDB) with keys and transactions; moved to wallets/ directory on new installs since 0.16.0
wallets/database/* | BDB database environment; used for wallets since 0.16.0
wallets/db.log | wallet database log file; since 0.16.0
Expand Down
5 changes: 5 additions & 0 deletions src/bitcoind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ static bool AppInit(int argc, char* argv[])
}
}

if (!gArgs.ReadSettingsFile()) {
fprintf(stderr, "Error reading settings file\n");
return false;
}

// -server defaults to true for bitcoind but not for the GUI so do this here
gArgs.SoftSetBoolArg("-server", true);
// Set this early so that parameter interactions go to console
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class NodeImpl : public Node
bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); }
bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); }
void selectParams(const std::string& network) override { SelectParams(network); }
bool readSettingsFile() override { return gArgs.ReadSettingsFile(); }
uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); }
uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); }
std::string getNetwork() override { return Params().NetworkIDString(); }
Expand Down
5 changes: 5 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class Node
//! Choose network parameters.
virtual void selectParams(const std::string& network) = 0;

//! Load <datadir>/settings.json file with saved settings. This needs to be
//! called after selectParams() because the settings file is
//! network-specific.
virtual bool readSettingsFile() = 0;

//! Get the (assumed) blockchain size.
virtual uint64_t getAssumedBlockchainSize() = 0;

Expand Down
5 changes: 5 additions & 0 deletions src/qt/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,11 @@ int GuiMain(int argc, char* argv[])
// Parse URIs on command line -- this can affect Params()
PaymentServer::ipcParseCommandLine(*node, argc, argv);
#endif
if (!node->readSettingsFile()) {
QMessageBox::critical(nullptr, QObject::tr(PACKAGE_NAME),
QObject::tr("Error: Cannot read settings file."));
return EXIT_FAILURE;
}

QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString())));
assert(!networkStyle.isNull());
Expand Down
3 changes: 3 additions & 0 deletions src/util/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ static void MergeSettings(const Settings& settings, const std::string& section,
if (auto* value = FindKey(settings.command_line_options, name)) {
fn(Source(SettingsSpan(*value)));
}
if (auto* value = FindKey(settings.rw_settings, name)) {
fn(Source(SettingsSpan(value)));
}
if (!section.empty()) {
if (auto* map = FindKey(settings.ro_config, section)) {
if (auto* value = FindKey(*map, name)) {
Expand Down
7 changes: 4 additions & 3 deletions src/util/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ namespace util {
//! substituted if there's a need to move away from UniValue.
using SettingsValue = UniValue;

//! Stored bitcoin settings. This struct combines settings from the command line
//! and a read-only configuration file.
//! Stored bitcoin settings. This struct combines settings from the command line,
//! a read-only configuration file, and a read-write runtime settings file.
struct Settings {
std::map<std::string, SettingsValue> forced_settings;
std::map<std::string, std::vector<SettingsValue>> command_line_options;
std::map<std::string, SettingsValue> rw_settings;
std::map<std::string, std::map<std::string, std::vector<SettingsValue>>> ro_config;
};

//! Get settings value from combined sources: forced settings, command line
//! arguments and the read-only config file.
//! arguments, runtime read-write settings, and the read-only config file.
//!
//! @param ignore_default_section_config - ignore values set in the top-level
//! section of the config file.
Expand Down
66 changes: 66 additions & 0 deletions src/util/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,72 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
return !ArgsManagerHelper::Get(*this, strArg).isNull();
}

static fs::path GetSettingsFile(bool temp = false)
{
return fs::absolute(temp ? "settings.json.tmp" : "settings.json", GetDataDir(/* net_specific= */ true));
}

bool ArgsManager::ReadSettingsFile()
{
fsbridge::ifstream file;
fs::path filepath = GetSettingsFile(/* temp= */ false);
file.open(filepath);
if (!file.is_open()) return true; // Ok for file not to exist.

util::SettingsValue in;
if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
LogPrintf("Error: Unable to parse settings file %s\n", filepath.string());
return false;
}
if (file.fail()) {
LogPrintf("Error reading settings file %s\n", filepath.string());
return false;
}
file.close();

if (!in.isObject()) {
LogPrintf("Error: Settings file %s is not in expected key-value format.\n", filepath.string());
return false;
}

LOCK(cs_args);
m_settings.rw_settings.clear();
const std::vector<std::string>& keys = in.getKeys();
const std::vector<UniValue>& values = in.getValues();
for (size_t i = 0; i < keys.size(); ++i) {
m_settings.rw_settings.emplace(keys[i], values[i]);
}
return true;
}

bool ArgsManager::WriteSettingsFile() const
{
util::SettingsValue out(util::SettingsValue::VOBJ);
{
LOCK(cs_args);
for (const auto& value : m_settings.rw_settings) {
out.__pushKV(value.first, value.second);
}
}

fsbridge::ofstream file;
fs::path filepath_tmp = GetSettingsFile(/* temp= */ true);
file.open(filepath_tmp);
if (file.fail()) {
LogPrintf("Error: Unable to open settings file %s for writing\n", filepath_tmp.string());
return false;
}
file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
file.close();

fs::path filepath = GetSettingsFile(/* temp= */ false);
if (!RenameOver(filepath_tmp, filepath)) {
LogPrintf("Error: Unable to rename settings file %s to %s\n", filepath_tmp.string(), filepath.string());
return false;
}
return true;
}

bool ArgsManager::IsArgNegated(const std::string& strArg) const
{
return ArgsManagerHelper::Get(*this, strArg).isFalse();
Expand Down
21 changes: 21 additions & 0 deletions src/util/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,27 @@ class ArgsManager
* Check whether we know of this arg
*/
bool IsArgKnown(const std::string& key) const;

/**
* Load <datadir>/settings.json file with saved settings. This needs to be
* called after SelectParams() because the settings file is network-specific.
*/
bool ReadSettingsFile();

/**
* Save <datadir>/settings.json file with persistent settings.
*/
bool WriteSettingsFile() const;

/**
* Access settings with lock held.
*/
template <typename Fn>
void LockSettings(Fn&& fn)
{
LOCK(cs_args);
fn(m_settings);
}
};

extern ArgsManager gArgs;
Expand Down

0 comments on commit 70675c3

Please sign in to comment.