diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1400,7 +1400,8 @@ // Warn if unrecognized section name are present in the config file. for (const auto §ion : gArgs.GetUnrecognizedSections()) { - InitWarning(strprintf(_("Section [%s] is not recognized."), section)); + InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), + section.m_file, section.m_line, section.m_name)); } } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -180,9 +180,10 @@ { LOCK(cs_args); m_config_args.clear(); + m_config_sections.clear(); } std::string error; - ReadConfigStream(streamConfig, error); + BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error)); } void SetNetworkOnlyArg(const std::string arg) { LOCK(cs_args); diff --git a/src/util/system.h b/src/util/system.h --- a/src/util/system.h +++ b/src/util/system.h @@ -138,6 +138,12 @@ AVALANCHE, }; +struct SectionInfo { + std::string m_name; + std::string m_file; + int m_line; +}; + class ArgsManager { protected: friend class ArgsManagerHelper; @@ -162,10 +168,12 @@ std::set m_network_only_args GUARDED_BY(cs_args); std::map> m_available_args GUARDED_BY(cs_args); - std::set m_config_sections GUARDED_BY(cs_args); + std::list m_config_sections GUARDED_BY(cs_args); - bool ReadConfigStream(std::istream &stream, std::string &error, - bool ignore_invalid_keys = false); + NODISCARD bool ReadConfigStream(std::istream &stream, + const std::string &filepath, + std::string &error, + bool ignore_invalid_keys = false); public: ArgsManager(); @@ -189,7 +197,7 @@ /** * Log warnings for unrecognized section names in the config file. */ - const std::set GetUnrecognizedSections() const; + const std::list GetUnrecognizedSections() const; /** * Return a vector of strings of the given argument diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -391,18 +391,19 @@ return unsuitables; } -const std::set ArgsManager::GetUnrecognizedSections() const { +const std::list ArgsManager::GetUnrecognizedSections() const { // Section names to be recognized in the config file. static const std::set available_sections{ CBaseChainParams::REGTEST, CBaseChainParams::TESTNET, CBaseChainParams::MAIN}; - std::set 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; + std::list unrecognized = m_config_sections; + unrecognized.remove_if([](const SectionInfo &appeared) { + return available_sections.find(appeared.m_name) != + available_sections.end(); + }); + return unrecognized; } void ArgsManager::SelectConfigNetwork(const std::string &network) { @@ -890,9 +891,10 @@ } static bool -GetConfigOptions(std::istream &stream, std::string &error, +GetConfigOptions(std::istream &stream, const std::string &filepath, + std::string &error, std::vector> &options, - std::set §ions) { + std::list §ions) { std::string str, prefix; std::string::size_type pos; int linenr = 1; @@ -907,7 +909,7 @@ if (!str.empty()) { if (*str.begin() == '[' && *str.rbegin() == ']') { const std::string section = str.substr(1, str.size() - 2); - sections.insert(section); + sections.emplace_back(SectionInfo{section, filepath, linenr}); prefix = section + '.'; } else if (*str.begin() == '-') { error = strprintf( @@ -928,8 +930,10 @@ return false; } options.emplace_back(name, value); - if ((pos = name.rfind('.')) != std::string::npos) { - sections.insert(name.substr(0, pos)); + if ((pos = name.rfind('.')) != std::string::npos && + prefix.length() <= pos) { + sections.emplace_back( + SectionInfo{name.substr(0, pos), filepath, linenr}); } } else { error = strprintf("parse error on line %i: %s", linenr, str); @@ -946,12 +950,14 @@ return true; } -bool ArgsManager::ReadConfigStream(std::istream &stream, std::string &error, +bool ArgsManager::ReadConfigStream(std::istream &stream, + const std::string &filepath, + std::string &error, bool ignore_invalid_keys) { LOCK(cs_args); std::vector> options; - m_config_sections.clear(); - if (!GetConfigOptions(stream, error, options, m_config_sections)) { + if (!GetConfigOptions(stream, filepath, error, options, + m_config_sections)) { return false; } for (const std::pair &option : options) { @@ -984,6 +990,7 @@ { LOCK(cs_args); m_config_args.clear(); + m_config_sections.clear(); } const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); @@ -991,7 +998,7 @@ // ok to not have a config file if (stream.good()) { - if (!ReadConfigStream(stream, error, ignore_invalid_keys)) { + if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) { return false; } // if there is an -includeconf in the override args, but it is empty, @@ -1027,7 +1034,7 @@ for (const std::string &to_include : includeconf) { fsbridge::ifstream include_config(GetConfigFile(to_include)); if (include_config.good()) { - if (!ReadConfigStream(include_config, error, + if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) { return false; } diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -60,16 +60,29 @@ self.nodes[0].assert_start_raises_init_error( expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided') + inc_conf_file2_path = os.path.join( + self.nodes[0].datadir, 'include2.conf') + with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf: + conf.write('includeconf={}\n'.format(inc_conf_file2_path)) + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: - conf.write('testnot.datadir=1\n[testnet]\n') + conf.write('testnot.datadir=1\n') + with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf: + conf.write('[testnet]\n') self.restart_node(0) self.nodes[0].stop_node( - expected_stderr='Warning: Section [testnet] is not recognized.' + + expected_stderr='Warning: ' + + inc_conf_file_path + + ':1 Section [testnot] is not recognized.' + os.linesep + - 'Warning: Section [testnot] is not recognized.') + 'Warning: ' + + inc_conf_file2_path + + ':1 Section [testnet] is not recognized.') with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: conf.write('') # clear + with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf: + conf.write('') # clear def test_log_buffer(self): with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0']):