diff --git a/src/interfaces/node.h b/src/interfaces/node.h --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -11,6 +11,7 @@ #include // For Network #include // For SecureString +#include // For util::SettingsValue #include #include @@ -85,6 +86,23 @@ //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; + //! Return whether a particular setting in /settings.json + //! would be ignored because it is also specified in the command line. + virtual bool isPersistentSettingIgnored(const std::string &name) = 0; + + //! Return setting value from /settings.json or bitcoin.conf. + virtual util::SettingsValue + getPersistentSetting(const std::string &name) = 0; + + //! Update a setting in /settings.json. + virtual void updateRwSetting(const std::string &name, + const util::SettingsValue &value) = 0; + + //! Force a setting value to be applied, overriding any other configuration + //! source, but not being persisted. + virtual void forceSetting(const std::string &name, + const util::SettingsValue &value) = 0; + //! Map port. virtual void mapPort(bool use_upnp, bool use_natpmp) = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -100,6 +100,41 @@ } } bool shutdownRequested() override { return ShutdownRequested(); } + bool isPersistentSettingIgnored(const std::string &name) override { + bool ignored = false; + gArgs.LockSettings([&](util::Settings &settings) { + if (auto *options = + util::FindKey(settings.command_line_options, name)) { + ignored = !options->empty(); + } + }); + return ignored; + } + util::SettingsValue + getPersistentSetting(const std::string &name) override { + return gArgs.GetPersistentSetting(name); + } + void updateRwSetting(const std::string &name, + const util::SettingsValue &value) override { + gArgs.LockSettings([&](util::Settings &settings) { + if (value.isNull()) { + settings.rw_settings.erase(name); + } else { + settings.rw_settings[name] = value; + } + }); + gArgs.WriteSettingsFile(); + } + void forceSetting(const std::string &name, + const util::SettingsValue &value) override { + gArgs.LockSettings([&](util::Settings &settings) { + if (value.isNull()) { + settings.forced_settings.erase(name); + } else { + settings.forced_settings[name] = value; + } + }); + } void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -114,11 +114,13 @@ static void CheckValues(const util::Settings &settings, const std::string &single_val, const std::string &list_val) { - util::SettingsValue single_value = - GetSetting(settings, "section", "name", false, false); + util::SettingsValue single_value = GetSetting( + settings, "section", "name", /*ignore_default_section_config=*/false, + /*ignore_nonpersistent=*/false, /*get_chain_name=*/false); util::SettingsValue list_value(util::SettingsValue::VARR); for (const auto &item : - GetSettingsList(settings, "section", "name", false)) { + GetSettingsList(settings, "section", "name", + /*ignore_default_section_config=*/false)) { list_value.push_back(item); } BOOST_CHECK_EQUAL(single_value.write().c_str(), single_val); @@ -150,13 +152,21 @@ BOOST_AUTO_TEST_CASE(NullOverride) { util::Settings settings; settings.command_line_options["name"].push_back("value"); - BOOST_CHECK_EQUAL( - R"("value")", - GetSetting(settings, "section", "name", false, false).write().c_str()); + BOOST_CHECK_EQUAL(R"("value")", + GetSetting(settings, "section", "name", + /*ignore_default_section_config=*/false, + /*ignore_nonpersistent=*/false, + /*get_chain_name=*/false) + .write() + .c_str()); settings.forced_settings["name"] = {}; - BOOST_CHECK_EQUAL( - R"(null)", - GetSetting(settings, "section", "name", false, false).write().c_str()); + BOOST_CHECK_EQUAL(R"(null)", + GetSetting(settings, "section", "name", + /*ignore_default_section_config=*/false, + /*ignore_nonpersistent=*/false, + /*get_chain_name=*/false) + .write() + .c_str()); } // Test different ways settings can be merged, and verify results. This test can @@ -247,7 +257,7 @@ desc += " || "; desc += GetSetting(settings, network, name, ignore_default_section_config, - /* get_chain_name= */ false) + /*ignore_nonpersistent=*/false, /*get_chain_name=*/false) .write(); desc += " |"; for (const auto &s : GetSettingsList(settings, network, name, diff --git a/src/util/settings.h b/src/util/settings.h --- a/src/util/settings.h +++ b/src/util/settings.h @@ -57,12 +57,17 @@ //! @param ignore_default_section_config - ignore values in the default section //! of the config file (part before any //! [section] keywords) +//! @param ignore_nonpersistent - ignore non-persistent settings values (forced +//! settings values and values specified on the +//! command line). Only return settings in the +//! read-only config and read-write settings +//! files. //! @param get_chain_name - enable special backwards compatible behavior //! for GetChainName SettingsValue GetSetting(const Settings &settings, const std::string §ion, const std::string &name, bool ignore_default_section_config, - bool get_chain_name); + bool ignore_nonpersistent, bool get_chain_name); //! Get combined setting value similar to GetSetting(), except if setting was //! specified multiple times, return a list of all the values specified. diff --git a/src/util/settings.cpp b/src/util/settings.cpp --- a/src/util/settings.cpp +++ b/src/util/settings.cpp @@ -142,7 +142,7 @@ SettingsValue GetSetting(const Settings &settings, const std::string §ion, const std::string &name, bool ignore_default_section_config, - bool get_chain_name) { + bool ignore_nonpersistent, bool get_chain_name) { SettingsValue result; // Done merging any more settings sources. bool done = false; @@ -184,6 +184,12 @@ return; } + // Ignore nonpersistent settings if requested. + if (ignore_nonpersistent && + (source == Source::COMMAND_LINE || source == Source::FORCED)) { + return; + } + // Skip negated command line settings. if (skip_negated_command_line && span.last_negated()) { return; diff --git a/src/util/system.h b/src/util/system.h --- a/src/util/system.h +++ b/src/util/system.h @@ -142,6 +142,10 @@ int m_line; }; +std::string SettingToString(const util::SettingsValue &, const std::string &); +int64_t SettingToInt(const util::SettingsValue &, int64_t); +bool SettingToBool(const util::SettingsValue &, bool); + class ArgsManager { public: enum Flags { @@ -424,6 +428,12 @@ */ bool WriteSettingsFile(std::vector *errors = nullptr) const; + /** + * Get current setting from config file or read/write settings file, + * ignoring nonpersistent command line or forced settings values. + */ + util::SettingsValue GetPersistentSetting(const std::string &name) const; + /** * Access settings with lock held. */ diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -582,6 +582,14 @@ return true; } +util::SettingsValue +ArgsManager::GetPersistentSetting(const std::string &name) const { + LOCK(cs_args); + return util::GetSetting( + m_settings, m_network, name, !UseDefaultSection("-" + name), + /*ignore_nonpersistent=*/true, /*get_chain_name=*/false); +} + bool ArgsManager::IsArgNegated(const std::string &strArg) const { return GetSetting(strArg).isFalse(); } @@ -589,6 +597,11 @@ std::string ArgsManager::GetArg(const std::string &strArg, const std::string &strDefault) const { const util::SettingsValue value = GetSetting(strArg); + return SettingToString(value, strDefault); +} + +std::string SettingToString(const util::SettingsValue &value, + const std::string &strDefault) { return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" @@ -598,6 +611,10 @@ int64_t ArgsManager::GetIntArg(const std::string &strArg, int64_t nDefault) const { const util::SettingsValue value = GetSetting(strArg); + return SettingToInt(value, nDefault); +} + +int64_t SettingToInt(const util::SettingsValue &value, int64_t nDefault) { return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 @@ -607,6 +624,10 @@ bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const { const util::SettingsValue value = GetSetting(strArg); + return SettingToBool(value, fDefault); +} + +bool SettingToBool(const util::SettingsValue &value, bool fDefault) { return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); @@ -1051,9 +1072,10 @@ auto get_net = [&](const std::string &arg) { LOCK(cs_args); util::SettingsValue value = - util::GetSetting(m_settings, /* section= */ "", SettingName(arg), - /* ignore_default_section_config= */ false, - /* get_chain_name= */ true); + util::GetSetting(m_settings, /*section=*/"", SettingName(arg), + /*ignore_default_section_config=*/false, + /*ignore_nonpersistent=*/false, + /*get_chain_name=*/true); return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); @@ -1085,7 +1107,8 @@ LOCK(cs_args); return util::GetSetting(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), - /* get_chain_name= */ false); + /*ignore_nonpersistent=*/false, + /*get_chain_name=*/false); } std::vector