LCOV - code coverage report
Current view: top level - src/util - settings.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 128 139 92.1 %
Date: 2026-06-25 07:23:43 Functions: 17 18 94.4 %

          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    11481530 : static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
      34             : {
      35             :     // Merge in the forced settings
      36    11481530 :     if (auto* value = FindKey(settings.forced_settings, name)) {
      37       88690 :         fn(SettingsSpan(*value), Source::FORCED);
      38       88690 :     }
      39             :     // Merge in the command-line options
      40    11481530 :     if (auto* values = FindKey(settings.command_line_options, name)) {
      41      268564 :         fn(SettingsSpan(*values), Source::COMMAND_LINE);
      42      268564 :     }
      43             :     // Merge in the read-write settings
      44    11481530 :     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
      45        2633 :         fn(SettingsSpan(*value), Source::RW_SETTINGS);
      46        2633 :     }
      47             :     // Merge in the network-specific section of the config file
      48    11481530 :     if (!section.empty()) {
      49    11274228 :         if (auto* map = FindKey(settings.ro_config, section)) {
      50    10637301 :             if (auto* values = FindKey(*map, name)) {
      51     2545188 :                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
      52     2545188 :             }
      53    10637301 :         }
      54    11274228 :     }
      55             :     // Merge in the default section of the config file
      56    11481530 :     if (auto* map = FindKey(settings.ro_config, "")) {
      57    10796126 :         if (auto* values = FindKey(*map, name)) {
      58      146227 :             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
      59      146227 :         }
      60    10796126 :     }
      61    11481530 : }
      62             : } // namespace
      63             : 
      64        4517 : bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)
      65             : {
      66        4517 :     values.clear();
      67        4517 :     errors.clear();
      68             : 
      69             :     // Ok for file to not exist
      70        4517 :     if (!fs::exists(path)) return true;
      71             : 
      72        2979 :     std::ifstream file;
      73        2979 :     file.open(path);
      74        2979 :     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        2979 :     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        2979 :     SettingsValue in;
      92        2979 :     if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {
      93           3 :         errors.emplace_back(strprintf("Unable to parse settings file %s", fs::PathToString(path)));
      94           3 :         return false;
      95             :     }
      96             : 
      97        2976 :     if (file.fail()) {
      98           0 :         errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));
      99           0 :         return false;
     100             :     }
     101        2976 :     file.close(); // Done with file descriptor. Release while copying data.
     102             : 
     103        2976 :     if (!in.isObject()) {
     104           3 :         errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));
     105           3 :         return false;
     106             :     }
     107             : 
     108        2973 :     const std::vector<std::string>& in_keys = in.getKeys();
     109        2973 :     const std::vector<SettingsValue>& in_values = in.getValues();
     110        5097 :     for (size_t i = 0; i < in_keys.size(); ++i) {
     111        2127 :         auto inserted = values.emplace(in_keys[i], in_values[i]);
     112        2127 :         if (!inserted.second) {
     113           3 :             errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));
     114           3 :             values.clear();
     115           3 :             break;
     116             :         }
     117        2124 :     }
     118        2973 :     return errors.empty();
     119        4517 : }
     120             : 
     121        5538 : bool WriteSettings(const fs::path& path,
     122             :     const std::map<std::string, SettingsValue>& values,
     123             :     std::vector<std::string>& errors)
     124             : {
     125        5538 :     SettingsValue out(SettingsValue::VOBJ);
     126        8687 :     for (const auto& value : values) {
     127        3149 :         out.__pushKV(value.first, value.second);
     128             :     }
     129        5538 :     std::ofstream file;
     130        5538 :     file.open(path);
     131        5538 :     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        5538 :     file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;
     136        5538 :     file.close();
     137        5538 :     return true;
     138        5538 : }
     139             : 
     140    11111512 : 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    11111512 :     SettingsValue result;
     148    11111512 :     bool done = false; // Done merging any more settings sources.
     149    13981489 :     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     2869977 :         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     2869977 :         const bool reverse_precedence =
     162     2869977 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
     163     2595512 :             !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     2869977 :         const bool skip_negated_command_line = get_chain_name;
     171             : 
     172     2869977 :         if (done) return;
     173             : 
     174             :         // Ignore settings in default config section if requested.
     175     2710077 :         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     2709582 :         if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;
     182             : 
     183             :         // Skip negated command line settings.
     184     2709582 :         if (skip_negated_command_line && span.last_negated()) return;
     185             : 
     186     2708572 :         if (!span.empty()) {
     187     2674966 :             result = reverse_precedence ? span.begin()[0] : span.end()[-1];
     188     2674966 :             done = true;
     189     2708572 :         } else if (span.last_negated()) {
     190       33499 :             result = false;
     191       33499 :             done = true;
     192       33499 :         }
     193     2869977 :     });
     194    11111512 :     return result;
     195    11111512 : }
     196             : 
     197      333974 : 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      333974 :     std::vector<SettingsValue> result;
     203      333974 :     bool done = false; // Done merging any more settings sources.
     204      333974 :     bool prev_negated_empty = false;
     205      475420 :     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      141446 :         const bool add_zombie_config_values =
     214      141446 :             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
     215       71380 :             !prev_negated_empty;
     216             : 
     217             :         // Ignore settings in default config section if requested.
     218      141446 :         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      133000 :         if (!done || add_zombie_config_values) {
     223      238854 :             for (const auto& value : span) {
     224      124047 :                 if (value.isArray()) {
     225        1112 :                     result.insert(result.end(), value.getValues().begin(), value.getValues().end());
     226        1112 :                 } else {
     227      122935 :                     result.push_back(value);
     228             :                 }
     229             :             }
     230      114807 :         }
     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      133000 :         done |= span.negated() > 0 || source == Source::FORCED;
     235             : 
     236             :         // Update the negated and empty state used for the zombie values check.
     237      133000 :         prev_negated_empty |= span.last_negated() && result.empty();
     238      141446 :     });
     239      333974 :     return result;
     240      333974 : }
     241             : 
     242       36044 : bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
     243             : {
     244       36044 :     bool has_default_section_setting = false;
     245       36044 :     bool has_other_setting = false;
     246       75923 :     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
     247       39879 :         if (span.empty()) return;
     248       28553 :         else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
     249       24043 :         else has_other_setting = true;
     250       39879 :     });
     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       36044 :     return has_default_section_setting && !has_other_setting;
     255             : }
     256             : 
     257     2960046 : SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
     258     2563788 : const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
     259      340798 : const SettingsValue* SettingsSpan::end() const { return data + size; }
     260     2748458 : bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
     261     5803704 : bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
     262     2696848 : size_t SettingsSpan::negated() const
     263             : {
     264     5416568 :     for (size_t i = size; i > 0; --i) {
     265     2782199 :         if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
     266     2719720 :     }
     267     2634369 :     return 0;
     268     2696848 : }
     269             : 
     270             : } // namespace util

Generated by: LCOV version 1.16