Changeset View
Changeset View
Standalone View
Standalone View
src/util/settings.cpp
- This file was added.
// Copyright (c) 2019 The Bitcoin Core developers | |||||
// Distributed under the MIT software license, see the accompanying | |||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | |||||
#include <util/settings.h> | |||||
#include <univalue.h> | |||||
namespace util { | |||||
namespace { | |||||
enum class Source { | |||||
FORCED, | |||||
COMMAND_LINE, | |||||
CONFIG_FILE_NETWORK_SECTION, | |||||
CONFIG_FILE_DEFAULT_SECTION | |||||
}; | |||||
//! Merge settings from multiple sources in precedence order: | |||||
//! Forced config > command line > config file network-specific section > | |||||
//! config file default section | |||||
//! | |||||
//! This function is provided with a callback function fn that contains | |||||
//! specific logic for how to merge the sources. | |||||
template <typename Fn> | |||||
static void MergeSettings(const Settings &settings, | |||||
const std::string §ion, | |||||
const std::string &name, Fn &&fn) { | |||||
// Merge in the forced settings | |||||
if (auto *value = FindKey(settings.forced_settings, name)) { | |||||
fn(SettingsSpan(*value), Source::FORCED); | |||||
} | |||||
// Merge in the command-line options | |||||
if (auto *values = FindKey(settings.command_line_options, name)) { | |||||
fn(SettingsSpan(*values), Source::COMMAND_LINE); | |||||
} | |||||
// Merge in the network-specific section of the config file | |||||
if (!section.empty()) { | |||||
if (auto *map = FindKey(settings.ro_config, section)) { | |||||
if (auto *values = FindKey(*map, name)) { | |||||
fn(SettingsSpan(*values), | |||||
Source::CONFIG_FILE_NETWORK_SECTION); | |||||
} | |||||
} | |||||
} | |||||
// Merge in the default section of the config file | |||||
if (auto *map = FindKey(settings.ro_config, "")) { | |||||
if (auto *values = FindKey(*map, name)) { | |||||
fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | |||||
} | |||||
} | |||||
} | |||||
} // namespace | |||||
SettingsValue GetSetting(const Settings &settings, const std::string §ion, | |||||
const std::string &name, | |||||
bool ignore_default_section_config, | |||||
bool get_chain_name) { | |||||
SettingsValue result; | |||||
MergeSettings( | |||||
settings, section, name, [&](SettingsSpan span, Source source) { | |||||
// Weird behavior preserved for backwards compatibility: Apply | |||||
// negated setting even if non-negated setting would be ignored. A | |||||
// negated value in the default section is applied to network | |||||
// specific options, even though normal non-negated values there | |||||
// would be ignored. | |||||
const bool never_ignore_negated_setting = span.last_negated(); | |||||
// Weird behavior preserved for backwards compatibility: Take first | |||||
// assigned value instead of last. In general, later settings take | |||||
// precedence over early settings, but for backwards compatibility | |||||
// in the config file the precedence is reversed for all settings | |||||
// except chain name settings. | |||||
const bool reverse_precedence = | |||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || | |||||
source == Source::CONFIG_FILE_DEFAULT_SECTION) && | |||||
!get_chain_name; | |||||
// Weird behavior preserved for backwards compatibility: Negated | |||||
// -regtest and -testnet arguments which you would expect to | |||||
// override values set in the configuration file are currently | |||||
// accepted but silently ignored. It would be better to apply these | |||||
// just like other negated values, or at least warn they are | |||||
// ignored. | |||||
const bool skip_negated_command_line = get_chain_name; | |||||
// Ignore settings in default config section if requested. | |||||
if (ignore_default_section_config && | |||||
source == Source::CONFIG_FILE_DEFAULT_SECTION && | |||||
!never_ignore_negated_setting) { | |||||
return; | |||||
} | |||||
// Skip negated command line settings. | |||||
if (skip_negated_command_line && span.last_negated()) { | |||||
return; | |||||
} | |||||
// Stick with highest priority value, keeping result if already set. | |||||
if (!result.isNull()) { | |||||
return; | |||||
} | |||||
if (!span.empty()) { | |||||
result = reverse_precedence ? span.begin()[0] : span.end()[-1]; | |||||
} else if (span.last_negated()) { | |||||
result = false; | |||||
} | |||||
}); | |||||
return result; | |||||
} | |||||
std::vector<SettingsValue> GetSettingsList(const Settings &settings, | |||||
const std::string §ion, | |||||
const std::string &name, | |||||
bool ignore_default_section_config) { | |||||
std::vector<SettingsValue> result; | |||||
bool result_complete = false; | |||||
bool prev_negated_empty = false; | |||||
MergeSettings( | |||||
settings, section, name, [&](SettingsSpan span, Source source) { | |||||
// Weird behavior preserved for backwards compatibility: Apply | |||||
// config file settings even if negated on command line. Negating a | |||||
// setting on command line will ignore earlier settings on the | |||||
// command line and ignore settings in the config file, unless the | |||||
// negated command line value is followed by non-negated value, in | |||||
// which case config file settings will be brought back from the | |||||
// dead (but earlier command line settings will still be ignored). | |||||
const bool add_zombie_config_values = | |||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || | |||||
source == Source::CONFIG_FILE_DEFAULT_SECTION) && | |||||
!prev_negated_empty; | |||||
// Ignore settings in default config section if requested. | |||||
if (ignore_default_section_config && | |||||
source == Source::CONFIG_FILE_DEFAULT_SECTION) { | |||||
return; | |||||
} | |||||
// Add new settings to the result if isn't already complete, or if | |||||
// the values are zombies. | |||||
if (!result_complete || add_zombie_config_values) { | |||||
for (const auto &value : span) { | |||||
if (value.isArray()) { | |||||
result.insert(result.end(), value.getValues().begin(), | |||||
value.getValues().end()); | |||||
} else { | |||||
result.push_back(value); | |||||
} | |||||
} | |||||
} | |||||
// If a setting was negated, or if a setting was forced, set | |||||
// result_complete to true to ignore any later lower priority | |||||
// settings. | |||||
result_complete |= span.negated() > 0 || source == Source::FORCED; | |||||
// Update the negated and empty state used for the zombie values | |||||
// check. | |||||
prev_negated_empty |= span.last_negated() && result.empty(); | |||||
}); | |||||
return result; | |||||
} | |||||
bool OnlyHasDefaultSectionSetting(const Settings &settings, | |||||
const std::string §ion, | |||||
const std::string &name) { | |||||
bool has_default_section_setting = false; | |||||
bool has_other_setting = false; | |||||
MergeSettings( | |||||
settings, section, name, [&](SettingsSpan span, Source source) { | |||||
if (span.empty()) { | |||||
return; | |||||
} else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) { | |||||
has_default_section_setting = true; | |||||
} else { | |||||
has_other_setting = true; | |||||
} | |||||
}); | |||||
// If a value is set in the default section and not explicitly overwritten | |||||
// by the user on the command line or in a different section, then we want | |||||
// to enable warnings about the value being ignored. | |||||
return has_default_section_setting && !has_other_setting; | |||||
} | |||||
SettingsSpan::SettingsSpan(const std::vector<SettingsValue> &vec) noexcept | |||||
: SettingsSpan(vec.data(), vec.size()) {} | |||||
const SettingsValue *SettingsSpan::begin() const { | |||||
return data + negated(); | |||||
} | |||||
const SettingsValue *SettingsSpan::end() const { | |||||
return data + size; | |||||
} | |||||
bool SettingsSpan::empty() const { | |||||
return size == 0 || last_negated(); | |||||
} | |||||
bool SettingsSpan::last_negated() const { | |||||
return size > 0 && data[size - 1].isFalse(); | |||||
} | |||||
size_t SettingsSpan::negated() const { | |||||
for (size_t i = size; i > 0; --i) { | |||||
if (data[i - 1].isFalse()) { | |||||
// Return number of negated values (position of last false value) | |||||
return i; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
} // namespace util |