Merge #14708: Warn unrecognised sections in the config file

3fb09b9889 Warn unrecognized sections in the config file (Akio Nakamura)

Pull request description:

  This PR intends to resolve #14702.

  In the config file, sections are specified by square bracket pair "[]"$,
  or included in the option name itself which separated by a period"(.)".

  Typicaly, [testnet] is not a correct section name and specified options
  in that section are ignored but user cannot recognize what is happen.

  So, add some log-warning messages if unrecognized section names are
  present in the config file after checking section only args.

  note: Currentry, followings are out of scope of this PR.
  1) Empty section name or option name can describe.
  e.g. [] , .a=b, =c
  2) Multiple period characters can exist in the section name and option name.
  e.g. [c.d.e], [..], f.g.h.i=j, ..=k

Tree-SHA512: 2cea02a0525feb40320613989a75cd7b7b1bd12158d5e6f3174ca77e6a25bb84425dd8812f62483df9fc482045c7b5402d69bc714430518b1847d055a2dc304b
This commit is contained in:
Wladimir J. van der Laan 2018-11-21 16:43:46 +01:00
commit d7b0258ff0
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
4 changed files with 56 additions and 9 deletions

View file

@ -803,7 +803,15 @@ void InitParameterInteraction()
// Warn if network-specific options (-addnode, -connect, etc) are // Warn if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden // specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file. // on the command line or in this network's section of the config file.
gArgs.WarnForSectionOnlyArgs(); std::string network = gArgs.GetChainName();
for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
InitWarning(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
}
// Warn if unrecognized section name are present in the config file.
for (const auto& section : gArgs.GetUnrecognizedSections()) {
InitWarning(strprintf(_("Section [%s] is not recognized."), section));
}
} }
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)

View file

@ -371,15 +371,17 @@ ArgsManager::ArgsManager() :
// nothing to do // nothing to do
} }
void ArgsManager::WarnForSectionOnlyArgs() const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
{ {
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()) return; if (m_network.empty()) 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) return; 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; std::pair<bool, std::string> found_result;
@ -397,8 +399,28 @@ void ArgsManager::WarnForSectionOnlyArgs()
if (!found_result.first) continue; if (!found_result.first) continue;
// otherwise, issue a warning // otherwise, issue a warning
LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network); unsuitables.insert(arg);
} }
return unsuitables;
}
const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
{
// Section names to be recognized in the config file.
static const std::set<std::string> available_sections{
CBaseChainParams::REGTEST,
CBaseChainParams::TESTNET,
CBaseChainParams::MAIN
};
std::set<std::string> diff;
LOCK(cs_args);
std::set_difference(
m_config_sections.begin(), m_config_sections.end(),
available_sections.begin(), available_sections.end(),
std::inserter(diff, diff.end()));
return diff;
} }
void ArgsManager::SelectConfigNetwork(const std::string& network) void ArgsManager::SelectConfigNetwork(const std::string& network)
@ -819,7 +841,7 @@ static std::string TrimString(const std::string& str, const std::string& pattern
return str.substr(front, end - front + 1); return str.substr(front, end - front + 1);
} }
static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>> &options) static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::set<std::string>& sections)
{ {
std::string str, prefix; std::string str, prefix;
std::string::size_type pos; std::string::size_type pos;
@ -834,7 +856,9 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
str = TrimString(str, pattern); str = TrimString(str, pattern);
if (!str.empty()) { if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') { if (*str.begin() == '[' && *str.rbegin() == ']') {
prefix = str.substr(1, str.size() - 2) + '.'; const std::string section = str.substr(1, str.size() - 2);
sections.insert(section);
prefix = section + '.';
} else if (*str.begin() == '-') { } else if (*str.begin() == '-') {
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str); error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
return false; return false;
@ -846,6 +870,9 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return false; return false;
} }
options.emplace_back(name, value); options.emplace_back(name, value);
if ((pos = name.rfind('.')) != std::string::npos) {
sections.insert(name.substr(0, pos));
}
} else { } else {
error = strprintf("parse error on line %i: %s", linenr, str); error = strprintf("parse error on line %i: %s", linenr, str);
if (str.size() >= 2 && str.substr(0, 2) == "no") { if (str.size() >= 2 && str.substr(0, 2) == "no") {
@ -863,7 +890,8 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, boo
{ {
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, error, options)) { m_config_sections.clear();
if (!GetConfigOptions(stream, error, options, 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) {

View file

@ -149,6 +149,7 @@ protected:
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);
std::set<std::string> m_config_sections GUARDED_BY(cs_args);
bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false); bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
@ -169,7 +170,12 @@ public:
* on the command line or in a network-specific section in the * on the command line or in a network-specific section in the
* config file. * config file.
*/ */
void WarnForSectionOnlyArgs(); const std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
/**
* Log warnings for unrecognized section names in the config file.
*/
const std::set<std::string> GetUnrecognizedSections() const;
/** /**
* Return a vector of strings of the given argument * Return a vector of strings of the given argument

View file

@ -33,6 +33,11 @@ class ConfArgsTest(BitcoinTestFramework):
conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass') conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided') self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('testnot.datadir=1\n[testnet]\n')
self.restart_node(0)
self.nodes[0].stop_node(expected_stderr='Warning: Section [testnet] is not recognized.' + os.linesep + 'Warning: Section [testnot] is not recognized.')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear conf.write('') # clear