LCOV - code coverage report
Current view: top level - src/util - settings.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 133 142 93.7 %
Date: 2022-04-21 14:51:19 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: 137 142 96.5 %

           Branch data     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                 :            : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
#      34                 :    2289906 : {
#      35                 :            :     // Merge in the forced settings
#      36 [ +  + ][ +  + ]:    2289906 :     if (auto* value = FindKey(settings.forced_settings, name)) {
#                 [ +  + ]
#      37                 :     131393 :         fn(SettingsSpan(*value), Source::FORCED);
#      38                 :     131393 :     }
#      39                 :            :     // Merge in the command-line options
#      40 [ +  + ][ +  + ]:    2289906 :     if (auto* values = FindKey(settings.command_line_options, name)) {
#                 [ +  + ]
#      41                 :     320363 :         fn(SettingsSpan(*values), Source::COMMAND_LINE);
#      42                 :     320363 :     }
#      43                 :            :     // Merge in the read-write settings
#      44 [ +  + ][ +  + ]:    2289906 :     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
#                 [ +  + ]
#      45                 :        784 :         fn(SettingsSpan(*value), Source::RW_SETTINGS);
#      46                 :        784 :     }
#      47                 :            :     // Merge in the network-specific section of the config file
#      48 [ +  + ][ +  + ]:    2289906 :     if (!section.empty()) {
#                 [ +  - ]
#      49 [ +  + ][ +  + ]:    2210058 :         if (auto* map = FindKey(settings.ro_config, section)) {
#                 [ +  + ]
#      50 [ +  + ][ +  + ]:    1860130 :             if (auto* values = FindKey(*map, name)) {
#                 [ +  + ]
#      51                 :     175287 :                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
#      52                 :     175287 :             }
#      53                 :    1860130 :         }
#      54                 :    2210058 :     }
#      55                 :            :     // Merge in the default section of the config file
#      56 [ +  + ][ +  + ]:    2289906 :     if (auto* map = FindKey(settings.ro_config, "")) {
#                 [ +  + ]
#      57 [ +  + ][ +  + ]:    2070824 :         if (auto* values = FindKey(*map, name)) {
#                 [ +  + ]
#      58                 :     267473 :             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
#      59                 :     267473 :         }
#      60                 :    2070824 :     }
#      61                 :    2289906 : }
#      62                 :            : } // namespace
#      63                 :            : 
#      64                 :            : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
#      65                 :       1155 : {
#      66                 :       1155 :     values.clear();
#      67                 :       1155 :     errors.clear();
#      68                 :            : 
#      69                 :            :     // Ok for file to not exist
#      70         [ +  + ]:       1155 :     if (!fs::exists(path)) return true;
#      71                 :            : 
#      72                 :        730 :     std::ifstream file;
#      73                 :        730 :     file.open(path);
#      74         [ -  + ]:        730 :     if (!file.is_open()) {
#      75                 :          0 :       errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));
#      76                 :          0 :       return false;
#      77                 :          0 :     }
#      78                 :            : 
#      79                 :        730 :     SettingsValue in;
#      80         [ +  + ]:        730 :     if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
#      81                 :          3 :         errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
#      82                 :          3 :         return false;
#      83                 :          3 :     }
#      84                 :            : 
#      85         [ -  + ]:        727 :     if (file.fail()) {
#      86                 :          0 :         errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
#      87                 :          0 :         return false;
#      88                 :          0 :     }
#      89                 :        727 :     file.close(); // Done with file descriptor. Release while copying data.
#      90                 :            : 
#      91         [ +  + ]:        727 :     if (!in.isObject()) {
#      92                 :          3 :         errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
#      93                 :          3 :         return false;
#      94                 :          3 :     }
#      95                 :            : 
#      96                 :        724 :     const std::vector<std::string>& in_keys = in.getKeys();
#      97                 :        724 :     const std::vector<SettingsValue>& in_values = in.getValues();
#      98         [ +  + ]:       1257 :     for (size_t i = 0; i < in_keys.size(); ++i) {
#      99                 :        533 :         auto inserted = values.emplace(in_keys[i], in_values[i]);
#     100         [ +  + ]:        533 :         if (!inserted.second) {
#     101                 :          3 :             errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
#     102                 :          3 :         }
#     103                 :        533 :     }
#     104                 :        724 :     return errors.empty();
#     105                 :        727 : }
#     106                 :            : 
#     107                 :            : bool WriteSettings(const fs::path& path,
#     108                 :            :     const std::map<std::string, SettingsValue>& values,
#     109                 :            :     std::vector<std::string>& errors)
#     110                 :       1720 : {
#     111                 :       1720 :     SettingsValue out(SettingsValue::VOBJ);
#     112         [ +  + ]:       1720 :     for (const auto& value : values) {
#     113                 :       1111 :         out.__pushKV(value.first, value.second);
#     114                 :       1111 :     }
#     115                 :       1720 :     std::ofstream file;
#     116                 :       1720 :     file.open(path);
#     117         [ -  + ]:       1720 :     if (file.fail()) {
#     118                 :          0 :         errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));
#     119                 :          0 :         return false;
#     120                 :          0 :     }
#     121                 :       1720 :     file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
#     122                 :       1720 :     file.close();
#     123                 :       1720 :     return true;
#     124                 :       1720 : }
#     125                 :            : 
#     126                 :            : SettingsValue GetSetting(const Settings& settings,
#     127                 :            :     const std::string& section,
#     128                 :            :     const std::string& name,
#     129                 :            :     bool ignore_default_section_config,
#     130                 :            :     bool ignore_nonpersistent,
#     131                 :            :     bool get_chain_name)
#     132                 :    2180846 : {
#     133                 :    2180846 :     SettingsValue result;
#     134                 :    2180846 :     bool done = false; // Done merging any more settings sources.
#     135                 :    2180846 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
#     136                 :            :         // Weird behavior preserved for backwards compatibility: Apply negated
#     137                 :            :         // setting even if non-negated setting would be ignored. A negated
#     138                 :            :         // value in the default section is applied to network specific options,
#     139                 :            :         // even though normal non-negated values there would be ignored.
#     140                 :     682764 :         const bool never_ignore_negated_setting = span.last_negated();
#     141                 :            : 
#     142                 :            :         // Weird behavior preserved for backwards compatibility: Take first
#     143                 :            :         // assigned value instead of last. In general, later settings take
#     144                 :            :         // precedence over early settings, but for backwards compatibility in
#     145                 :            :         // the config file the precedence is reversed for all settings except
#     146                 :            :         // chain name settings.
#     147                 :     682764 :         const bool reverse_precedence =
#     148 [ +  + ][ +  + ]:     682764 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
#     149         [ +  + ]:     682764 :             !get_chain_name;
#     150                 :            : 
#     151                 :            :         // Weird behavior preserved for backwards compatibility: Negated
#     152                 :            :         // -regtest and -testnet arguments which you would expect to override
#     153                 :            :         // values set in the configuration file are currently accepted but
#     154                 :            :         // silently ignored. It would be better to apply these just like other
#     155                 :            :         // negated values, or at least warn they are ignored.
#     156                 :     682764 :         const bool skip_negated_command_line = get_chain_name;
#     157                 :            : 
#     158         [ +  + ]:     682764 :         if (done) return;
#     159                 :            : 
#     160                 :            :         // Ignore settings in default config section if requested.
#     161 [ +  + ][ +  + ]:     361842 :         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&
#     162         [ +  + ]:     361842 :             !never_ignore_negated_setting) {
#     163                 :        990 :             return;
#     164                 :        990 :         }
#     165                 :            : 
#     166                 :            :         // Ignore nonpersistent settings if requested.
#     167 [ +  + ][ -  + ]:     360852 :         if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
#                 [ +  + ]
#     168                 :            : 
#     169                 :            :         // Skip negated command line settings.
#     170 [ +  + ][ +  + ]:     360833 :         if (skip_negated_command_line && span.last_negated()) return;
#     171                 :            : 
#     172         [ +  + ]:     358813 :         if (!span.empty()) {
#     173         [ +  + ]:     291792 :             result = reverse_precedence ? span.begin()[0] : span.end()[-1];
#     174                 :     291792 :             done = true;
#     175         [ +  + ]:     291792 :         } else if (span.last_negated()) {
#     176                 :      66807 :             result = false;
#     177                 :      66807 :             done = true;
#     178                 :      66807 :         }
#     179                 :     358813 :     });
#     180                 :    2180846 :     return result;
#     181                 :    2180846 : }
#     182                 :            : 
#     183                 :            : std::vector<SettingsValue> GetSettingsList(const Settings& settings,
#     184                 :            :     const std::string& section,
#     185                 :            :     const std::string& name,
#     186                 :            :     bool ignore_default_section_config)
#     187                 :      81076 : {
#     188                 :      81076 :     std::vector<SettingsValue> result;
#     189                 :      81076 :     bool done = false; // Done merging any more settings sources.
#     190                 :      81076 :     bool prev_negated_empty = false;
#     191                 :     154084 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
#     192                 :            :         // Weird behavior preserved for backwards compatibility: Apply config
#     193                 :            :         // file settings even if negated on command line. Negating a setting on
#     194                 :            :         // command line will ignore earlier settings on the command line and
#     195                 :            :         // ignore settings in the config file, unless the negated command line
#     196                 :            :         // value is followed by non-negated value, in which case config file
#     197                 :            :         // settings will be brought back from the dead (but earlier command
#     198                 :            :         // line settings will still be ignored).
#     199                 :     154084 :         const bool add_zombie_config_values =
#     200 [ +  + ][ +  + ]:     154084 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
#     201         [ +  + ]:     154084 :             !prev_negated_empty;
#     202                 :            : 
#     203                 :            :         // Ignore settings in default config section if requested.
#     204 [ +  + ][ +  + ]:     154084 :         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
#     205                 :            : 
#     206                 :            :         // Add new settings to the result if isn't already complete, or if the
#     207                 :            :         // values are zombies.
#     208 [ +  + ][ +  + ]:     137192 :         if (!done || add_zombie_config_values) {
#     209         [ +  + ]:     106284 :             for (const auto& value : span) {
#     210         [ +  + ]:     106284 :                 if (value.isArray()) {
#     211                 :        248 :                     result.insert(result.end(), value.getValues().begin(), value.getValues().end());
#     212                 :     106036 :                 } else {
#     213                 :     106036 :                     result.push_back(value);
#     214                 :     106036 :                 }
#     215                 :     106284 :             }
#     216                 :     101594 :         }
#     217                 :            : 
#     218                 :            :         // If a setting was negated, or if a setting was forced, set
#     219                 :            :         // done to true to ignore any later lower priority settings.
#     220 [ +  + ][ +  + ]:     137192 :         done |= span.negated() > 0 || source == Source::FORCED;
#     221                 :            : 
#     222                 :            :         // Update the negated and empty state used for the zombie values check.
#     223 [ +  + ][ +  + ]:     137192 :         prev_negated_empty |= span.last_negated() && result.empty();
#     224                 :     137192 :     });
#     225                 :      81076 :     return result;
#     226                 :      81076 : }
#     227                 :            : 
#     228                 :            : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
#     229                 :      27984 : {
#     230                 :      27984 :     bool has_default_section_setting = false;
#     231                 :      27984 :     bool has_other_setting = false;
#     232                 :      58452 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
#     233         [ +  + ]:      58452 :         if (span.empty()) return;
#     234         [ +  + ]:      35933 :         else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
#     235                 :      26916 :         else has_other_setting = true;
#     236                 :      58452 :     });
#     237                 :            :     // If a value is set in the default section and not explicitly overwritten by the
#     238                 :            :     // user on the command line or in a different section, then we want to enable
#     239                 :            :     // warnings about the value being ignored.
#     240 [ +  + ][ +  + ]:      27984 :     return has_default_section_setting && !has_other_setting;
#     241                 :      27984 : }
#     242                 :            : 
#     243                 :     763160 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
#     244                 :     173825 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
#     245                 :     321161 : const SettingsValue* SettingsSpan::end() const { return data + size; }
#     246 [ +  + ][ +  + ]:     417273 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
#     247 [ +  + ][ +  + ]:    1316783 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
#     248                 :            : size_t SettingsSpan::negated() const
#     249                 :     311046 : {
#     250         [ +  + ]:     640157 :     for (size_t i = size; i > 0; --i) {
#     251         [ +  + ]:     452389 :         if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
#     252                 :     452389 :     }
#     253                 :     187768 :     return 0;
#     254                 :     311046 : }
#     255                 :            : 
#     256                 :            : } // namespace util

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a