LCOV - code coverage report
Current view: top level - src/util - settings.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 120 125 96.0 %
Date: 2020-09-26 01:30:44 Functions: 17 18 94.4 %

          Line data    Source code
       1             : // Copyright (c) 2019 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 <util/settings.h>
       6             : 
       7             : #include <tinyformat.h>
       8             : #include <univalue.h>
       9             : 
      10             : namespace util {
      11             : namespace {
      12             : 
      13             : enum class Source {
      14             :    FORCED,
      15             :    COMMAND_LINE,
      16             :    RW_SETTINGS,
      17             :    CONFIG_FILE_NETWORK_SECTION,
      18             :    CONFIG_FILE_DEFAULT_SECTION
      19             : };
      20             : 
      21             : //! Merge settings from multiple sources in precedence order:
      22             : //! Forced config > command line > read-write settings file > config file network-specific section > config file default section
      23             : //!
      24             : //! This function is provided with a callback function fn that contains
      25             : //! specific logic for how to merge the sources.
      26             : template <typename Fn>
      27     1666355 : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
      28             : {
      29             :     // Merge in the forced settings
      30     1666355 :     if (auto* value = FindKey(settings.forced_settings, name)) {
      31       56234 :         fn(SettingsSpan(*value), Source::FORCED);
      32       56234 :     }
      33             :     // Merge in the command-line options
      34     1666355 :     if (auto* values = FindKey(settings.command_line_options, name)) {
      35      137246 :         fn(SettingsSpan(*values), Source::COMMAND_LINE);
      36      137246 :     }
      37             :     // Merge in the read-write settings
      38     1666355 :     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
      39        1090 :         fn(SettingsSpan(*value), Source::RW_SETTINGS);
      40        1090 :     }
      41             :     // Merge in the network-specific section of the config file
      42     1666355 :     if (!section.empty()) {
      43     1635266 :         if (auto* map = FindKey(settings.ro_config, section)) {
      44     1363041 :             if (auto* values = FindKey(*map, name)) {
      45       86039 :                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
      46       86039 :             }
      47     1363041 :         }
      48     1635266 :     }
      49             :     // Merge in the default section of the config file
      50     1666355 :     if (auto* map = FindKey(settings.ro_config, "")) {
      51     1437514 :         if (auto* values = FindKey(*map, name)) {
      52      120120 :             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
      53      120120 :         }
      54     1437514 :     }
      55     1666355 : }
      56             : } // namespace
      57             : 
      58         541 : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
      59             : {
      60         541 :     values.clear();
      61         541 :     errors.clear();
      62             : 
      63         541 :     fsbridge::ifstream file;
      64         541 :     file.open(path);
      65         541 :     if (!file.is_open()) return true; // Ok for file not to exist.
      66             : 
      67         234 :     SettingsValue in;
      68         234 :     if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
      69           2 :         errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
      70           2 :         return false;
      71             :     }
      72             : 
      73         232 :     if (file.fail()) {
      74           0 :         errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
      75           0 :         return false;
      76             :     }
      77         232 :     file.close(); // Done with file descriptor. Release while copying data.
      78             : 
      79         232 :     if (!in.isObject()) {
      80           2 :         errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
      81           2 :         return false;
      82             :     }
      83             : 
      84         230 :     const std::vector<std::string>& in_keys = in.getKeys();
      85         230 :     const std::vector<SettingsValue>& in_values = in.getValues();
      86         248 :     for (size_t i = 0; i < in_keys.size(); ++i) {
      87          18 :         auto inserted = values.emplace(in_keys[i], in_values[i]);
      88          18 :         if (!inserted.second) {
      89           2 :             errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
      90           2 :         }
      91             :     }
      92         230 :     return errors.empty();
      93         541 : }
      94             : 
      95         542 : bool WriteSettings(const fs::path& path,
      96             :     const std::map<std::string, SettingsValue>& values,
      97             :     std::vector<std::string>& errors)
      98             : {
      99         542 :     SettingsValue out(SettingsValue::VOBJ);
     100         561 :     for (const auto& value : values) {
     101          19 :         out.__pushKV(value.first, value.second);
     102           0 :     }
     103         542 :     fsbridge::ofstream file;
     104         542 :     file.open(path);
     105         542 :     if (file.fail()) {
     106           0 :         errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", path.string()));
     107           0 :         return false;
     108             :     }
     109         542 :     file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
     110         542 :     file.close();
     111         542 :     return true;
     112         542 : }
     113             : 
     114     1610361 : SettingsValue GetSetting(const Settings& settings,
     115             :     const std::string& section,
     116             :     const std::string& name,
     117             :     bool ignore_default_section_config,
     118             :     bool get_chain_name)
     119             : {
     120     1610361 :     SettingsValue result;
     121     1610361 :     bool done = false; // Done merging any more settings sources.
     122     1915426 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
     123             :         // Weird behavior preserved for backwards compatibility: Apply negated
     124             :         // setting even if non-negated setting would be ignored. A negated
     125             :         // value in the default section is applied to network specific options,
     126             :         // even though normal non-negated values there would be ignored.
     127      305065 :         const bool never_ignore_negated_setting = span.last_negated();
     128             : 
     129             :         // Weird behavior preserved for backwards compatibility: Take first
     130             :         // assigned value instead of last. In general, later settings take
     131             :         // precedence over early settings, but for backwards compatibility in
     132             :         // the config file the precedence is reversed for all settings except
     133             :         // chain name settings.
     134      305065 :         const bool reverse_precedence =
     135      305065 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
     136      159380 :             !get_chain_name;
     137             : 
     138             :         // Weird behavior preserved for backwards compatibility: Negated
     139             :         // -regtest and -testnet arguments which you would expect to override
     140             :         // values set in the configuration file are currently accepted but
     141             :         // silently ignored. It would be better to apply these just like other
     142             :         // negated values, or at least warn they are ignored.
     143      305065 :         const bool skip_negated_command_line = get_chain_name;
     144             : 
     145      305065 :         if (done) return;
     146             : 
     147             :         // Ignore settings in default config section if requested.
     148      150008 :         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
     149             :             !never_ignore_negated_setting) {
     150         266 :             return;
     151             :         }
     152             : 
     153             :         // Skip negated command line settings.
     154      149742 :         if (skip_negated_command_line && span.last_negated()) return;
     155             : 
     156      147722 :         if (!span.empty()) {
     157      117477 :             result = reverse_precedence ? span.begin()[0] : span.end()[-1];
     158      117477 :             done = true;
     159      147722 :         } else if (span.last_negated()) {
     160       30138 :             result = false;
     161       30138 :             done = true;
     162       30138 :         }
     163      305065 :     });
     164             :     return result;
     165     1610361 : }
     166             : 
     167       43990 : std::vector<SettingsValue> GetSettingsList(const Settings& settings,
     168             :     const std::string& section,
     169             :     const std::string& name,
     170             :     bool ignore_default_section_config)
     171             : {
     172       43990 :     std::vector<SettingsValue> result;
     173       43990 :     bool done = false; // Done merging any more settings sources.
     174       43990 :     bool prev_negated_empty = false;
     175      116907 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
     176             :         // Weird behavior preserved for backwards compatibility: Apply config
     177             :         // file settings even if negated on command line. Negating a setting on
     178             :         // command line will ignore earlier settings on the command line and
     179             :         // ignore settings in the config file, unless the negated command line
     180             :         // value is followed by non-negated value, in which case config file
     181             :         // settings will be brought back from the dead (but earlier command
     182             :         // line settings will still be ignored).
     183       72917 :         const bool add_zombie_config_values =
     184       72917 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
     185       34741 :             !prev_negated_empty;
     186             : 
     187             :         // Ignore settings in default config section if requested.
     188       72917 :         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
     189             : 
     190             :         // Add new settings to the result if isn't already complete, or if the
     191             :         // values are zombies.
     192       67043 :         if (!done || add_zombie_config_values) {
     193      102383 :             for (const auto& value : span) {
     194       52471 :                 if (value.isArray()) {
     195         681 :                     result.insert(result.end(), value.getValues().begin(), value.getValues().end());
     196         681 :                 } else {
     197       51790 :                     result.push_back(value);
     198             :                 }
     199             :             }
     200       49912 :         }
     201             : 
     202             :         // If a setting was negated, or if a setting was forced, set
     203             :         // done to true to ignore any later lower priority settings.
     204       67043 :         done |= span.negated() > 0 || source == Source::FORCED;
     205             : 
     206             :         // Update the negated and empty state used for the zombie values check.
     207       67043 :         prev_negated_empty |= span.last_negated() && result.empty();
     208       72917 :     });
     209             :     return result;
     210       43990 : }
     211             : 
     212       12004 : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
     213             : {
     214       12004 :     bool has_default_section_setting = false;
     215       12004 :     bool has_other_setting = false;
     216       34751 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
     217       22747 :         if (span.empty()) return;
     218       13780 :         else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
     219       10559 :         else has_other_setting = true;
     220       22747 :     });
     221             :     // If a value is set in the default section and not explicitly overwritten by the
     222             :     // user on the command line or in a different section, then we want to enable
     223             :     // warnings about the value being ignored.
     224       24008 :     return has_default_section_setting && !has_other_setting;
     225       12004 : }
     226             : 
     227      343433 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
     228       75824 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
     229      141479 : const SettingsValue* SettingsSpan::end() const { return data + size; }
     230      170469 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
     231      581868 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
     232      142894 : size_t SettingsSpan::negated() const
     233             : {
     234      295383 :     for (size_t i = size; i > 0; --i) {
     235      214587 :         if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
     236             :     }
     237       80796 :     return 0;
     238      142894 : }
     239             : 
     240             : } // namespace util

Generated by: LCOV version 1.15