/Users/eugenesiegel/btc/bitcoin/src/common/settings.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2019-2022 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 <common/settings.h> |
6 | | |
7 | | #include <bitcoin-build-config.h> // IWYU pragma: keep |
8 | | |
9 | | #include <tinyformat.h> |
10 | | #include <univalue.h> |
11 | | #include <util/fs.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <fstream> |
15 | | #include <iterator> |
16 | | #include <map> |
17 | | #include <string> |
18 | | #include <utility> |
19 | | #include <vector> |
20 | | |
21 | | namespace common { |
22 | | namespace { |
23 | | |
24 | | enum class Source { |
25 | | FORCED, |
26 | | COMMAND_LINE, |
27 | | RW_SETTINGS, |
28 | | CONFIG_FILE_NETWORK_SECTION, |
29 | | CONFIG_FILE_DEFAULT_SECTION |
30 | | }; |
31 | | |
32 | | // Json object key for the auto-generated warning comment |
33 | | const std::string SETTINGS_WARN_MSG_KEY{"_warning_"}; |
34 | | |
35 | | //! Merge settings from multiple sources in precedence order: |
36 | | //! Forced config > command line > read-write settings file > config file network-specific section > config file default section |
37 | | //! |
38 | | //! This function is provided with a callback function fn that contains |
39 | | //! specific logic for how to merge the sources. |
40 | | template <typename Fn> |
41 | | static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn) |
42 | 16.9M | { |
43 | | // Merge in the forced settings |
44 | 16.9M | if (auto* value = FindKey(settings.forced_settings, name)) { |
45 | 399k | fn(SettingsSpan(*value), Source::FORCED); |
46 | 399k | } |
47 | | // Merge in the command-line options |
48 | 16.9M | if (auto* values = FindKey(settings.command_line_options, name)) { |
49 | 499k | fn(SettingsSpan(*values), Source::COMMAND_LINE); |
50 | 499k | } |
51 | | // Merge in the read-write settings |
52 | 16.9M | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { |
53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); |
54 | 0 | } |
55 | | // Merge in the network-specific section of the config file |
56 | 16.9M | if (!section.empty()) { |
57 | 16.0M | if (auto* map = FindKey(settings.ro_config, section)) { |
58 | 0 | if (auto* values = FindKey(*map, name)) { |
59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); |
60 | 0 | } |
61 | 0 | } |
62 | 16.0M | } |
63 | | // Merge in the default section of the config file |
64 | 16.9M | if (auto* map = FindKey(settings.ro_config, "")) { |
65 | 0 | if (auto* values = FindKey(*map, name)) { |
66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); |
67 | 0 | } |
68 | 0 | } |
69 | 16.9M | } settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_10GetSettingERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_bbbE3$_0EEvS4_SD_SD_OT_ Line | Count | Source | 42 | 15.8M | { | 43 | | // Merge in the forced settings | 44 | 15.8M | if (auto* value = FindKey(settings.forced_settings, name)) { | 45 | 399k | fn(SettingsSpan(*value), Source::FORCED); | 46 | 399k | } | 47 | | // Merge in the command-line options | 48 | 15.8M | if (auto* values = FindKey(settings.command_line_options, name)) { | 49 | 349k | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 349k | } | 51 | | // Merge in the read-write settings | 52 | 15.8M | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { | 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 15.8M | if (!section.empty()) { | 57 | 14.9M | if (auto* map = FindKey(settings.ro_config, section)) { | 58 | 0 | if (auto* values = FindKey(*map, name)) { | 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 14.9M | } | 63 | | // Merge in the default section of the config file | 64 | 15.8M | if (auto* map = FindKey(settings.ro_config, "")) { | 65 | 0 | if (auto* values = FindKey(*map, name)) { | 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 15.8M | } |
settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_15GetSettingsListERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_bE3$_0EEvS4_SD_SD_OT_ Line | Count | Source | 42 | 706k | { | 43 | | // Merge in the forced settings | 44 | 706k | if (auto* value = FindKey(settings.forced_settings, name)) { | 45 | 0 | fn(SettingsSpan(*value), Source::FORCED); | 46 | 0 | } | 47 | | // Merge in the command-line options | 48 | 706k | if (auto* values = FindKey(settings.command_line_options, name)) { | 49 | 149k | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 149k | } | 51 | | // Merge in the read-write settings | 52 | 706k | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { | 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 706k | if (!section.empty()) { | 57 | 706k | if (auto* map = FindKey(settings.ro_config, section)) { | 58 | 0 | if (auto* values = FindKey(*map, name)) { | 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 706k | } | 63 | | // Merge in the default section of the config file | 64 | 706k | if (auto* map = FindKey(settings.ro_config, "")) { | 65 | 0 | if (auto* values = FindKey(*map, name)) { | 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 706k | } |
settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_28OnlyHasDefaultSectionSettingERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_E3$_0EEvS4_SD_SD_OT_ Line | Count | Source | 42 | 399k | { | 43 | | // Merge in the forced settings | 44 | 399k | if (auto* value = FindKey(settings.forced_settings, name)) { | 45 | 0 | fn(SettingsSpan(*value), Source::FORCED); | 46 | 0 | } | 47 | | // Merge in the command-line options | 48 | 399k | if (auto* values = FindKey(settings.command_line_options, name)) { | 49 | 0 | fn(SettingsSpan(*values), Source::COMMAND_LINE); | 50 | 0 | } | 51 | | // Merge in the read-write settings | 52 | 399k | if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { | 53 | 0 | fn(SettingsSpan(*value), Source::RW_SETTINGS); | 54 | 0 | } | 55 | | // Merge in the network-specific section of the config file | 56 | 399k | if (!section.empty()) { | 57 | 399k | if (auto* map = FindKey(settings.ro_config, section)) { | 58 | 0 | if (auto* values = FindKey(*map, name)) { | 59 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 60 | 0 | } | 61 | 0 | } | 62 | 399k | } | 63 | | // Merge in the default section of the config file | 64 | 399k | if (auto* map = FindKey(settings.ro_config, "")) { | 65 | 0 | if (auto* values = FindKey(*map, name)) { | 66 | 0 | fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 67 | 0 | } | 68 | 0 | } | 69 | 399k | } |
|
70 | | } // namespace |
71 | | |
72 | | bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors) |
73 | 0 | { |
74 | 0 | values.clear(); |
75 | 0 | errors.clear(); |
76 | | |
77 | | // Ok for file to not exist |
78 | 0 | if (!fs::exists(path)) return true; |
79 | | |
80 | 0 | std::ifstream file; |
81 | 0 | file.open(path); |
82 | 0 | if (!file.is_open()) { |
83 | 0 | errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path))); Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
84 | 0 | return false; |
85 | 0 | } |
86 | | |
87 | 0 | SettingsValue in; |
88 | 0 | if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) { |
89 | 0 | errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, " Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
90 | 0 | "and can be fixed by removing the file, which will reset settings to default values.", |
91 | 0 | fs::PathToString(path))); |
92 | 0 | return false; |
93 | 0 | } |
94 | | |
95 | 0 | if (file.fail()) { |
96 | 0 | errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path))); Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
97 | 0 | return false; |
98 | 0 | } |
99 | 0 | file.close(); // Done with file descriptor. Release while copying data. |
100 | |
|
101 | 0 | if (!in.isObject()) { |
102 | 0 | errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path))); Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
103 | 0 | return false; |
104 | 0 | } |
105 | | |
106 | 0 | const std::vector<std::string>& in_keys = in.getKeys(); |
107 | 0 | const std::vector<SettingsValue>& in_values = in.getValues(); |
108 | 0 | for (size_t i = 0; i < in_keys.size(); ++i) { |
109 | 0 | auto inserted = values.emplace(in_keys[i], in_values[i]); |
110 | 0 | if (!inserted.second) { |
111 | 0 | errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path))); Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
112 | 0 | values.clear(); |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | // Remove auto-generated warning comment from the accessible settings. |
118 | 0 | values.erase(SETTINGS_WARN_MSG_KEY); |
119 | |
|
120 | 0 | return errors.empty(); |
121 | 0 | } |
122 | | |
123 | | bool WriteSettings(const fs::path& path, |
124 | | const std::map<std::string, SettingsValue>& values, |
125 | | std::vector<std::string>& errors) |
126 | 49.9k | { |
127 | 49.9k | SettingsValue out(SettingsValue::VOBJ); |
128 | | // Add auto-generated warning comment |
129 | 49.9k | out.pushKV(SETTINGS_WARN_MSG_KEY, strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node " Line | Count | Source | 1172 | 49.9k | #define strprintf tfm::format |
|
130 | 49.9k | "is running, as any changes might be ignored or overwritten.", CLIENT_NAME)); Line | Count | Source | 115 | 49.9k | #define CLIENT_NAME "Bitcoin Core" |
|
131 | | // Push settings values |
132 | 49.9k | for (const auto& value : values) { |
133 | 49.9k | out.pushKVEnd(value.first, value.second); |
134 | 49.9k | } |
135 | 49.9k | std::ofstream file; |
136 | 49.9k | file.open(path); |
137 | 49.9k | if (file.fail()) { |
138 | 0 | errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path))); Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
139 | 0 | return false; |
140 | 0 | } |
141 | 49.9k | file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl; |
142 | 49.9k | file.close(); |
143 | 49.9k | return true; |
144 | 49.9k | } |
145 | | |
146 | | SettingsValue GetSetting(const Settings& settings, |
147 | | const std::string& section, |
148 | | const std::string& name, |
149 | | bool ignore_default_section_config, |
150 | | bool ignore_nonpersistent, |
151 | | bool get_chain_type) |
152 | 15.8M | { |
153 | 15.8M | SettingsValue result; |
154 | 15.8M | bool done = false; // Done merging any more settings sources. |
155 | 15.8M | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
156 | | // Weird behavior preserved for backwards compatibility: Apply negated |
157 | | // setting even if non-negated setting would be ignored. A negated |
158 | | // value in the default section is applied to network specific options, |
159 | | // even though normal non-negated values there would be ignored. |
160 | 749k | const bool never_ignore_negated_setting = span.last_negated(); |
161 | | |
162 | | // Weird behavior preserved for backwards compatibility: Take first |
163 | | // assigned value instead of last. In general, later settings take |
164 | | // precedence over early settings, but for backwards compatibility in |
165 | | // the config file the precedence is reversed for all settings except |
166 | | // chain type settings. |
167 | 749k | const bool reverse_precedence = |
168 | 749k | (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && |
169 | 749k | !get_chain_type0 ; |
170 | | |
171 | | // Weird behavior preserved for backwards compatibility: Negated |
172 | | // -regtest and -testnet arguments which you would expect to override |
173 | | // values set in the configuration file are currently accepted but |
174 | | // silently ignored. It would be better to apply these just like other |
175 | | // negated values, or at least warn they are ignored. |
176 | 749k | const bool skip_negated_command_line = get_chain_type; |
177 | | |
178 | 749k | if (done) return0 ; |
179 | | |
180 | | // Ignore settings in default config section if requested. |
181 | 749k | if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION0 && |
182 | 749k | !never_ignore_negated_setting0 ) { |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | | // Ignore nonpersistent settings if requested. |
187 | 749k | if (ignore_nonpersistent && (0 source == Source::COMMAND_LINE0 || source == Source::FORCED0 )) return0 ; |
188 | | |
189 | | // Skip negated command line settings. |
190 | 749k | if (skip_negated_command_line && span.last_negated()0 ) return0 ; |
191 | | |
192 | 749k | if (!span.empty()) { |
193 | 649k | result = reverse_precedence ? span.begin()[0]0 : span.end()[-1]; |
194 | 649k | done = true; |
195 | 649k | } else if (99.9k span.last_negated()99.9k ) { |
196 | 99.9k | result = false; |
197 | 99.9k | done = true; |
198 | 99.9k | } |
199 | 749k | }); |
200 | 15.8M | return result; |
201 | 15.8M | } |
202 | | |
203 | | std::vector<SettingsValue> GetSettingsList(const Settings& settings, |
204 | | const std::string& section, |
205 | | const std::string& name, |
206 | | bool ignore_default_section_config) |
207 | 706k | { |
208 | 706k | std::vector<SettingsValue> result; |
209 | 706k | bool done = false; // Done merging any more settings sources. |
210 | 706k | bool prev_negated_empty = false; |
211 | 706k | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
212 | | // Weird behavior preserved for backwards compatibility: Apply config |
213 | | // file settings even if negated on command line. Negating a setting on |
214 | | // command line will ignore earlier settings on the command line and |
215 | | // ignore settings in the config file, unless the negated command line |
216 | | // value is followed by non-negated value, in which case config file |
217 | | // settings will be brought back from the dead (but earlier command |
218 | | // line settings will still be ignored). |
219 | 149k | const bool add_zombie_config_values = |
220 | 149k | (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && |
221 | 149k | !prev_negated_empty0 ; |
222 | | |
223 | | // Ignore settings in default config section if requested. |
224 | 149k | if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION0 ) return0 ; |
225 | | |
226 | | // Add new settings to the result if isn't already complete, or if the |
227 | | // values are zombies. |
228 | 149k | if (!done || add_zombie_config_values0 ) { |
229 | 149k | for (const auto& value : span) { |
230 | 149k | if (value.isArray()) { |
231 | 0 | result.insert(result.end(), value.getValues().begin(), value.getValues().end()); |
232 | 149k | } else { |
233 | 149k | result.push_back(value); |
234 | 149k | } |
235 | 149k | } |
236 | 149k | } |
237 | | |
238 | | // If a setting was negated, or if a setting was forced, set |
239 | | // done to true to ignore any later lower priority settings. |
240 | 149k | done |= span.negated() > 0 || source == Source::FORCED99.9k ; |
241 | | |
242 | | // Update the negated and empty state used for the zombie values check. |
243 | 149k | prev_negated_empty |= span.last_negated() && result.empty()49.9k ; |
244 | 149k | }); |
245 | 706k | return result; |
246 | 706k | } |
247 | | |
248 | | bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name) |
249 | 399k | { |
250 | 399k | bool has_default_section_setting = false; |
251 | 399k | bool has_other_setting = false; |
252 | 399k | MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { |
253 | 0 | if (span.empty()) return; |
254 | 0 | else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true; |
255 | 0 | else has_other_setting = true; |
256 | 0 | }); |
257 | | // If a value is set in the default section and not explicitly overwritten by the |
258 | | // user on the command line or in a different section, then we want to enable |
259 | | // warnings about the value being ignored. |
260 | 399k | return has_default_section_setting && !has_other_setting0 ; |
261 | 399k | } |
262 | | |
263 | 499k | SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {} Unexecuted instantiation: _ZN6common12SettingsSpanC2ERKNSt3__16vectorI8UniValueNS1_9allocatorIS3_EEEE _ZN6common12SettingsSpanC1ERKNSt3__16vectorI8UniValueNS1_9allocatorIS3_EEEE Line | Count | Source | 263 | 499k | SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {} |
|
264 | 149k | const SettingsValue* SettingsSpan::begin() const { return data + negated(); } |
265 | 799k | const SettingsValue* SettingsSpan::end() const { return data + size; } |
266 | 749k | bool SettingsSpan::empty() const { return size == 0 || last_negated(); } |
267 | 1.74M | bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); } |
268 | | size_t SettingsSpan::negated() const |
269 | 299k | { |
270 | 599k | for (size_t i = size; i > 0; --i299k ) { |
271 | 399k | if (data[i - 1].isFalse()) return i99.9k ; // Return number of negated values (position of last false value) |
272 | 399k | } |
273 | 199k | return 0; |
274 | 299k | } |
275 | | |
276 | | } // namespace common |