LCOV - code coverage report
Current view: top level - src/util - settings.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 131 137 95.6 %
Date: 2021-06-29 14:35:33 Functions: 17 17 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 131 134 97.8 %

           Branch data     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                 :            : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
#      28                 :    2332533 : {
#      29                 :            :     // Merge in the forced settings
#      30 [ +  + ][ +  + ]:    2332533 :     if (auto* value = FindKey(settings.forced_settings, name)) {
#                 [ +  + ]
#      31                 :     128657 :         fn(SettingsSpan(*value), Source::FORCED);
#      32                 :     128657 :     }
#      33                 :            :     // Merge in the command-line options
#      34 [ +  + ][ +  + ]:    2332533 :     if (auto* values = FindKey(settings.command_line_options, name)) {
#                 [ +  + ]
#      35                 :     289713 :         fn(SettingsSpan(*values), Source::COMMAND_LINE);
#      36                 :     289713 :     }
#      37                 :            :     // Merge in the read-write settings
#      38 [ +  + ][ +  + ]:    2332533 :     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
#                 [ +  + ]
#      39                 :        666 :         fn(SettingsSpan(*value), Source::RW_SETTINGS);
#      40                 :        666 :     }
#      41                 :            :     // Merge in the network-specific section of the config file
#      42 [ +  + ][ +  + ]:    2332533 :     if (!section.empty()) {
#                 [ +  - ]
#      43 [ +  + ][ +  + ]:    2282298 :         if (auto* map = FindKey(settings.ro_config, section)) {
#                 [ +  + ]
#      44 [ +  + ][ +  + ]:    1845934 :             if (auto* values = FindKey(*map, name)) {
#                 [ +  + ]
#      45                 :     155938 :                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
#      46                 :     155938 :             }
#      47                 :    1845934 :         }
#      48                 :    2282298 :     }
#      49                 :            :     // Merge in the default section of the config file
#      50 [ +  + ][ +  + ]:    2332533 :     if (auto* map = FindKey(settings.ro_config, "")) {
#                 [ +  + ]
#      51 [ +  + ][ +  + ]:    2043154 :         if (auto* values = FindKey(*map, name)) {
#                 [ +  + ]
#      52                 :     264811 :             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
#      53                 :     264811 :         }
#      54                 :    2043154 :     }
#      55                 :    2332533 : }
#      56                 :            : } // namespace
#      57                 :            : 
#      58                 :            : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
#      59                 :        681 : {
#      60                 :        681 :     values.clear();
#      61                 :        681 :     errors.clear();
#      62                 :            : 
#      63                 :        681 :     fsbridge::ifstream file;
#      64                 :        681 :     file.open(path);
#      65         [ +  + ]:        681 :     if (!file.is_open()) return true; // Ok for file not to exist.
#      66                 :            : 
#      67                 :        306 :     SettingsValue in;
#      68         [ +  + ]:        306 :     if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
#      69                 :          3 :         errors.emplace_back(strprintf("Unable to parse settings file %s", path.string()));
#      70                 :          3 :         return false;
#      71                 :          3 :     }
#      72                 :            : 
#      73         [ -  + ]:        303 :     if (file.fail()) {
#      74                 :          0 :         errors.emplace_back(strprintf("Failed reading settings file %s", path.string()));
#      75                 :          0 :         return false;
#      76                 :          0 :     }
#      77                 :        303 :     file.close(); // Done with file descriptor. Release while copying data.
#      78                 :            : 
#      79         [ +  + ]:        303 :     if (!in.isObject()) {
#      80                 :          3 :         errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), path.string()));
#      81                 :          3 :         return false;
#      82                 :          3 :     }
#      83                 :            : 
#      84                 :        300 :     const std::vector<std::string>& in_keys = in.getKeys();
#      85                 :        300 :     const std::vector<SettingsValue>& in_values = in.getValues();
#      86         [ +  + ]:        502 :     for (size_t i = 0; i < in_keys.size(); ++i) {
#      87                 :        202 :         auto inserted = values.emplace(in_keys[i], in_values[i]);
#      88         [ +  + ]:        202 :         if (!inserted.second) {
#      89                 :          3 :             errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], path.string()));
#      90                 :          3 :         }
#      91                 :        202 :     }
#      92                 :        300 :     return errors.empty();
#      93                 :        300 : }
#      94                 :            : 
#      95                 :            : bool WriteSettings(const fs::path& path,
#      96                 :            :     const std::map<std::string, SettingsValue>& values,
#      97                 :            :     std::vector<std::string>& errors)
#      98                 :        909 : {
#      99                 :        909 :     SettingsValue out(SettingsValue::VOBJ);
#     100         [ +  + ]:        909 :     for (const auto& value : values) {
#     101                 :        429 :         out.__pushKV(value.first, value.second);
#     102                 :        429 :     }
#     103                 :        909 :     fsbridge::ofstream file;
#     104                 :        909 :     file.open(path);
#     105         [ -  + ]:        909 :     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                 :          0 :     }
#     109                 :        909 :     file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
#     110                 :        909 :     file.close();
#     111                 :        909 :     return true;
#     112                 :        909 : }
#     113                 :            : 
#     114                 :            : 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                 :    2228197 : {
#     120                 :    2228197 :     SettingsValue result;
#     121                 :    2228197 :     bool done = false; // Done merging any more settings sources.
#     122                 :    2228197 :     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                 :     632410 :         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                 :     632410 :         const bool reverse_precedence =
#     135 [ +  + ][ +  + ]:     632410 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
#     136         [ +  + ]:     632410 :             !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                 :     632410 :         const bool skip_negated_command_line = get_chain_name;
#     144                 :            : 
#     145         [ +  + ]:     632410 :         if (done) return;
#     146                 :            : 
#     147                 :            :         // Ignore settings in default config section if requested.
#     148 [ +  + ][ +  + ]:     320008 :         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
#     149         [ +  + ]:     320008 :             !never_ignore_negated_setting) {
#     150                 :        990 :             return;
#     151                 :        990 :         }
#     152                 :            : 
#     153                 :            :         // Skip negated command line settings.
#     154 [ +  + ][ +  + ]:     319018 :         if (skip_negated_command_line && span.last_negated()) return;
#     155                 :            : 
#     156         [ +  + ]:     316998 :         if (!span.empty()) {
#     157         [ +  + ]:     250003 :             result = reverse_precedence ? span.begin()[0] : span.end()[-1];
#     158                 :     250003 :             done = true;
#     159         [ +  + ]:     250003 :         } else if (span.last_negated()) {
#     160                 :      66781 :             result = false;
#     161                 :      66781 :             done = true;
#     162                 :      66781 :         }
#     163                 :     316998 :     });
#     164                 :    2228197 :     return result;
#     165                 :    2228197 : }
#     166                 :            : 
#     167                 :            : std::vector<SettingsValue> GetSettingsList(const Settings& settings,
#     168                 :            :     const std::string& section,
#     169                 :            :     const std::string& name,
#     170                 :            :     bool ignore_default_section_config)
#     171                 :      77784 : {
#     172                 :      77784 :     std::vector<SettingsValue> result;
#     173                 :      77784 :     bool done = false; // Done merging any more settings sources.
#     174                 :      77784 :     bool prev_negated_empty = false;
#     175                 :     150179 :     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                 :     150179 :         const bool add_zombie_config_values =
#     184 [ +  + ][ +  + ]:     150179 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
#     185         [ +  + ]:     150179 :             !prev_negated_empty;
#     186                 :            : 
#     187                 :            :         // Ignore settings in default config section if requested.
#     188 [ +  + ][ +  + ]:     150179 :         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 [ +  + ][ +  + ]:     133287 :         if (!done || add_zombie_config_values) {
#     193         [ +  + ]:     102246 :             for (const auto& value : span) {
#     194         [ +  + ]:     102246 :                 if (value.isArray()) {
#     195                 :        261 :                     result.insert(result.end(), value.getValues().begin(), value.getValues().end());
#     196                 :     101985 :                 } else {
#     197                 :     101985 :                     result.push_back(value);
#     198                 :     101985 :                 }
#     199                 :     102246 :             }
#     200                 :      97685 :         }
#     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 [ +  + ][ +  + ]:     133287 :         done |= span.negated() > 0 || source == Source::FORCED;
#     205                 :            : 
#     206                 :            :         // Update the negated and empty state used for the zombie values check.
#     207 [ +  + ][ +  + ]:     133287 :         prev_negated_empty |= span.last_negated() && result.empty();
#     208                 :     133287 :     });
#     209                 :      77784 :     return result;
#     210                 :      77784 : }
#     211                 :            : 
#     212                 :            : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
#     213                 :      26552 : {
#     214                 :      26552 :     bool has_default_section_setting = false;
#     215                 :      26552 :     bool has_other_setting = false;
#     216                 :      57196 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
#     217         [ +  + ]:      57196 :         if (span.empty()) return;
#     218         [ +  + ]:      34675 :         else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
#     219                 :      25658 :         else has_other_setting = true;
#     220                 :      57196 :     });
#     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 [ +  + ][ +  + ]:      26552 :     return has_default_section_setting && !has_other_setting;
#     225                 :      26552 : }
#     226                 :            : 
#     227                 :     710493 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
#     228                 :     163115 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
#     229                 :     282260 : const SettingsValue* SettingsSpan::end() const { return data + size; }
#     230 [ +  + ][ +  + ]:     374194 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
#     231 [ +  + ][ +  + ]:    1216774 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
#     232                 :            : size_t SettingsSpan::negated() const
#     233                 :     296431 : {
#     234         [ +  + ]:     610669 :     for (size_t i = size; i > 0; --i) {
#     235         [ +  + ]:     437524 :         if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
#     236                 :     437524 :     }
#     237                 :     296431 :     return 0;
#     238                 :     296431 : }
#     239                 :            : 
#     240                 :            : } // namespace util

Generated by: LCOV version 1.14