Changeset View
Changeset View
Standalone View
Standalone View
src/util/system.cpp
Show All 11 Lines | |||||
#include <chainparamsbase.h> | #include <chainparamsbase.h> | ||||
#include <fs.h> | #include <fs.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <serialize.h> | #include <serialize.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/time.h> | #include <util/time.h> | ||||
#include <univalue.h> | |||||
#include <boost/thread.hpp> | #include <boost/thread.hpp> | ||||
#include <cstdarg> | #include <cstdarg> | ||||
#include <memory> | #include <memory> | ||||
#include <thread> | #include <thread> | ||||
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) | #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) | ||||
#include <pthread.h> | #include <pthread.h> | ||||
▲ Show 20 Lines • Show All 148 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static bool InterpretBool(const std::string &strValue) { | static bool InterpretBool(const std::string &strValue) { | ||||
if (strValue.empty()) { | if (strValue.empty()) { | ||||
return true; | return true; | ||||
} | } | ||||
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, | static inline bool UseDefaultSection(const ArgsManager &am, | ||||
const std::string &arg) | const std::string &arg) | ||||
EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) { | EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) { | ||||
return (am.m_network == CBaseChainParams::MAIN || | return (am.m_network == CBaseChainParams::MAIN || | ||||
am.m_network_only_args.count(arg) == 0); | am.m_network_only_args.count(arg) == 0); | ||||
} | } | ||||
/** Convert regular argument into the network-specific setting */ | static util::SettingsValue Get(const ArgsManager &am, | ||||
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) { | 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), | |||||
// We pass "true" to GetArgHelper in order to return the last | /* get_chain_name= */ false); | ||||
// 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) { | |||||
// not set | |||||
return false; | |||||
} | |||||
} | |||||
// is set, so evaluate | |||||
return InterpretBool(found_result.second); | |||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Interpret -nofoo as if the user supplied -foo=0. | * Interpret -nofoo as if the user supplied -foo=0. | ||||
* | * | ||||
* This method also tracks when the -no form was supplied, and if so, checks | * This method also tracks when the -no form was supplied, and if so, checks | ||||
* whether there was a double-negative (-nofoo=0 -> -foo=1). | * 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 IsArgNegated() | * 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 options that are | * method. One use case for this is to have a way to disable options that are | ||||
* not normally boolean (e.g. using -nodebuglogfile to request that debug log | * not normally boolean (e.g. using -nodebuglogfile to request that debug log | ||||
* output is not sent to any file at all). | * output is not sent to any file at all). | ||||
*/ | */ | ||||
NODISCARD static bool | static util::SettingsValue InterpretOption(std::string §ion, | ||||
InterpretOption(std::string key, std::string val, unsigned int flags, | std::string &key, | ||||
std::map<std::string, std::vector<std::string>> &args, | const std::string &value) { | ||||
std::string &error) { | // Split section name from key name for keys like "testnet.foo" or | ||||
assert(key[0] == '-'); | // "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.c_str()); | |||||
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; | ||||
} | } | ||||
ArgsManager::ArgsManager() { | ArgsManager::ArgsManager() { | ||||
// nothing to do | // nothing to do | ||||
} | } | ||||
const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const { | const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const { | ||||
std::set<std::string> unsuitables; | std::set<std::string> unsuitables; | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
// if there's no section selected, don't worry | // if there's no section selected, don't worry | ||||
if (m_network.empty()) { | if (m_network.empty()) { | ||||
return std::set<std::string>{}; | return std::set<std::string>{}; | ||||
} | } | ||||
// if it's okay to use the default section for this network, don't worry | // if it's okay to use the default section for this network, don't worry | ||||
if (m_network == CBaseChainParams::MAIN) { | if (m_network == CBaseChainParams::MAIN) { | ||||
return std::set<std::string>{}; | 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))) { | |||||
// 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); | unsuitables.insert(arg); | ||||
} | } | ||||
} | |||||
return unsuitables; | return unsuitables; | ||||
} | } | ||||
const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const { | const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const { | ||||
// Section names to be recognized in the config file. | // Section names to be recognized in the config file. | ||||
static const std::set<std::string> available_sections{ | static const std::set<std::string> available_sections{ | ||||
CBaseChainParams::REGTEST, CBaseChainParams::TESTNET, | CBaseChainParams::REGTEST, CBaseChainParams::TESTNET, | ||||
CBaseChainParams::MAIN}; | CBaseChainParams::MAIN}; | ||||
Show All 34 Lines | if (key.length() > 1 && key[1] == '-') { | ||||
key.erase(0, 1); | key.erase(0, 1); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ArgsManager::ParseParameters(int argc, const char *const argv[], | bool ArgsManager::ParseParameters(int argc, const char *const argv[], | ||||
std::string &error) { | 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]); | ||||
if (key == "-") { | if (key == "-") { | ||||
// bitcoin-tx using stdin | // bitcoin-tx using stdin | ||||
break; | break; | ||||
} | } | ||||
std::string val; | std::string val; | ||||
if (!ParseKeyValue(key, val)) { | if (!ParseKeyValue(key, val)) { | ||||
break; | break; | ||||
} | } | ||||
// 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.c_str()); | 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 = | ||||
if (it->second.size() > 0) { | util::FindKey(m_settings.command_line_options, "includeconf")) { | ||||
for (const auto &ic : it->second) { | for (const auto &include : util::SettingsSpan(*includes)) { | ||||
error += "-includeconf cannot be used from commandline; " | error += | ||||
"-includeconf=" + | "-includeconf cannot be used from commandline; -includeconf=" + | ||||
ic + "\n"; | include.get_str() + "\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; | ||||
} | } | ||||
} | } | ||||
return ArgsManager::NONE; | return ArgsManager::NONE; | ||||
} | } | ||||
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 = {}; | |||||
// special case | |||||
if (IsArgNegated(strArg)) { | |||||
return result; | |||||
} | |||||
LOCK(cs_args); | LOCK(cs_args); | ||||
bool ignore_default_section_config = | |||||
ArgsManagerHelper::AddArgs(result, m_override_args, strArg); | !ArgsManagerHelper::UseDefaultSection(*this, strArg); | ||||
if (!m_network.empty()) { | std::vector<std::string> result; | ||||
ArgsManagerHelper::AddArgs( | for (const util::SettingsValue &value : | ||||
result, m_config_args, | util::GetSettingsList(m_settings, m_network, SettingName(strArg), | ||||
ArgsManagerHelper::NetworkArg(*this, 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 { | ||||
// special case | return !ArgsManagerHelper::Get(*this, strArg).isNull(); | ||||
if (IsArgNegated(strArg)) { | |||||
return true; | |||||
} | |||||
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, | std::string ArgsManager::GetArg(const std::string &strArg, | ||||
const std::string &strDefault) const { | const std::string &strDefault) const { | ||||
if (IsArgNegated(strArg)) { | const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg); | ||||
return "0"; | return value.isNull() | ||||
} | ? strDefault | ||||
std::pair<bool, std::string> found_res = | : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str(); | ||||
ArgsManagerHelper::GetArg(*this, strArg); | |||||
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)) { | const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg); | ||||
return 0; | return value.isNull() | ||||
} | ? nDefault | ||||
std::pair<bool, std::string> found_res = | : value.isFalse() | ||||
ArgsManagerHelper::GetArg(*this, strArg); | ? 0 | ||||
if (found_res.first) { | : value.isTrue() ? 1 | ||||
return atoi64(found_res.second); | : value.isNum() ? value.get_int64() | ||||
} | : atoi64(value.get_str()); | ||||
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)) { | const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg); | ||||
return false; | return value.isNull() ? fDefault | ||||
} | : value.isBool() ? value.get_bool() | ||||
std::pair<bool, std::string> found_res = | : InterpretBool(value.get_str()); | ||||
ArgsManagerHelper::GetArg(*this, strArg); | |||||
if (found_res.first) { | |||||
return InterpretBool(found_res.second); | |||||
} | |||||
return fDefault; | |||||
} | } | ||||
bool ArgsManager::SoftSetArg(const std::string &strArg, | bool ArgsManager::SoftSetArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::string &strValue) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
if (IsArgSet(strArg)) { | if (IsArgSet(strArg)) { | ||||
return false; | return false; | ||||
} | } | ||||
ForceSetArg(strArg, strValue); | ForceSetArg(strArg, strValue); | ||||
return true; | return true; | ||||
} | } | ||||
bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) { | bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) { | ||||
if (fValue) { | if (fValue) { | ||||
return SoftSetArg(strArg, std::string("1")); | return SoftSetArg(strArg, std::string("1")); | ||||
} else { | } else { | ||||
return SoftSetArg(strArg, std::string("0")); | return SoftSetArg(strArg, std::string("0")); | ||||
} | } | ||||
} | } | ||||
void ArgsManager::ForceSetArg(const std::string &strArg, | void ArgsManager::ForceSetArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::string &strValue) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
m_override_args[strArg] = {strValue}; | m_settings.forced_settings[SettingName(strArg)] = strValue; | ||||
} | } | ||||
/** | /** | ||||
* This function is only used for testing purpose so | * This function is only used for testing purpose so | ||||
* so we should not worry about element uniqueness and | * so we should not worry about element uniqueness and | ||||
* integrity of mapMultiArgs data structure | * integrity of mapMultiArgs data structure | ||||
*/ | */ | ||||
void ArgsManager::ForceSetMultiArg(const std::string &strArg, | void ArgsManager::ForceSetMultiArg(const std::string &strArg, | ||||
const std::string &strValue) { | const std::vector<std::string> &values) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
m_override_args[strArg].push_back(strValue); | util::SettingsValue value; | ||||
value.setArray(); | |||||
for (const std::string &s : values) { | |||||
value.push_back(s); | |||||
} | |||||
m_settings.forced_settings[SettingName(strArg)] = value; | |||||
} | } | ||||
void ArgsManager::AddArg(const std::string &name, const std::string &help, | void ArgsManager::AddArg(const std::string &name, const std::string &help, | ||||
unsigned int flags, const OptionsCategory &cat) { | unsigned int flags, const OptionsCategory &cat) { | ||||
// Split arg name from its help param | // Split arg name from its help param | ||||
size_t eq_index = name.find('='); | size_t eq_index = name.find('='); | ||||
if (eq_index == std::string::npos) { | if (eq_index == std::string::npos) { | ||||
eq_index = name.size(); | eq_index = name.size(); | ||||
Show All 16 Lines | |||||
void ArgsManager::AddHiddenArgs(const std::vector<std::string> &names) { | void ArgsManager::AddHiddenArgs(const std::vector<std::string> &names) { | ||||
for (const std::string &name : names) { | for (const std::string &name : names) { | ||||
AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); | AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); | ||||
} | } | ||||
} | } | ||||
void ArgsManager::ClearForcedArg(const std::string &strArg) { | void ArgsManager::ClearForcedArg(const std::string &strArg) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
m_override_args.erase(strArg); | m_settings.forced_settings.erase(SettingName(strArg)); | ||||
} | } | ||||
std::string ArgsManager::GetHelpMessage() const { | std::string ArgsManager::GetHelpMessage() const { | ||||
const bool show_debug = gArgs.GetBoolArg("-help-debug", false); | const bool show_debug = gArgs.GetBoolArg("-help-debug", false); | ||||
std::string usage = ""; | std::string usage = ""; | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
for (const auto &arg_map : m_available_args) { | for (const auto &arg_map : m_available_args) { | ||||
▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | bool ArgsManager::ReadConfigStream(std::istream &stream, | ||||
bool ignore_invalid_keys) { | bool ignore_invalid_keys) { | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
std::vector<std::pair<std::string, std::string>> options; | std::vector<std::pair<std::string, std::string>> options; | ||||
if (!GetConfigOptions(stream, filepath, error, options, | if (!GetConfigOptions(stream, filepath, error, options, | ||||
m_config_sections)) { | m_config_sections)) { | ||||
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, | if (!CheckValid(key, value, flags, error)) { | ||||
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", | LogPrintf("Ignoring unknown configuration value %s\n", | ||||
option.first); | option.first); | ||||
} else { | } else { | ||||
error = strprintf("Invalid configuration value %s", | error = strprintf("Invalid configuration value %s", | ||||
option.first.c_str()); | option.first.c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ArgsManager::ReadConfigFiles(std::string &error, | bool ArgsManager::ReadConfigFiles(std::string &error, | ||||
bool ignore_invalid_keys) { | 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(); | ||||
} | } | ||||
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); | const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); | ||||
fsbridge::ifstream stream(GetConfigFile(confPath)); | fsbridge::ifstream stream(GetConfigFile(confPath)); | ||||
// ok to not have a config file | // ok to not have a config file | ||||
if (stream.good()) { | if (stream.good()) { | ||||
if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) { | if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) { | ||||
return false; | return false; | ||||
} | } | ||||
// `-includeconf` cannot be included in the command line arguments | // `-includeconf` cannot be included in the command line arguments | ||||
// except as `-noincludeconf` (which indicates that no conf file should | // except as `-noincludeconf` (which indicates that no conf file should | ||||
// be used). | // be used). | ||||
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, | ||||
if (it != m_override_args.end()) { | "includeconf")) { | ||||
// ParseParameters() fails if a non-negated -includeconf is | // ParseParameters() fails if a non-negated -includeconf is | ||||
// passed on the command-line | // 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 | auto add_includes = [&](const std::string &network, | ||||
// recursion later | size_t skip = 0) { | ||||
{ | size_t num_values = 0; | ||||
LOCK(cs_args); | LOCK(cs_args); | ||||
m_config_args.erase("-includeconf"); | if (auto *section = | ||||
m_config_args.erase(std::string("-") + chain_id + | util::FindKey(m_settings.ro_config, network)) { | ||||
".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( | fsbridge::ifstream conf_file_stream( | ||||
GetConfigFile(conf_file_name)); | GetConfigFile(conf_file_name)); | ||||
if (conf_file_stream.good()) { | if (conf_file_stream.good()) { | ||||
if (!ReadConfigStream(conf_file_stream, conf_file_name, | if (!ReadConfigStream(conf_file_stream, conf_file_name, | ||||
error, ignore_invalid_keys)) { | error, ignore_invalid_keys)) { | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("Included configuration file %s\n", | LogPrintf("Included configuration file %s\n", | ||||
conf_file_name); | conf_file_name); | ||||
} else { | } else { | ||||
error = "Failed to include configuration file " + | error = "Failed to include configuration file " + | ||||
conf_file_name; | conf_file_name; | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// Warn about recursive -includeconf | // Warn about recursive -includeconf | ||||
conf_file_names = GetArgs("-includeconf"); | conf_file_names.clear(); | ||||
std::vector<std::string> includeconf_net( | add_includes(chain_id, /* skip= */ chain_includes); | ||||
GetArgs(std::string("-") + chain_id + ".includeconf")); | add_includes({}, /* skip= */ default_includes); | ||||
conf_file_names.insert(conf_file_names.end(), | |||||
includeconf_net.begin(), | |||||
includeconf_net.end()); | |||||
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 | // Also warn about recursive includeconf for the chain that was | ||||
// specified in one of the includeconfs | // specified in one of the includeconfs | ||||
includeconf_net = | add_includes(chain_id_final); | ||||
GetArgs(std::string("-") + chain_id_final + ".includeconf"); | |||||
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, | tfm::format(std::cerr, | ||||
"warning: -includeconf cannot be used from " | "warning: -includeconf cannot be used from " | ||||
"included files; ignoring -includeconf=%s\n", | "included files; ignoring -includeconf=%s\n", | ||||
conf_file_name); | conf_file_name); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// If datadir is changed in .conf file: | // If datadir is changed in .conf file: | ||||
ClearDatadirCache(); | ClearDatadirCache(); | ||||
if (!CheckDataDirOption()) { | if (!CheckDataDirOption()) { | ||||
error = strprintf("specified data directory \"%s\" does not exist.", | error = strprintf("specified data directory \"%s\" does not exist.", | ||||
gArgs.GetArg("-datadir", "").c_str()); | gArgs.GetArg("-datadir", "").c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
std::string ArgsManager::GetChainName() const { | std::string ArgsManager::GetChainName() const { | ||||
auto get_net = [&](const std::string &arg) { | |||||
LOCK(cs_args); | LOCK(cs_args); | ||||
const bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); | util::SettingsValue value = | ||||
const bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); | 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) { | ||||
throw std::runtime_error("Invalid combination of -regtest, -testnet " | throw std::runtime_error("Invalid combination of -regtest, -testnet " | ||||
"and -chain. Can use at most one."); | "and -chain. Can use at most one."); | ||||
} | } | ||||
if (fRegTest) { | if (fRegTest) { | ||||
return CBaseChainParams::REGTEST; | return CBaseChainParams::REGTEST; | ||||
▲ Show 20 Lines • Show All 293 Lines • Show Last 20 Lines |