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