Line data Source code
1 : // Copyright (c) 2019 The Bitcoin Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <util/settings.h>
6 :
7 : #include <tinyformat.h>
8 : #include <univalue.h>
9 :
10 : namespace util {
11 : namespace {
12 :
13 : enum class Source {
14 : FORCED,
15 : COMMAND_LINE,
16 : RW_SETTINGS,
17 : CONFIG_FILE_NETWORK_SECTION,
18 : CONFIG_FILE_DEFAULT_SECTION
19 : };
20 :
21 : //! Merge settings from multiple sources in precedence order:
22 : //! Forced config > command line > read-write settings file > config file network-specific section > config file default section
23 : //!
24 : //! This function is provided with a callback function fn that contains
25 : //! specific logic for how to merge the sources.
26 : template <typename Fn>
27 1666355 : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
28 : {
29 : // Merge in the forced settings
30 1666355 : if (auto* value = FindKey(settings.forced_settings, name)) {
31 56234 : fn(SettingsSpan(*value), Source::FORCED);
32 56234 : }
33 : // Merge in the command-line options
34 1666355 : if (auto* values = FindKey(settings.command_line_options, name)) {
35 137246 : fn(SettingsSpan(*values), Source::COMMAND_LINE);
36 137246 : }
37 : // Merge in the read-write settings
38 1666355 : if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
39 1090 : fn(SettingsSpan(*value), Source::RW_SETTINGS);
40 1090 : }
41 : // Merge in the network-specific section of the config file
42 1666355 : if (!section.empty()) {
43 1635266 : if (auto* map = FindKey(settings.ro_config, section)) {
44 1363041 : if (auto* values = FindKey(*map, name)) {
45 86039 : fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
46 86039 : }
47 1363041 : }
48 1635266 : }
49 : // Merge in the default section of the config file
50 1666355 : if (auto* map = FindKey(settings.ro_config, "")) {
51 1437514 : if (auto* values = FindKey(*map, name)) {
52 120120 : fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
53 120120 : }
54 1437514 : }
55 1666355 : }
56 : } // namespace
57 :
58 541 : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
59 : {
60 541 : values.clear();
61 541 : errors.clear();
62 :
63 541 : fsbridge::ifstream file;
64 541 : file.open(path);
65 541 : if (!file.is_open()) return true; // Ok for file not to exist.
66 :
67 234 : SettingsValue in;
68 234 : if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
69 2 : errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
70 2 : return false;
71 : }
72 :
73 232 : if (file.fail()) {
74 0 : errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
75 0 : return false;
76 : }
77 232 : file.close(); // Done with file descriptor. Release while copying data.
78 :
79 232 : if (!in.isObject()) {
80 2 : errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
81 2 : return false;
82 : }
83 :
84 230 : const std::vector<std::string>& in_keys = in.getKeys();
85 230 : const std::vector<SettingsValue>& in_values = in.getValues();
86 248 : for (size_t i = 0; i < in_keys.size(); ++i) {
87 18 : auto inserted = values.emplace(in_keys[i], in_values[i]);
88 18 : if (!inserted.second) {
89 2 : errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
90 2 : }
91 : }
92 230 : return errors.empty();
93 541 : }
94 :
95 542 : bool WriteSettings(const fs::path& path,
96 : const std::map<std::string, SettingsValue>& values,
97 : std::vector<std::string>& errors)
98 : {
99 542 : SettingsValue out(SettingsValue::VOBJ);
100 561 : for (const auto& value : values) {
101 19 : out.__pushKV(value.first, value.second);
102 0 : }
103 542 : fsbridge::ofstream file;
104 542 : file.open(path);
105 542 : if (file.fail()) {
106 0 : errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string()));
107 0 : return false;
108 : }
109 542 : file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
110 542 : file.close();
111 542 : return true;
112 542 : }
113 :
114 1610361 : SettingsValue GetSetting(const Settings& settings,
115 : const std::string& section,
116 : const std::string& name,
117 : bool ignore_default_section_config,
118 : bool get_chain_name)
119 : {
120 1610361 : SettingsValue result;
121 1610361 : bool done = false; // Done merging any more settings sources.
122 1915426 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
123 : // Weird behavior preserved for backwards compatibility: Apply negated
124 : // setting even if non-negated setting would be ignored. A negated
125 : // value in the default section is applied to network specific options,
126 : // even though normal non-negated values there would be ignored.
127 305065 : const bool never_ignore_negated_setting = span.last_negated();
128 :
129 : // Weird behavior preserved for backwards compatibility: Take first
130 : // assigned value instead of last. In general, later settings take
131 : // precedence over early settings, but for backwards compatibility in
132 : // the config file the precedence is reversed for all settings except
133 : // chain name settings.
134 305065 : const bool reverse_precedence =
135 305065 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
136 159380 : !get_chain_name;
137 :
138 : // Weird behavior preserved for backwards compatibility: Negated
139 : // -regtest and -testnet arguments which you would expect to override
140 : // values set in the configuration file are currently accepted but
141 : // silently ignored. It would be better to apply these just like other
142 : // negated values, or at least warn they are ignored.
143 305065 : const bool skip_negated_command_line = get_chain_name;
144 :
145 305065 : if (done) return;
146 :
147 : // Ignore settings in default config section if requested.
148 150008 : if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
149 : !never_ignore_negated_setting) {
150 266 : return;
151 : }
152 :
153 : // Skip negated command line settings.
154 149742 : if (skip_negated_command_line && span.last_negated()) return;
155 :
156 147722 : if (!span.empty()) {
157 117477 : result = reverse_precedence ? span.begin()[0] : span.end()[-1];
158 117477 : done = true;
159 147722 : } else if (span.last_negated()) {
160 30138 : result = false;
161 30138 : done = true;
162 30138 : }
163 305065 : });
164 : return result;
165 1610361 : }
166 :
167 43990 : std::vector<SettingsValue> GetSettingsList(const Settings& settings,
168 : const std::string& section,
169 : const std::string& name,
170 : bool ignore_default_section_config)
171 : {
172 43990 : std::vector<SettingsValue> result;
173 43990 : bool done = false; // Done merging any more settings sources.
174 43990 : bool prev_negated_empty = false;
175 116907 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
176 : // Weird behavior preserved for backwards compatibility: Apply config
177 : // file settings even if negated on command line. Negating a setting on
178 : // command line will ignore earlier settings on the command line and
179 : // ignore settings in the config file, unless the negated command line
180 : // value is followed by non-negated value, in which case config file
181 : // settings will be brought back from the dead (but earlier command
182 : // line settings will still be ignored).
183 72917 : const bool add_zombie_config_values =
184 72917 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
185 34741 : !prev_negated_empty;
186 :
187 : // Ignore settings in default config section if requested.
188 72917 : if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
189 :
190 : // Add new settings to the result if isn't already complete, or if the
191 : // values are zombies.
192 67043 : if (!done || add_zombie_config_values) {
193 102383 : for (const auto& value : span) {
194 52471 : if (value.isArray()) {
195 681 : result.insert(result.end(), value.getValues().begin(), value.getValues().end());
196 681 : } else {
197 51790 : result.push_back(value);
198 : }
199 : }
200 49912 : }
201 :
202 : // If a setting was negated, or if a setting was forced, set
203 : // done to true to ignore any later lower priority settings.
204 67043 : done |= span.negated() > 0 || source == Source::FORCED;
205 :
206 : // Update the negated and empty state used for the zombie values check.
207 67043 : prev_negated_empty |= span.last_negated() && result.empty();
208 72917 : });
209 : return result;
210 43990 : }
211 :
212 12004 : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
213 : {
214 12004 : bool has_default_section_setting = false;
215 12004 : bool has_other_setting = false;
216 34751 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
217 22747 : if (span.empty()) return;
218 13780 : else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
219 10559 : else has_other_setting = true;
220 22747 : });
221 : // If a value is set in the default section and not explicitly overwritten by the
222 : // user on the command line or in a different section, then we want to enable
223 : // warnings about the value being ignored.
224 24008 : return has_default_section_setting && !has_other_setting;
225 12004 : }
226 :
227 343433 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
228 75824 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
229 141479 : const SettingsValue* SettingsSpan::end() const { return data + size; }
230 170469 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
231 581868 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
232 142894 : size_t SettingsSpan::negated() const
233 : {
234 295383 : for (size_t i = size; i > 0; --i) {
235 214587 : if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
236 : }
237 80796 : return 0;
238 142894 : }
239 :
240 : } // namespace util
|