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 694362 : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
34 : {
35 : // Merge in the forced settings
36 694362 : if (auto* value = FindKey(settings.forced_settings, name)) {
37 66752 : fn(SettingsSpan(*value), Source::FORCED);
38 66752 : }
39 : // Merge in the command-line options
40 694362 : if (auto* values = FindKey(settings.command_line_options, name)) {
41 134731 : fn(SettingsSpan(*values), Source::COMMAND_LINE);
42 134731 : }
43 : // Merge in the read-write settings
44 694362 : if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
45 65 : fn(SettingsSpan(*value), Source::RW_SETTINGS);
46 65 : }
47 : // Merge in the network-specific section of the config file
48 694362 : if (!section.empty()) {
49 679476 : if (auto* map = FindKey(settings.ro_config, section)) {
50 45117 : if (auto* values = FindKey(*map, name)) {
51 45096 : fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
52 45096 : }
53 45117 : }
54 679476 : }
55 : // Merge in the default section of the config file
56 694362 : if (auto* map = FindKey(settings.ro_config, "")) {
57 133944 : if (auto* values = FindKey(*map, name)) {
58 130044 : fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
59 130044 : }
60 133944 : }
61 694362 : }
62 : } // namespace
63 :
64 9 : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
65 : {
66 9 : values.clear();
67 9 : errors.clear();
68 :
69 : // Ok for file to not exist
70 9 : if (!fs::exists(path)) return true;
71 :
72 8 : std::ifstream file;
73 8 : file.open(path);
74 8 : 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 8 : 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 8 : SettingsValue in;
92 8 : if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
93 1 : errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
94 1 : return false;
95 : }
96 :
97 7 : if (file.fail()) {
98 0 : errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
99 0 : return false;
100 : }
101 7 : file.close(); // Done with file descriptor. Release while copying data.
102 :
103 7 : if (!in.isObject()) {
104 1 : errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
105 1 : return false;
106 : }
107 :
108 6 : const std::vector<std::string>& in_keys = in.getKeys();
109 6 : const std::vector<SettingsValue>& in_values = in.getValues();
110 15 : for (size_t i = 0; i < in_keys.size(); ++i) {
111 10 : auto inserted = values.emplace(in_keys[i], in_values[i]);
112 10 : if (!inserted.second) {
113 1 : errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
114 1 : values.clear();
115 1 : break;
116 : }
117 9 : }
118 6 : return errors.empty();
119 9 : }
120 :
121 197 : bool WriteSettings(const fs::path& path,
122 : const std::map<std::string, SettingsValue>& values,
123 : std::vector<std::string>& errors)
124 : {
125 197 : SettingsValue out(SettingsValue::VOBJ);
126 394 : for (const auto& value : values) {
127 197 : out.__pushKV(value.first, value.second);
128 : }
129 197 : std::ofstream file;
130 197 : file.open(path);
131 197 : 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 197 : file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
136 197 : file.close();
137 197 : return true;
138 197 : }
139 :
140 650244 : 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 650244 : SettingsValue result;
148 650244 : bool done = false; // Done merging any more settings sources.
149 925123 : 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 274879 : 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 274879 : const bool reverse_precedence =
162 274879 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
163 128462 : !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 274879 : const bool skip_negated_command_line = get_chain_name;
171 :
172 274879 : if (done) return;
173 :
174 : // Ignore settings in default config section if requested.
175 120689 : 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 120194 : if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
182 :
183 : // Skip negated command line settings.
184 120194 : if (skip_negated_command_line && span.last_negated()) return;
185 :
186 119184 : if (!span.empty()) {
187 85705 : result = reverse_precedence ? span.begin()[0] : span.end()[-1];
188 85705 : done = true;
189 119184 : } else if (span.last_negated()) {
190 33372 : result = false;
191 33372 : done = true;
192 33372 : }
193 274879 : });
194 650244 : return result;
195 650244 : }
196 :
197 32922 : 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 32922 : std::vector<SettingsValue> result;
203 32922 : bool done = false; // Done merging any more settings sources.
204 32922 : bool prev_negated_empty = false;
205 107249 : 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 74327 : const bool add_zombie_config_values =
214 74327 : (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
215 33658 : !prev_negated_empty;
216 :
217 : // Ignore settings in default config section if requested.
218 74327 : 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 65881 : if (!done || add_zombie_config_values) {
223 98408 : for (const auto& value : span) {
224 50297 : if (value.isArray()) {
225 0 : result.insert(result.end(), value.getValues().begin(), value.getValues().end());
226 0 : } else {
227 50297 : result.push_back(value);
228 : }
229 : }
230 48111 : }
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 65881 : done |= span.negated() > 0 || source == Source::FORCED;
235 :
236 : // Update the negated and empty state used for the zombie values check.
237 65881 : prev_negated_empty |= span.last_negated() && result.empty();
238 74327 : });
239 32922 : return result;
240 32922 : }
241 :
242 11196 : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
243 : {
244 11196 : bool has_default_section_setting = false;
245 11196 : bool has_other_setting = false;
246 38678 : MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
247 27482 : if (span.empty()) return;
248 16238 : else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
249 11730 : else has_other_setting = true;
250 27482 : });
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 11196 : return has_default_section_setting && !has_other_setting;
255 : }
256 :
257 309874 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
258 51553 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
259 130376 : const SettingsValue* SettingsSpan::end() const { return data + size; }
260 146669 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
261 523500 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
262 117434 : size_t SettingsSpan::negated() const
263 : {
264 243568 : for (size_t i = size; i > 0; --i) {
265 187707 : if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
266 126134 : }
267 55861 : return 0;
268 117434 : }
269 :
270 : } // namespace util
|