Line data Source code
1 : // Copyright (c) 2019-2021 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 <fs.h>
6 : #include <util/settings.h>
7 :
8 : #include <tinyformat.h>
9 : #include <univalue.h>
10 :
11 : #include <fstream>
12 : #include <map>
13 : #include <string>
14 : #include <vector>
15 :
16 : namespace util {
17 : namespace {
18 :
19 : enum class Source {
20 : FORCED,
21 : COMMAND_LINE,
22 : RW_SETTINGS,
23 : CONFIG_FILE_NETWORK_SECTION,
24 : CONFIG_FILE_DEFAULT_SECTION
25 : };
26 :
27 : //! Merge settings from multiple sources in precedence order:
28 : //! Forced config > command line > read-write settings file > config file network-specific section > config file default section
29 : //!
30 : //! This function is provided with a callback function fn that contains
31 : //! specific logic for how to merge the sources.
32 : template <typename Fn>
33 11481530 : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
34 : {
35 : // Merge in the forced settings
36 11481530 : if (auto* value = FindKey(settings.forced_settings, name)) {
37 88690 : fn(SettingsSpan(*value), Source::FORCED);
38 88690 : }
39 : // Merge in the command-line options
40 11481530 : if (auto* values = FindKey(settings.command_line_options, name)) {
41 268564 : fn(SettingsSpan(*values), Source::COMMAND_LINE);
42 268564 : }
43 : // Merge in the read-write settings
44 11481530 : if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
45 2633 : fn(SettingsSpan(*value), Source::RW_SETTINGS);
46 2633 : }
47 : // Merge in the network-specific section of the config file
48 11481530 : if (!section.empty()) {
49 11274228 : if (auto* map = FindKey(settings.ro_config, section)) {
50 10637301 : if (auto* values = FindKey(*map, name)) {
51 2545188 : fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
52 2545188 : }
53 10637301 : }
54 11274228 : }
55 : // Merge in the default section of the config file
56 11481530 : if (auto* map = FindKey(settings.ro_config, "")) {
57 10796126 : if (auto* values = FindKey(*map, name)) {
58 146227 : fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
59 146227 : }
60 10796126 : }
61 11481530 : }
62 : } // namespace
63 :
64 4517 : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
65 : {
66 4517 : values.clear();
67 4517 : errors.clear();
68 :
69 : // Ok for file to not exist
70 4517 : if (!fs::exists(path)) return true;
71 :
72 2979 : std::ifstream file;
73 2979 : file.open(path);
74 2979 : if (!file.is_open()) {
75 0 : errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
76 0 : return false;
77 : }
78 :
79 : // Check if settings file is empty
80 2979 : if (file.peek() == std::ifstream::traits_type::eof()) {
81 : // In that case delete it and return true: it will be created with default value later
82 0 : file.close();
83 0 : if (!fs::remove(path)) {
84 : // Return false only if it failed to delete the empty settings file
85 0 : errors.emplace_back(strprintf("Unable to delete empty settings file %s", fs::PathToString(path)));
86 0 : return false;
87 : }
88 0 : return true;
89 : }
90 :
91 2979 : SettingsValue in;
92 2979 : if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
93 3 : errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
94 3 : return false;
95 : }
96 :
97 2976 : if (file.fail()) {
98 0 : errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
99 0 : return false;
100 : }
101 2976 : file.close(); // Done with file descriptor. Release while copying data.
102 :
103 2976 : if (!in.isObject()) {
104 3 : errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
105 3 : return false;
106 : }
107 :
108 2973 : const std::vector<std::string>& in_keys = in.getKeys();
109 2973 : const std::vector<SettingsValue>& in_values = in.getValues();
110 5097 : for (size_t i = 0; i < in_keys.size(); ++i) {
111 2127 : auto inserted = values.emplace(in_keys[i], in_values[i]);
112 2127 : if (!inserted.second) {
113 3 : errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
114 3 : values.clear();
115 3 : break;
116 : }
117 2124 : }
118 2973 : return errors.empty();
119 4517 : }
120 :
121 5538 : bool WriteSettings(const fs::path& path,
122 : const std::map<std::string, SettingsValue>& values,
123 : std::vector<std::string>& errors)
124 : {
125 5538 : SettingsValue out(SettingsValue::VOBJ);
126 8687 : for (const auto& value : values) {
127 3149 : out.__pushKV(value.first, value.second);
128 : }
129 5538 : std::ofstream file;
130 5538 : file.open(path);
131 5538 : if (file.fail()) {
132 0 : errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));
133 0 : return false;
134 : }
135 5538 : file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
136 5538 : file.close();
137 5538 : return true;
138 5538 : }
139 :
140 11111512 : SettingsValue GetSetting(const Settings& settings,
141 : const std::string& section,
142 : const std::string& name,
143 : bool ignore_default_section_config,
144 : bool ignore_nonpersistent,
145 : bool get_chain_name)
146 : {
147 11111512 : SettingsValue result;
148 11111512 : bool done = false; // Done merging any more settings sources.
149 13981489 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
150 : // Weird behavior preserved for backwards compatibility: Apply negated
151 : // setting even if non-negated setting would be ignored. A negated
152 : // value in the default section is applied to network specific options,
153 : // even though normal non-negated values there would be ignored.
154 2869977 : const bool never_ignore_negated_setting = span.last_negated();
155 :
156 : // Weird behavior preserved for backwards compatibility: Take first
157 : // assigned value instead of last. In general, later settings take
158 : // precedence over early settings, but for backwards compatibility in
159 : // the config file the precedence is reversed for all settings except
160 : // chain name settings.
161 2869977 : const bool reverse_precedence =
162 2869977 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
163 2595512 : !get_chain_name;
164 :
165 : // Weird behavior preserved for backwards compatibility: Negated
166 : // -regtest and -testnet arguments which you would expect to override
167 : // values set in the configuration file are currently accepted but
168 : // silently ignored. It would be better to apply these just like other
169 : // negated values, or at least warn they are ignored.
170 2869977 : const bool skip_negated_command_line = get_chain_name;
171 :
172 2869977 : if (done) return;
173 :
174 : // Ignore settings in default config section if requested.
175 2710077 : if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
176 1479 : !never_ignore_negated_setting) {
177 495 : return;
178 : }
179 :
180 : // Ignore nonpersistent settings if requested.
181 2709582 : if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
182 :
183 : // Skip negated command line settings.
184 2709582 : if (skip_negated_command_line && span.last_negated()) return;
185 :
186 2708572 : if (!span.empty()) {
187 2674966 : result = reverse_precedence ? span.begin()[0] : span.end()[-1];
188 2674966 : done = true;
189 2708572 : } else if (span.last_negated()) {
190 33499 : result = false;
191 33499 : done = true;
192 33499 : }
193 2869977 : });
194 11111512 : return result;
195 11111512 : }
196 :
197 333974 : std::vector<SettingsValue> GetSettingsList(const Settings& settings,
198 : const std::string& section,
199 : const std::string& name,
200 : bool ignore_default_section_config)
201 : {
202 333974 : std::vector<SettingsValue> result;
203 333974 : bool done = false; // Done merging any more settings sources.
204 333974 : bool prev_negated_empty = false;
205 475420 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
206 : // Weird behavior preserved for backwards compatibility: Apply config
207 : // file settings even if negated on command line. Negating a setting on
208 : // command line will ignore earlier settings on the command line and
209 : // ignore settings in the config file, unless the negated command line
210 : // value is followed by non-negated value, in which case config file
211 : // settings will be brought back from the dead (but earlier command
212 : // line settings will still be ignored).
213 141446 : const bool add_zombie_config_values =
214 141446 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
215 71380 : !prev_negated_empty;
216 :
217 : // Ignore settings in default config section if requested.
218 141446 : if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
219 :
220 : // Add new settings to the result if isn't already complete, or if the
221 : // values are zombies.
222 133000 : if (!done || add_zombie_config_values) {
223 238854 : for (const auto& value : span) {
224 124047 : if (value.isArray()) {
225 1112 : result.insert(result.end(), value.getValues().begin(), value.getValues().end());
226 1112 : } else {
227 122935 : result.push_back(value);
228 : }
229 : }
230 114807 : }
231 :
232 : // If a setting was negated, or if a setting was forced, set
233 : // done to true to ignore any later lower priority settings.
234 133000 : done |= span.negated() > 0 || source == Source::FORCED;
235 :
236 : // Update the negated and empty state used for the zombie values check.
237 133000 : prev_negated_empty |= span.last_negated() && result.empty();
238 141446 : });
239 333974 : return result;
240 333974 : }
241 :
242 36044 : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
243 : {
244 36044 : bool has_default_section_setting = false;
245 36044 : bool has_other_setting = false;
246 75923 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
247 39879 : if (span.empty()) return;
248 28553 : else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
249 24043 : else has_other_setting = true;
250 39879 : });
251 : // If a value is set in the default section and not explicitly overwritten by the
252 : // user on the command line or in a different section, then we want to enable
253 : // warnings about the value being ignored.
254 36044 : return has_default_section_setting && !has_other_setting;
255 : }
256 :
257 2960046 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
258 2563788 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
259 340798 : const SettingsValue* SettingsSpan::end() const { return data + size; }
260 2748458 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
261 5803704 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
262 2696848 : size_t SettingsSpan::negated() const
263 : {
264 5416568 : for (size_t i = size; i > 0; --i) {
265 2782199 : if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
266 2719720 : }
267 2634369 : return 0;
268 2696848 : }
269 :
270 : } // namespace util
|