Skip to content

Commit a6aca8d

Browse files
committed
Merge #14105: util: Report parse errors in configuration file
ed2332a test: Add test for config file parsing errors (MarcoFalke) a66c0f7 util: Report parse errors in configuration file (Wladimir J. van der Laan) Pull request description: Report errors while parsing the configuration file, instead of silently ignoring them. $ src/bitcoind -regtest Error reading configuration file: parse error on line 22: nodebuglogfile, if you intended to specify a negated option, use nodebuglogfile=1 instead $ src/bitcoind -regtest Error reading configuration file: parse error on line 22: sdafsdfafs $ src/bitcoind -regtest Error reading configuration file: parse error on line 24: -nodebuglogfile=1, options in the configuration file must be specified without leading - (inspired by bitcoin/bitcoin#14100 (comment)) Tree-SHA512: d516342b65db2969edf200390994bbbda23654c648f85dcc99f9f2d217d3d59a72e0f58227be7b4746529dcfa54ba26d8188ba9f14a57c9ab00015d7283fade2
2 parents adf27b5 + ed2332a commit a6aca8d

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

src/util.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -818,11 +818,11 @@ static std::string TrimString(const std::string& str, const std::string& pattern
818818
return str.substr(front, end - front + 1);
819819
}
820820

821-
static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::istream& stream)
821+
static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>> &options)
822822
{
823-
std::vector<std::pair<std::string, std::string>> options;
824823
std::string str, prefix;
825824
std::string::size_type pos;
825+
int linenr = 1;
826826
while (std::getline(stream, str)) {
827827
if ((pos = str.find('#')) != std::string::npos) {
828828
str = str.substr(0, pos);
@@ -832,21 +832,34 @@ static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::is
832832
if (!str.empty()) {
833833
if (*str.begin() == '[' && *str.rbegin() == ']') {
834834
prefix = str.substr(1, str.size() - 2) + '.';
835+
} else if (*str.begin() == '-') {
836+
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
837+
return false;
835838
} else if ((pos = str.find('=')) != std::string::npos) {
836839
std::string name = prefix + TrimString(str.substr(0, pos), pattern);
837840
std::string value = TrimString(str.substr(pos + 1), pattern);
838841
options.emplace_back(name, value);
842+
} else {
843+
error = strprintf("parse error on line %i: %s", linenr, str);
844+
if (str.size() >= 2 && str.substr(0, 2) == "no") {
845+
error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
846+
}
847+
return false;
839848
}
840849
}
850+
++linenr;
841851
}
842-
return options;
852+
return true;
843853
}
844854

845855
bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys)
846856
{
847857
LOCK(cs_args);
848-
849-
for (const std::pair<std::string, std::string>& option : GetConfigOptions(stream)) {
858+
std::vector<std::pair<std::string, std::string>> options;
859+
if (!GetConfigOptions(stream, error, options)) {
860+
return false;
861+
}
862+
for (const std::pair<std::string, std::string>& option : options) {
850863
std::string strKey = std::string("-") + option.first;
851864
std::string strValue = option.second;
852865

test/functional/feature_config_args.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,29 @@ def set_test_params(self):
1414
self.setup_clean_chain = True
1515
self.num_nodes = 1
1616

17+
def test_config_file_parser(self):
18+
# Assume node is stopped
19+
20+
inc_conf_file_path = os.path.join(self.nodes[0].datadir, 'include.conf')
21+
with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf:
22+
conf.write('includeconf={}\n'.format(inc_conf_file_path))
23+
24+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
25+
conf.write('-dash=1\n')
26+
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -')
27+
28+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
29+
conf.write('nono\n')
30+
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
31+
32+
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
33+
conf.write('') # clear
34+
1735
def run_test(self):
1836
self.stop_node(0)
37+
38+
self.test_config_file_parser()
39+
1940
# Remove the -datadir argument so it doesn't override the config file
2041
self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
2142

0 commit comments

Comments
 (0)