Deduplicate settings merge code

Get rid of settings merging code in util/system.cpp repeated 5 places,
inconsistently:

- ArgsManagerHelper::GetArg
- ArgsManagerHelper::GetNetBoolArg
- ArgsManager::GetArgs
- ArgsManager::IsArgNegated
- ArgsManager::GetUnsuitableSectionOnlyArgs

Having settings merging code separated from parsing simplifies parsing somewhat
(for example negated values can simply be represented as false values instead
of partially cleared or emply placeholder lists).

Having settings merge happen one place instead of 5 makes it easier to add new
settings sources and harder to introduce new inconsistencies in the way
settings are merged.

This commit does not change behavior in any way.
This commit is contained in:
Russell Yanofsky committed 2019-11-07 23:08:22 -04:00
1 parent 9dcb952fe5
commit 7f40528cd5
3 files changed
+170 -268

No files matched your search

+49 -42
View File
@@ -17,6 +17,7 @@
#include <stdint.h> #include <stdint.h>
#include <thread> #include <thread>
#include <univalue.h>
#include <utility> #include <utility>
#include <vector> #include <vector>
#ifndef WIN32 #ifndef WIN32
@@ -166,14 +167,12 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
struct TestArgsManager : public ArgsManager struct TestArgsManager : public ArgsManager
{ {
TestArgsManager() { m_network_only_args.clear(); } TestArgsManager() { m_network_only_args.clear(); }
std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; }
std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; }
void ReadConfigString(const std::string str_config) void ReadConfigString(const std::string str_config)
{ {
std::istringstream streamConfig(str_config); std::istringstream streamConfig(str_config);
{ {
LOCK(cs_args); LOCK(cs_args);
m_config_args.clear(); m_settings.ro_config.clear();
m_config_sections.clear(); m_config_sections.clear();
} }
std::string error; std::string error;
@@ -193,6 +192,7 @@ struct TestArgsManager : public ArgsManager
using ArgsManager::ReadConfigStream; using ArgsManager::ReadConfigStream;
using ArgsManager::cs_args; using ArgsManager::cs_args;
using ArgsManager::m_network; using ArgsManager::m_network;
using ArgsManager::m_settings;
}; };
BOOST_AUTO_TEST_CASE(util_ParseParameters) BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -206,28 +206,29 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
std::string error; std::string error;
LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, ccc, d}); testArgs.SetupArgs({a, b, ccc, d});
BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error)); BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error)); BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error)); BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// expectation: -ignored is ignored (program name argument), // expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after // -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing) // a non-option argument (non-GNU option parsing)
BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
&& !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc") BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc")
&& !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d")); && !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d"));
BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1); BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == ""); BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == "");
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2); BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2);
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument"); BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument");
BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple"); BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple");
BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
} }
@@ -298,6 +299,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
const char *argv_test[] = { const char *argv_test[] = {
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"}; "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error; std::string error;
LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, c, d, e, f}); testArgs.SetupArgs({a, b, c, d, e, f});
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error)); BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
@@ -306,8 +308,8 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
// Nothing else should be in the map // Nothing else should be in the map
BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 && BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 &&
testArgs.GetConfigArgs().empty()); testArgs.m_settings.ro_config.empty());
// The -no prefix should get stripped on the way in. // The -no prefix should get stripped on the way in.
BOOST_CHECK(!testArgs.IsArgSet("-nob")); BOOST_CHECK(!testArgs.IsArgSet("-nob"));
@@ -403,6 +405,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
"iii=2\n"; "iii=2\n";
TestArgsManager test_args; TestArgsManager test_args;
LOCK(test_args.cs_args);
const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL); const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL); const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING); const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING);
@@ -419,22 +422,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map // expectation: a, b, ccc, d, fff, ggg, h, i end up in map
// so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii
BOOST_CHECK(test_args.GetOverrideArgs().empty()); BOOST_CHECK(test_args.m_settings.command_line_options.empty());
BOOST_CHECK(test_args.GetConfigArgs().size() == 13); BOOST_CHECK(test_args.m_settings.ro_config.size() == 3);
BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8);
BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
BOOST_CHECK(test_args.GetConfigArgs().count("-a") BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")
&& test_args.GetConfigArgs().count("-b") && test_args.m_settings.ro_config[""].count("b")
&& test_args.GetConfigArgs().count("-ccc") && test_args.m_settings.ro_config[""].count("ccc")
&& test_args.GetConfigArgs().count("-d") && test_args.m_settings.ro_config[""].count("d")
&& test_args.GetConfigArgs().count("-fff") && test_args.m_settings.ro_config[""].count("fff")
&& test_args.GetConfigArgs().count("-ggg") && test_args.m_settings.ro_config[""].count("ggg")
&& test_args.GetConfigArgs().count("-h") && test_args.m_settings.ro_config[""].count("h")
&& test_args.GetConfigArgs().count("-i") && test_args.m_settings.ro_config[""].count("i")
); );
BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc") BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")
&& test_args.GetConfigArgs().count("-sec1.h") && test_args.m_settings.ro_config["sec1"].count("h")
&& test_args.GetConfigArgs().count("-sec2.ccc") && test_args.m_settings.ro_config["sec2"].count("ccc")
&& test_args.GetConfigArgs().count("-sec2.iii") && test_args.m_settings.ro_config["sec2"].count("iii")
); );
BOOST_CHECK(test_args.IsArgSet("-a") BOOST_CHECK(test_args.IsArgSet("-a")
@@ -573,24 +579,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_AUTO_TEST_CASE(util_GetArg)
{ {
TestArgsManager testArgs; TestArgsManager testArgs;
testArgs.GetOverrideArgs().clear(); LOCK(testArgs.cs_args);
testArgs.GetOverrideArgs()["strtest1"] = {"string..."}; testArgs.m_settings.command_line_options.clear();
testArgs.m_settings.command_line_options["strtest1"] = {"string..."};
// strtest2 undefined on purpose // strtest2 undefined on purpose
testArgs.GetOverrideArgs()["inttest1"] = {"12345"}; testArgs.m_settings.command_line_options["inttest1"] = {"12345"};
testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"}; testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"};
// inttest3 undefined on purpose // inttest3 undefined on purpose
testArgs.GetOverrideArgs()["booltest1"] = {""}; testArgs.m_settings.command_line_options["booltest1"] = {""};
// booltest2 undefined on purpose // booltest2 undefined on purpose
testArgs.GetOverrideArgs()["booltest3"] = {"0"}; testArgs.m_settings.command_line_options["booltest3"] = {"0"};
testArgs.GetOverrideArgs()["booltest4"] = {"1"}; testArgs.m_settings.command_line_options["booltest4"] = {"1"};
// priorities // priorities
testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"}; testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"};
testArgs.GetConfigArgs()["pritest2"] = {"a", "b"}; testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"};
testArgs.GetOverrideArgs()["pritest3"] = {"a"}; testArgs.m_settings.command_line_options["pritest3"] = {"a"};
testArgs.GetConfigArgs()["pritest3"] = {"b"}; testArgs.m_settings.ro_config[""]["pritest3"] = {"b"};
testArgs.GetOverrideArgs()["pritest4"] = {"a","b"}; testArgs.m_settings.command_line_options["pritest4"] = {"a","b"};
testArgs.GetConfigArgs()["pritest4"] = {"c","d"}; testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"};
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
+119 -224
View File
@@ -63,6 +63,7 @@
#endif #endif
#include <thread> #include <thread>
#include <univalue.h>
// Application startup time (used for uptime calculation) // Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime(); const int64_t nStartupTime = GetTime();
@@ -161,11 +162,14 @@ static bool InterpretBool(const std::string& strValue)
return (atoi(strValue) != 0); return (atoi(strValue) != 0);
} }
static std::string SettingName(const std::string& arg)
{
return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
}
/** Internal helper functions for ArgsManager */ /** Internal helper functions for ArgsManager */
class ArgsManagerHelper { class ArgsManagerHelper {
public: public:
typedef std::map<std::string, std::vector<std::string>> MapArgs;
/** Determine whether to use config settings in the default section, /** Determine whether to use config settings in the default section,
* See also comments around ArgsManager::ArgsManager() below. */ * See also comments around ArgsManager::ArgsManager() below. */
static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
@@ -173,91 +177,10 @@ public:
return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0);
} }
/** Convert regular argument into the network-specific setting */ static util::SettingsValue Get(const ArgsManager& am, const std::string& arg)
static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg)
{
assert(arg.length() > 1 && arg[0] == '-');
return "-" + am.m_network + "." + arg.substr(1);
}
/** Find arguments in a map and add them to a vector */
static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg)
{
auto it = map_args.find(arg);
if (it != map_args.end()) {
res.insert(res.end(), it->second.begin(), it->second.end());
}
}
/** Return true/false if an argument is set in a map, and also
* return the first (or last) of the possibly multiple values it has
*/
static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false)
{
auto it = map_args.find(arg);
if (it == map_args.end() || it->second.empty()) {
return std::make_pair(false, std::string());
}
if (getLast) {
return std::make_pair(true, it->second.back());
} else {
return std::make_pair(true, it->second.front());
}
}
/* Get the string value of an argument, returning a pair of a boolean
* indicating the argument was found, and the value for the argument
* if it was found (or the empty string if not found).
*/
static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg)
{ {
LOCK(am.cs_args); LOCK(am.cs_args);
std::pair<bool,std::string> found_result(false, std::string()); return GetSetting(am.m_settings, am.m_network, SettingName(arg), !UseDefaultSection(am, arg), /* get_chain_name= */ false);
// We pass "true" to GetArgHelper in order to return the last
// argument value seen from the command line (so "bitcoind -foo=bar
// -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
found_result = GetArgHelper(am.m_override_args, arg, true);
if (found_result.first) {
return found_result;
}
// But in contrast we return the first argument seen in a config file,
// so "foo=bar \n foo=baz" in the config file gives
// GetArg(am,"foo")={true,"bar"}
if (!am.m_network.empty()) {
found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
if (found_result.first) {
return found_result;
}
}
if (UseDefaultSection(am, arg)) {
found_result = GetArgHelper(am.m_config_args, arg);
if (found_result.first) {
return found_result;
}
}
return found_result;
}
/* Special test for -testnet and -regtest args, because we
* don't want to be confused by craziness like "[regtest] testnet=1"
*/
static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
{
std::pair<bool,std::string> found_result(false,std::string());
found_result = GetArgHelper(am.m_override_args, net_arg, true);
if (!found_result.first) {
found_result = GetArgHelper(am.m_config_args, net_arg, true);
if (!found_result.first) {
return false; // not set
}
}
return InterpretBool(found_result.second); // is set, so evaluate
} }
}; };
@@ -268,13 +191,12 @@ public:
* checks whether there was a double-negative (-nofoo=0 -> -foo=1). * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
* *
* If there was not a double negative, it removes the "no" from the key * If there was not a double negative, it removes the "no" from the key
* and clears the args vector to indicate a negated option. * and returns false.
* *
* If there was a double negative, it removes "no" from the key, sets the * If there was a double negative, it removes "no" from the key, and
* value to "1" and pushes the key and the updated value to the args vector. * returns true.
* *
* If there was no "no", it leaves key and value untouched and pushes them * If there was no "no", it returns the string value untouched.
* to the args vector.
* *
* Where an option was negated can be later checked using the * Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable * IsArgNegated() method. One use case for this is to have a way to disable
@@ -282,34 +204,39 @@ public:
* that debug log output is not sent to any file at all). * that debug log output is not sent to any file at all).
*/ */
NODISCARD static bool InterpretOption(std::string key, std::string val, unsigned int flags, static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
std::map<std::string, std::vector<std::string>>& args,
std::string& error)
{ {
assert(key[0] == '-'); // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
size_t option_index = key.find('.'); size_t option_index = key.find('.');
if (option_index == std::string::npos) { if (option_index != std::string::npos) {
option_index = 1; section = key.substr(0, option_index);
} else { key.erase(0, option_index + 1);
++option_index;
} }
if (key.substr(option_index, 2) == "no") { if (key.substr(0, 2) == "no") {
key.erase(option_index, 2); key.erase(0, 2);
if (flags & ArgsManager::ALLOW_BOOL) { // Double negatives like -nofoo=0 are supported (but discouraged)
if (InterpretBool(val)) { if (!InterpretBool(value)) {
args[key].clear(); LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
return true; return true;
}
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
val = "1";
} else {
error = strprintf("Negating of %s is meaningless and therefore forbidden", key);
return false;
} }
return false;
}
return value;
}
/**
* Check settings value validity according to flags.
*
* TODO: Add more meaningful error checks here in the future
* See "here's how the flags are meant to behave" in
* https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
*/
static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
{
if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
return false;
} }
args[key].push_back(val);
return true; return true;
} }
@@ -331,22 +258,9 @@ const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {}; if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
for (const auto& arg : m_network_only_args) { for (const auto& arg : m_network_only_args) {
std::pair<bool, std::string> found_result; if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
unsuitables.insert(arg);
// if this option is overridden it's fine }
found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
if (found_result.first) continue;
// if there's a network-specific value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
if (found_result.first) continue;
// if there isn't a default value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
if (!found_result.first) continue;
// otherwise, issue a warning
unsuitables.insert(arg);
} }
return unsuitables; return unsuitables;
} }
@@ -375,7 +289,7 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error) bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
{ {
LOCK(cs_args); LOCK(cs_args);
m_override_args.clear(); m_settings.command_line_options.clear();
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::string key(argv[i]); std::string key(argv[i]);
@@ -408,49 +322,44 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
if (key.length() > 1 && key[1] == '-') if (key.length() > 1 && key[1] == '-')
key.erase(0, 1); key.erase(0, 1);
// Transform -foo to foo
key.erase(0, 1);
std::string section;
util::SettingsValue value = InterpretOption(section, key, val);
const unsigned int flags = FlagsOfKnownArg(key); const unsigned int flags = FlagsOfKnownArg(key);
if (flags) { if (flags) {
if (!InterpretOption(key, val, flags, m_override_args, error)) { if (!CheckValid(key, value, flags, error)) {
return false; return false;
} }
// Weird behavior preserved for backwards compatibility: command
// line options with section prefixes are allowed but ignored. It
// would be better if these options triggered the Invalid parameter
// error below.
if (section.empty()) {
m_settings.command_line_options[key].push_back(value);
}
} else { } else {
error = strprintf("Invalid parameter %s", key); error = strprintf("Invalid parameter -%s", key);
return false; return false;
} }
} }
// we do not allow -includeconf from command line, so we clear it here // we do not allow -includeconf from command line, so we clear it here
auto it = m_override_args.find("-includeconf"); bool success = true;
if (it != m_override_args.end()) { if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
if (it->second.size() > 0) { for (const auto& include : util::SettingsSpan(*includes)) {
for (const auto& ic : it->second) { error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n";
error += "-includeconf cannot be used from commandline; -includeconf=" + ic + "\n"; success = false;
}
return false;
} }
} }
return true; return success;
} }
unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
{ {
assert(key[0] == '-');
size_t option_index = key.find('.');
if (option_index == std::string::npos) {
option_index = 1;
} else {
++option_index;
}
if (key.substr(option_index, 2) == "no") {
option_index += 2;
}
const std::string base_arg_name = '-' + key.substr(option_index);
LOCK(cs_args); LOCK(cs_args);
for (const auto& arg_map : m_available_args) { for (const auto& arg_map : m_available_args) {
const auto search = arg_map.second.find(base_arg_name); const auto search = arg_map.second.find('-' + key);
if (search != arg_map.second.end()) { if (search != arg_map.second.end()) {
return search->second.m_flags; return search->second.m_flags;
} }
@@ -460,69 +369,42 @@ unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
{ {
std::vector<std::string> result = {};
if (IsArgNegated(strArg)) return result; // special case
LOCK(cs_args); LOCK(cs_args);
bool ignore_default_section_config = !ArgsManagerHelper::UseDefaultSection(*this, strArg);
ArgsManagerHelper::AddArgs(result, m_override_args, strArg); std::vector<std::string> result;
if (!m_network.empty()) { for (const util::SettingsValue& value :
ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg)); util::GetSettingsList(m_settings, m_network, SettingName(strArg), ignore_default_section_config)) {
result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
} }
if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
}
return result; return result;
} }
bool ArgsManager::IsArgSet(const std::string& strArg) const bool ArgsManager::IsArgSet(const std::string& strArg) const
{ {
if (IsArgNegated(strArg)) return true; // special case return !ArgsManagerHelper::Get(*this, strArg).isNull();
return ArgsManagerHelper::GetArg(*this, strArg).first;
} }
bool ArgsManager::IsArgNegated(const std::string& strArg) const bool ArgsManager::IsArgNegated(const std::string& strArg) const
{ {
LOCK(cs_args); return ArgsManagerHelper::Get(*this, strArg).isFalse();
const auto& ov = m_override_args.find(strArg);
if (ov != m_override_args.end()) return ov->second.empty();
if (!m_network.empty()) {
const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
if (cfs != m_config_args.end()) return cfs->second.empty();
}
const auto& cf = m_config_args.find(strArg);
if (cf != m_config_args.end()) return cf->second.empty();
return false;
} }
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{ {
if (IsArgNegated(strArg)) return "0"; const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
if (found_res.first) return found_res.second;
return strDefault;
} }
int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
{ {
if (IsArgNegated(strArg)) return 0; const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
if (found_res.first) return atoi64(found_res.second);
return nDefault;
} }
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
{ {
if (IsArgNegated(strArg)) return false; const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
if (found_res.first) return InterpretBool(found_res.second);
return fDefault;
} }
bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue) bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
@@ -544,7 +426,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue) void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
{ {
LOCK(cs_args); LOCK(cs_args);
m_override_args[strArg] = {strValue}; m_settings.forced_settings[SettingName(strArg)] = strValue;
} }
void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat) void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
@@ -860,12 +742,15 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false; return false;
} }
for (const std::pair<std::string, std::string>& option : options) { for (const std::pair<std::string, std::string>& option : options) {
const std::string strKey = std::string("-") + option.first; std::string section;
const unsigned int flags = FlagsOfKnownArg(strKey); std::string key = option.first;
util::SettingsValue value = InterpretOption(section, key, option.second);
const unsigned int flags = FlagsOfKnownArg(key);
if (flags) { if (flags) {
if (!InterpretOption(strKey, option.second, flags, m_config_args, error)) { if (!CheckValid(key, value, flags, error)) {
return false; return false;
} }
m_settings.ro_config[section][key].push_back(value);
} else { } else {
if (ignore_invalid_keys) { if (ignore_invalid_keys) {
LogPrintf("Ignoring unknown configuration value %s\n", option.first); LogPrintf("Ignoring unknown configuration value %s\n", option.first);
@@ -882,7 +767,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{ {
{ {
LOCK(cs_args); LOCK(cs_args);
m_config_args.clear(); m_settings.ro_config.clear();
m_config_sections.clear(); m_config_sections.clear();
} }
@@ -899,30 +784,34 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
bool use_conf_file{true}; bool use_conf_file{true};
{ {
LOCK(cs_args); LOCK(cs_args);
auto it = m_override_args.find("-includeconf"); if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
if (it != m_override_args.end()) {
// ParseParameters() fails if a non-negated -includeconf is passed on the command-line // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
assert(it->second.empty()); assert(util::SettingsSpan(*includes).last_negated());
use_conf_file = false; use_conf_file = false;
} }
} }
if (use_conf_file) { if (use_conf_file) {
std::string chain_id = GetChainName(); std::string chain_id = GetChainName();
std::vector<std::string> conf_file_names(GetArgs("-includeconf")); std::vector<std::string> conf_file_names;
{
// We haven't set m_network yet (that happens in SelectParams()), so manually check
// for network.includeconf args.
std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf"));
conf_file_names.insert(conf_file_names.end(), includeconf_net.begin(), includeconf_net.end());
}
// Remove -includeconf from configuration, so we can warn about recursion auto add_includes = [&](const std::string& network, size_t skip = 0) {
// later size_t num_values = 0;
{
LOCK(cs_args); LOCK(cs_args);
m_config_args.erase("-includeconf"); if (auto* section = util::FindKey(m_settings.ro_config, network)) {
m_config_args.erase(std::string("-") + chain_id + ".includeconf"); if (auto* values = util::FindKey(*section, "includeconf")) {
} for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
conf_file_names.push_back((*values)[i].get_str());
}
num_values = values->size();
}
}
return num_values;
};
// We haven't set m_network yet (that happens in SelectParams()), so manually check
// for network.includeconf args.
const size_t chain_includes = add_includes(chain_id);
const size_t default_includes = add_includes({});
for (const std::string& conf_file_name : conf_file_names) { for (const std::string& conf_file_name : conf_file_names) {
fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name)); fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name));
@@ -938,14 +827,13 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
} }
// Warn about recursive -includeconf // Warn about recursive -includeconf
conf_file_names = GetArgs("-includeconf"); conf_file_names.clear();
std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf")); add_includes(chain_id, /* skip= */ chain_includes);
conf_file_names.insert(conf_file_names.end(), includeconf_net.begin(), includeconf_net.end()); add_includes({}, /* skip= */ default_includes);
std::string chain_id_final = GetChainName(); std::string chain_id_final = GetChainName();
if (chain_id_final != chain_id) { if (chain_id_final != chain_id) {
// Also warn about recursive includeconf for the chain that was specified in one of the includeconfs // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
includeconf_net = GetArgs(std::string("-") + chain_id_final + ".includeconf"); add_includes(chain_id_final);
conf_file_names.insert(conf_file_names.end(), includeconf_net.begin(), includeconf_net.end());
} }
for (const std::string& conf_file_name : conf_file_names) { for (const std::string& conf_file_name : conf_file_names) {
tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name); tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
@@ -964,9 +852,16 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
std::string ArgsManager::GetChainName() const std::string ArgsManager::GetChainName() const
{ {
LOCK(cs_args); auto get_net = [&](const std::string& arg) {
const bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); LOCK(cs_args);
const bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); util::SettingsValue value = GetSetting(m_settings, /* section= */ "", SettingName(arg),
/* ignore_default_section_config= */ false,
/* get_chain_name= */ true);
return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
};
const bool fRegTest = get_net("-regtest");
const bool fTestNet = get_net("-testnet");
const bool is_chain_arg_set = IsArgSet("-chain"); const bool is_chain_arg_set = IsArgSet("-chain");
if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) { if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
+2 -2
View File
@@ -22,6 +22,7 @@
#include <sync.h> #include <sync.h>
#include <tinyformat.h> #include <tinyformat.h>
#include <util/memory.h> #include <util/memory.h>
#include <util/settings.h>
#include <util/threadnames.h> #include <util/threadnames.h>
#include <util/time.h> #include <util/time.h>
@@ -157,8 +158,7 @@ protected:
}; };
mutable CCriticalSection cs_args; mutable CCriticalSection cs_args;
std::map<std::string, std::vector<std::string>> m_override_args GUARDED_BY(cs_args); util::Settings m_settings GUARDED_BY(cs_args);
std::map<std::string, std::vector<std::string>> m_config_args GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args); std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args); std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args); std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);