LCOV - code coverage report
Current view: top level - src/util - system.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 496 790 62.8 %
Date: 2026-06-25 07:23:51 Functions: 81 102 79.4 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <util/system.h>
       8             : 
       9             : #include <support/allocators/secure.h>
      10             : 
      11             : #include <chainparamsbase.h>
      12             : #include <ctpl_stl.h>
      13             : #include <fs.h>
      14             : #include <stacktraces.h>
      15             : #include <sync.h>
      16             : #include <util/check.h>
      17             : #include <util/getuniquepath.h>
      18             : #include <util/strencodings.h>
      19             : #include <util/string.h>
      20             : #include <util/syserror.h>
      21             : #include <util/threadnames.h>
      22             : #include <util/translation.h>
      23             : 
      24             : #include <tinyformat.h>
      25             : 
      26             : #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
      27             : #include <pthread.h>
      28             : #include <pthread_np.h>
      29             : #endif
      30             : 
      31             : #ifndef WIN32
      32             : // for posix_fallocate, in configure.ac we check if it is present after this
      33             : #ifdef __linux__
      34             : 
      35             : #ifdef _POSIX_C_SOURCE
      36             : #undef _POSIX_C_SOURCE
      37             : #endif
      38             : 
      39             : #define _POSIX_C_SOURCE 200112L
      40             : 
      41             : #endif // __linux__
      42             : 
      43             : #include <algorithm>
      44             : #include <atomic>
      45             : #include <cassert>
      46             : #include <fcntl.h>
      47             : #include <sched.h>
      48             : #include <sys/resource.h>
      49             : #include <sys/stat.h>
      50             : 
      51             : #else
      52             : 
      53             : #include <codecvt>
      54             : 
      55             : #include <io.h> /* for _commit */
      56             : #include <shellapi.h>
      57             : #include <shlobj.h>
      58             : #endif
      59             : 
      60             : #ifdef HAVE_MALLOPT_ARENA_MAX
      61             : #include <malloc.h>
      62             : #endif
      63             : 
      64             : #include <univalue.h>
      65             : 
      66             : #include <fstream>
      67             : #include <map>
      68             : #include <memory>
      69             : #include <optional>
      70             : #include <string>
      71             : #include <system_error>
      72             : #include <thread>
      73             : #include <typeinfo>
      74             : 
      75             : // Application startup time (used for uptime calculation)
      76         223 : const int64_t nStartupTime = GetTime();
      77             : 
      78             : //Dash only features
      79             : const std::string gCoinJoinName = "CoinJoin";
      80             : 
      81             : /**
      82             :     nWalletBackups:
      83             :         1..10   - number of automatic backups to keep
      84             :         0       - disabled by command-line
      85             :         -1      - disabled because of some error during run-time
      86             :         -2      - disabled because wallet was locked and we were not able to replenish keypool
      87             : */
      88             : int nWalletBackups = 10;
      89             : 
      90             : const char * const BITCOIN_CONF_FILENAME = "dash.conf";
      91             : const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
      92             : 
      93         223 : ArgsManager gArgs;
      94             : 
      95             : /** Mutex to protect dir_locks. */
      96             : static GlobalMutex cs_dir_locks;
      97             : 
      98             : /** A map that contains all the currently held directory locks. After
      99             :  * successful locking, these will be held here until the global destructor
     100             :  * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
     101             :  * is called.
     102             :  */
     103         223 : static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
     104             : 
     105          20 : bool LockDirectory(const fs::path& directory, const fs::path& lockfile_name, bool probe_only)
     106             : {
     107          20 :     LOCK(cs_dir_locks);
     108          20 :     fs::path pathLockFile = directory / lockfile_name;
     109             : 
     110             :     // If a lock for this directory already exists in the map, don't try to re-lock it
     111          20 :     if (dir_locks.count(fs::PathToString(pathLockFile))) {
     112           2 :         return true;
     113             :     }
     114             : 
     115             :     // Create empty lock file if it doesn't exist.
     116          18 :     FILE* file = fsbridge::fopen(pathLockFile, "a");
     117          18 :     if (file) fclose(file);
     118          18 :     auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
     119          18 :     if (!lock->TryLock()) {
     120           3 :         return error("Error while attempting to lock directory %s: %s", fs::PathToString(directory), lock->GetReason());
     121             :     }
     122          15 :     if (!probe_only) {
     123             :         // Lock successful and we're not just probing, put it into the map
     124          11 :         dir_locks.emplace(fs::PathToString(pathLockFile), std::move(lock));
     125          11 :     }
     126          15 :     return true;
     127          20 : }
     128             : 
     129           8 : void UnlockDirectory(const fs::path& directory, const fs::path& lockfile_name)
     130             : {
     131           8 :     LOCK(cs_dir_locks);
     132           8 :     dir_locks.erase(fs::PathToString(directory / lockfile_name));
     133           8 : }
     134             : 
     135           3 : void ReleaseDirectoryLocks()
     136             : {
     137           3 :     LOCK(cs_dir_locks);
     138           3 :     dir_locks.clear();
     139           3 : }
     140             : 
     141           3 : bool DirIsWritable(const fs::path& directory)
     142             : {
     143           3 :     fs::path tmpFile = GetUniquePath(directory);
     144             : 
     145           3 :     FILE* file = fsbridge::fopen(tmpFile, "a");
     146           3 :     if (!file) return false;
     147             : 
     148           2 :     fclose(file);
     149           2 :     remove(tmpFile);
     150             : 
     151           2 :     return true;
     152           3 : }
     153             : 
     154         307 : bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
     155             : {
     156         307 :     constexpr uint64_t min_disk_space = 52428800; // 50 MiB
     157             : 
     158         307 :     uint64_t free_bytes_available = fs::space(dir).available;
     159         307 :     return free_bytes_available >= min_disk_space + additional_bytes;
     160             : }
     161             : 
     162           0 : std::streampos GetFileSize(const char* path, std::streamsize max) {
     163           0 :     std::ifstream file{path, std::ios::binary};
     164           0 :     file.ignore(max);
     165           0 :     return file.gcount();
     166           0 : }
     167             : 
     168             : /**
     169             :  * Interpret a string argument as a boolean.
     170             :  *
     171             :  * The definition of LocaleIndependentAtoi<int>() requires that non-numeric string values
     172             :  * like "foo", return 0. This means that if a user unintentionally supplies a
     173             :  * non-integer argument here, the return value is always false. This means that
     174             :  * -foo=false does what the user probably expects, but -foo=true is well defined
     175             :  * but does not do what they probably expected.
     176             :  *
     177             :  * The return value of LocaleIndependentAtoi<int>(...) is zero when given input not
     178             :  * representable as an int.
     179             :  *
     180             :  * For a more extensive discussion of this topic (and a wide range of opinions
     181             :  * on the Right Way to change this code), see PR12713.
     182             :  */
     183       63563 : static bool InterpretBool(const std::string& strValue)
     184             : {
     185       63563 :     if (strValue.empty())
     186        1990 :         return true;
     187       61573 :     return (LocaleIndependentAtoi<int>(strValue) != 0);
     188       63563 : }
     189             : 
     190      718367 : static std::string SettingName(const std::string& arg)
     191             : {
     192      718367 :     return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
     193             : }
     194             : 
     195      181735 : struct KeyInfo {
     196             :     std::string name;
     197             :     std::string section;
     198      181735 :     bool negated{false};
     199             : };
     200             : 
     201             : /**
     202             :  * Parse "name", "section.name", "noname", "section.noname" settings keys.
     203             :  *
     204             :  * @note Where an option was negated can be later checked using the
     205             :  * IsArgNegated() method. One use case for this is to have a way to disable
     206             :  * options that are not normally boolean (e.g. using -nodebuglogfile to request
     207             :  * that debug log output is not sent to any file at all).
     208             :  */
     209      181735 : KeyInfo InterpretKey(std::string key)
     210             : {
     211      181735 :     KeyInfo result;
     212             :     // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
     213      181735 :     size_t option_index = key.find('.');
     214      181735 :     if (option_index != std::string::npos) {
     215       51425 :         result.section = key.substr(0, option_index);
     216       51425 :         key.erase(0, option_index + 1);
     217       51425 :     }
     218      181735 :     if (key.substr(0, 2) == "no") {
     219       58829 :         key.erase(0, 2);
     220       58829 :         result.negated = true;
     221       58829 :     }
     222      181735 :     result.name = key;
     223      181735 :     return result;
     224      181735 : }
     225             : 
     226             : /**
     227             :  * Interpret settings value based on registered flags.
     228             :  *
     229             :  * @param[in]   key      key information to know if key was negated
     230             :  * @param[in]   value    string value of setting to be parsed
     231             :  * @param[in]   flags    ArgsManager registered argument flags
     232             :  * @param[out]  error    Error description if settings value is not valid
     233             :  *
     234             :  * @return parsed settings value if it is valid, otherwise nullopt accompanied
     235             :  * by a descriptive error string
     236             :  */
     237      181732 : static std::optional<util::SettingsValue> InterpretValue(const KeyInfo& key, const std::string* value,
     238             :                                                          unsigned int flags, std::string& error)
     239             : {
     240             :     // Return negated settings as false values.
     241      181732 :     if (key.negated) {
     242       58829 :         if (flags & ArgsManager::DISALLOW_NEGATION) {
     243           0 :             error = strprintf("Negating of -%s is meaningless and therefore forbidden", key.name);
     244           0 :             return std::nullopt;
     245             :         }
     246             :         // Double negatives like -nofoo=0 are supported (but discouraged)
     247       58829 :         if (value && !InterpretBool(*value)) {
     248           7 :             LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
     249           7 :             return true;
     250             :         }
     251       58822 :         return false;
     252             :     }
     253      122903 :     if (!value && (flags & ArgsManager::DISALLOW_ELISION)) {
     254           0 :         error = strprintf("Can not set -%s with no value. Please specify value with -%s=value.", key.name, key.name);
     255           0 :         return std::nullopt;
     256             :     }
     257      122903 :     return value ? *value : "";
     258      181732 : }
     259             : 
     260             : // Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to
     261             : // #include class definitions for all members.
     262             : // For example, m_settings has an internal dependency on univalue.
     263       29867 : ArgsManager::ArgsManager() = default;
     264       29419 : ArgsManager::~ArgsManager() = default;
     265             : 
     266       27339 : std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
     267             : {
     268       27339 :     std::set<std::string> unsuitables;
     269             : 
     270       27339 :     LOCK(cs_args);
     271             : 
     272             :     // if there's no section selected, don't worry
     273       27339 :     if (m_network.empty()) return std::set<std::string> {};
     274             : 
     275             :     // if it's okay to use the default section for this network, don't worry
     276       27339 :     if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
     277             : 
     278       27621 :     for (const auto& arg : m_network_only_args) {
     279        9712 :         if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
     280         846 :             unsuitables.insert(arg);
     281         846 :         }
     282             :     }
     283       17909 :     return unsuitables;
     284       27339 : }
     285             : 
     286         627 : std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
     287             : {
     288             :     // Section names to be recognized in the config file.
     289         627 :     static const std::set<std::string> available_sections{
     290         116 :         CBaseChainParams::REGTEST,
     291         116 :         CBaseChainParams::DEVNET,
     292         116 :         CBaseChainParams::TESTNET,
     293         116 :         CBaseChainParams::MAIN,
     294             :     };
     295             : 
     296         627 :     LOCK(cs_args);
     297         627 :     std::list<SectionInfo> unrecognized = m_config_sections;
     298         627 :     unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
     299         627 :     return unrecognized;
     300         627 : }
     301             : 
     302           0 : std::map<std::string, std::vector<util::SettingsValue>> ArgsManager::GetCommandLineArgs() const {
     303           0 :     LOCK(cs_args);
     304           0 :     return m_settings.command_line_options;
     305           0 : }
     306             : 
     307         973 : void ArgsManager::SelectConfigNetwork(const std::string& network)
     308             : {
     309         973 :     LOCK(cs_args);
     310         973 :     m_network = network;
     311         973 : }
     312             : 
     313       28914 : bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
     314             : {
     315       28914 :     LOCK(cs_args);
     316       28914 :     m_settings.command_line_options.clear();
     317             : 
     318      105336 :     for (int i = 1; i < argc; i++) {
     319       77128 :         std::string key(argv[i]);
     320       77128 :         if (key == "-") break; //dash-tx using stdin
     321             : 
     322             : #ifdef MAC_OSX
     323             :         // At the first time when a user gets the "App downloaded from the
     324             :         // internet" warning, and clicks the Open button, macOS passes
     325             :         // a unique process serial number (PSN) as -psn_... command-line
     326             :         // argument, which we filter out.
     327       77116 :         if (key.substr(0, 5) == "-psn_") continue;
     328             : #endif
     329             : 
     330       77116 :         std::optional<std::string> val;
     331       77116 :         size_t is_index = key.find('=');
     332       77116 :         if (is_index != std::string::npos) {
     333       73845 :             val = key.substr(is_index + 1);
     334       73845 :             key.erase(is_index);
     335       73845 :         }
     336             : #ifdef WIN32
     337             :         key = ToLower(key);
     338             :         if (key[0] == '/')
     339             :             key[0] = '-';
     340             : #endif
     341             : 
     342       77116 :         if (key[0] != '-') {
     343         692 :             if (!m_accept_any_command && m_command.empty()) {
     344             :                 // The first non-dash arg is a registered command
     345           5 :                 std::optional<unsigned int> flags = GetArgFlags(key);
     346           5 :                 if (!flags || !(*flags & ArgsManager::COMMAND)) {
     347           2 :                     error = strprintf("Invalid command '%s'", argv[i]);
     348           2 :                     return false;
     349             :                 }
     350           3 :             }
     351         690 :             m_command.push_back(key);
     352         760 :             while (++i < argc) {
     353             :                 // The remaining args are command args
     354          70 :                 m_command.push_back(argv[i]);
     355             :             }
     356         690 :             break;
     357             :         }
     358             : 
     359             :         // Transform --foo to -foo
     360       76424 :         if (key.length() > 1 && key[1] == '-')
     361           6 :             key.erase(0, 1);
     362             : 
     363             :         // Transform -foo to foo
     364       76424 :         key.erase(0, 1);
     365       76424 :         KeyInfo keyinfo = InterpretKey(key);
     366       76424 :         std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
     367             : 
     368             :         // Unknown command line options and command line options with dot
     369             :         // characters (which are returned from InterpretKey with nonempty
     370             :         // section strings) are not valid.
     371       76424 :         if (!flags || !keyinfo.section.empty()) {
     372           2 :             error = strprintf("Invalid parameter %s", argv[i]);
     373           2 :             return false;
     374             :         }
     375             : 
     376       76422 :         std::optional<util::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
     377       76422 :         if (!value) return false;
     378             : 
     379       76422 :         m_settings.command_line_options[keyinfo.name].push_back(*value);
     380       77128 :     }
     381             : 
     382             :     // we do not allow -includeconf from command line, only -noincludeconf
     383       28910 :     if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
     384           3 :         const util::SettingsSpan values{*includes};
     385             :         // Range may be empty if -noincludeconf was passed
     386           3 :         if (!values.empty()) {
     387           2 :             error = "-includeconf cannot be used from commandline; -includeconf=" + values.begin()->write();
     388           2 :             return false; // pick first value as example
     389             :         }
     390           1 :     }
     391       28908 :     return true;
     392       28914 : }
     393             : 
     394      181744 : std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
     395             : {
     396      181744 :     LOCK(cs_args);
     397      232488 :     for (const auto& arg_map : m_available_args) {
     398      232484 :         const auto search = arg_map.second.find(name);
     399      232484 :         if (search != arg_map.second.end()) {
     400      181740 :             return search->second.m_flags;
     401             :         }
     402             :     }
     403           4 :     return std::nullopt;
     404      181744 : }
     405             : 
     406        2153 : fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
     407             : {
     408        2153 :     if (IsArgNegated(arg)) return fs::path{};
     409        2152 :     std::string path_str = GetArg(arg, "");
     410        2152 :     if (path_str.empty()) return default_value;
     411        1516 :     fs::path result = fs::PathFromString(path_str).lexically_normal();
     412             :     // Remove trailing slash, if present.
     413        1516 :     return result.has_filename() ? result : result.parent_path();
     414        2153 : }
     415             : 
     416      145046 : fs::path ArgsManager::GetBlocksDirPath() const
     417             : {
     418      145046 :     LOCK(cs_args);
     419      145046 :     fs::path& path = m_cached_blocks_path;
     420             : 
     421             :     // Cache the path to avoid calling fs::create_directories on every call of
     422             :     // this function
     423      145046 :     if (!path.empty()) return path;
     424             : 
     425         627 :     if (IsArgSet("-blocksdir")) {
     426           0 :         path = fs::absolute(GetPathArg("-blocksdir"));
     427           0 :         if (!fs::is_directory(path)) {
     428           0 :             path = "";
     429           0 :             return path;
     430             :         }
     431           0 :     } else {
     432         627 :         path = GetDataDirBase();
     433             :     }
     434             : 
     435         627 :     path /= fs::PathFromString(BaseParams().DataDir());
     436         627 :     path /= "blocks";
     437         627 :     fs::create_directories(path);
     438         627 :     return path;
     439      145046 : }
     440             : 
     441        5233 : fs::path ArgsManager::GetDataDir(bool net_specific) const
     442             : {
     443        5233 :     LOCK(cs_args);
     444        5233 :     fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;
     445             : 
     446             :     // Used cached path if available
     447        5233 :     if (!path.empty()) return path;
     448             : 
     449        1484 :     const fs::path datadir{GetPathArg("-datadir")};
     450        1484 :     if (!datadir.empty()) {
     451        1484 :         path = fs::absolute(datadir);
     452        1484 :         if (!fs::is_directory(path)) {
     453           0 :             path = "";
     454           0 :             return path;
     455             :         }
     456        1484 :     } else {
     457           0 :         path = GetDefaultDataDir();
     458             :     }
     459             : 
     460        1484 :     if (net_specific && !BaseParams().DataDir().empty()) {
     461         102 :         path /= fs::PathFromString(BaseParams().DataDir());
     462         102 :     }
     463             : 
     464        1484 :     return path;
     465        5233 : }
     466             : 
     467           5 : fs::path ArgsManager::GetBackupsDirPath()
     468             : {
     469           5 :     if (!IsArgSet("-walletbackupsdir"))
     470           5 :         return GetDataDirNet() / "backups";
     471             : 
     472           0 :     return fs::absolute(GetPathArg("-walletbackupsdir"));
     473           5 : }
     474             : 
     475           0 : void ArgsManager::EnsureDataDir() const
     476             : {
     477             :     /**
     478             :      * "/wallets" subdirectories are created in all **new**
     479             :      * datadirs, because wallet code will create new wallets in the "wallets"
     480             :      * subdirectory only if exists already, otherwise it will create them in
     481             :      * the top-level datadir where they could interfere with other files.
     482             :      * Wallet init code currently avoids creating "wallets" directories itself
     483             :      * for backwards compatibility, but this be changed in the future and
     484             :      * wallet code here could go away (#16220).
     485             :      */
     486           0 :     auto path{GetDataDir(false)};
     487           0 :     if (!fs::exists(path)) {
     488           0 :         fs::create_directories(path / "wallets");
     489           0 :     }
     490           0 :     path = GetDataDir(true);
     491           0 :     if (!fs::exists(path)) {
     492           0 :         fs::create_directories(path / "wallets");
     493           0 :     }
     494           0 : }
     495             : 
     496         631 : void ArgsManager::ClearPathCache()
     497             : {
     498         631 :     LOCK(cs_args);
     499             : 
     500         631 :     m_cached_datadir_path = fs::path();
     501         631 :     m_cached_network_datadir_path = fs::path();
     502         631 :     m_cached_blocks_path = fs::path();
     503         631 : }
     504             : 
     505           3 : std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
     506             : {
     507           3 :     Command ret;
     508           3 :     LOCK(cs_args);
     509           3 :     auto it = m_command.begin();
     510           3 :     if (it == m_command.end()) {
     511             :         // No command was passed
     512           0 :         return std::nullopt;
     513             :     }
     514           3 :     if (!m_accept_any_command) {
     515             :         // The registered command
     516           3 :         ret.command = *(it++);
     517           3 :     }
     518           6 :     while (it != m_command.end()) {
     519             :         // The unregistered command and args (if any)
     520           3 :         ret.args.push_back(*(it++));
     521             :     }
     522           3 :     return ret;
     523           3 : }
     524             : 
     525       31419 : std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
     526             : {
     527       31419 :     std::vector<std::string> result;
     528       78697 :     for (const util::SettingsValue& value : GetSettingsList(strArg)) {
     529       47278 :         result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
     530             :     }
     531       31419 :     return result;
     532       31419 : }
     533             : 
     534      124589 : bool ArgsManager::IsArgSet(const std::string& strArg) const
     535             : {
     536      124589 :     return !GetSetting(strArg).isNull();
     537           0 : }
     538             : 
     539           0 : bool ArgsManager::InitSettings(std::string& error)
     540             : {
     541           0 :     EnsureDataDir();
     542           0 :     if (!GetSettingsPath()) {
     543           0 :         return true; // Do nothing if settings file disabled.
     544             :     }
     545             : 
     546           0 :     std::vector<std::string> errors;
     547           0 :     if (!ReadSettingsFile(&errors)) {
     548           0 :         error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
     549           0 :         return false;
     550             :     }
     551           0 :     if (!WriteSettingsFile(&errors)) {
     552           0 :         error = strprintf("Failed saving settings file:\n%s\n", MakeUnorderedList(errors));
     553           0 :         return false;
     554             :     }
     555           0 :     return true;
     556           0 : }
     557             : 
     558           5 : bool ArgsManager::GetSettingsPath(fs::path* filepath, bool temp, bool backup) const
     559             : {
     560           5 :     fs::path settings = GetPathArg("-settings", BITCOIN_SETTINGS_FILENAME);
     561           5 :     if (settings.empty()) {
     562           0 :         return false;
     563             :     }
     564           5 :     if (backup) {
     565           0 :         settings += ".bak";
     566           0 :     }
     567           5 :     if (filepath) {
     568           5 :         *filepath = fsbridge::AbsPathJoin(GetDataDirNet(), temp ? settings + ".tmp" : settings);
     569           5 :     }
     570           5 :     return true;
     571           5 : }
     572             : 
     573           1 : static void SaveErrors(const std::vector<std::string> errors, std::vector<std::string>* error_out)
     574             : {
     575           2 :     for (const auto& error : errors) {
     576           1 :         if (error_out) {
     577           0 :             error_out->emplace_back(error);
     578           0 :         } else {
     579           1 :             LogPrintf("%s\n", error);
     580             :         }
     581             :     }
     582           1 : }
     583             : 
     584           1 : bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
     585             : {
     586           1 :     fs::path path;
     587           1 :     if (!GetSettingsPath(&path, /* temp= */ false)) {
     588           0 :         return true; // Do nothing if settings file disabled.
     589             :     }
     590             : 
     591           1 :     LOCK(cs_args);
     592           1 :     m_settings.rw_settings.clear();
     593           1 :     std::vector<std::string> read_errors;
     594           1 :     if (!util::ReadSettings(path, m_settings.rw_settings, read_errors)) {
     595           0 :         SaveErrors(read_errors, errors);
     596           0 :         return false;
     597             :     }
     598           2 :     for (const auto& setting : m_settings.rw_settings) {
     599           1 :         KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
     600           1 :         if (!GetArgFlags('-' + key.name)) {
     601           1 :             LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
     602           1 :         }
     603           1 :     }
     604           1 :     return true;
     605           1 : }
     606             : 
     607           2 : bool ArgsManager::WriteSettingsFile(std::vector<std::string>* errors, bool backup) const
     608             : {
     609           2 :     fs::path path, path_tmp;
     610           2 :     if (!GetSettingsPath(&path, /*temp=*/false, backup) || !GetSettingsPath(&path_tmp, /*temp=*/true, backup)) {
     611           0 :         throw std::logic_error("Attempt to write settings file when dynamic settings are disabled.");
     612             :     }
     613             : 
     614           2 :     LOCK(cs_args);
     615           2 :     std::vector<std::string> write_errors;
     616           2 :     if (!util::WriteSettings(path_tmp, m_settings.rw_settings, write_errors)) {
     617           0 :         SaveErrors(write_errors, errors);
     618           0 :         return false;
     619             :     }
     620           2 :     if (!RenameOver(path_tmp, path)) {
     621           1 :         SaveErrors({strprintf("Failed renaming settings file %s to %s\n", fs::PathToString(path_tmp), fs::PathToString(path))}, errors);
     622           1 :         return false;
     623             :     }
     624           1 :     return true;
     625           2 : }
     626             : 
     627           0 : util::SettingsValue ArgsManager::GetPersistentSetting(const std::string& name) const
     628             : {
     629           0 :     LOCK(cs_args);
     630           0 :     return util::GetSetting(m_settings, m_network, name, !UseDefaultSection("-" + name),
     631             :         /*ignore_nonpersistent=*/true, /*get_chain_name=*/false);
     632           0 : }
     633             : 
     634       29509 : bool ArgsManager::IsArgNegated(const std::string& strArg) const
     635             : {
     636       29509 :     return GetSetting(strArg).isFalse();
     637           0 : }
     638             : 
     639       34720 : std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
     640             : {
     641       34720 :     const util::SettingsValue value = GetSetting(strArg);
     642       34720 :     return SettingToString(value, strDefault);
     643       34720 : }
     644             : 
     645       34720 : std::string SettingToString(const util::SettingsValue& value, const std::string& strDefault)
     646             : {
     647       34720 :     return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.isNum() ? value.getValStr() : value.get_str();
     648             : }
     649             : 
     650      160579 : int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
     651             : {
     652      160579 :     const util::SettingsValue value = GetSetting(strArg);
     653      160579 :     return SettingToInt(value, nDefault);
     654      160579 : }
     655             : 
     656      160579 : int64_t SettingToInt(const util::SettingsValue& value, int64_t nDefault)
     657             : {
     658      160579 :     return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.getInt<int64_t>() : LocaleIndependentAtoi<int64_t>(value.get_str());
     659             : }
     660             : 
     661      293075 : bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
     662             : {
     663      293075 :     const util::SettingsValue value = GetSetting(strArg);
     664      293075 :     return SettingToBool(value, fDefault);
     665      293075 : }
     666             : 
     667      293075 : bool SettingToBool(const util::SettingsValue& value, bool fDefault)
     668             : {
     669      293075 :     return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
     670             : }
     671             : 
     672       26715 : bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
     673             : {
     674       26715 :     LOCK(cs_args);
     675       26715 :     if (IsArgSet(strArg)) return false;
     676         379 :     ForceSetArg(strArg, strValue);
     677         379 :     return true;
     678       26715 : }
     679             : 
     680           1 : bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
     681             : {
     682           1 :     if (fValue)
     683           1 :         return SoftSetArg(strArg, std::string("1"));
     684             :     else
     685           0 :         return SoftSetArg(strArg, std::string("0"));
     686           1 : }
     687             : 
     688       28463 : void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
     689             : {
     690       28463 :     LOCK(cs_args);
     691       28463 :     m_settings.forced_settings[SettingName(strArg)] = strValue;
     692       28463 : }
     693             : 
     694           5 : void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
     695             : {
     696           5 :     Assert(cmd.find('=') == std::string::npos);
     697           5 :     Assert(cmd.at(0) != '-');
     698             : 
     699           5 :     LOCK(cs_args);
     700           5 :     m_accept_any_command = false; // latch to false
     701           5 :     std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
     702           5 :     auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
     703           5 :     Assert(ret.second); // Fail on duplicate commands
     704           5 : }
     705             : 
     706      203106 : void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
     707             : {
     708      203106 :     Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
     709             : 
     710             :     // Split arg name from its help param
     711      203106 :     size_t eq_index = name.find('=');
     712      203106 :     if (eq_index == std::string::npos) {
     713       87275 :         eq_index = name.size();
     714       87275 :     }
     715      203106 :     std::string arg_name = name.substr(0, eq_index);
     716             : 
     717      203106 :     LOCK(cs_args);
     718      203106 :     std::map<std::string, Arg>& arg_map = m_available_args[cat];
     719      203106 :     auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
     720      203106 :     assert(ret.second); // Make sure an insertion actually happened
     721             : 
     722      203106 :     if (flags & ArgsManager::NETWORK_ONLY) {
     723        5016 :         m_network_only_args.emplace(arg_name);
     724        5016 :     }
     725      203106 : }
     726             : 
     727        1331 : void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
     728             : {
     729       34089 :     for (const std::string& name : names) {
     730       32758 :         AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
     731             :     }
     732        1331 : }
     733             : 
     734           0 : std::string ArgsManager::GetHelpMessage() const
     735             : {
     736           0 :     const bool show_debug = GetBoolArg("-help-debug", false);
     737             : 
     738           0 :     std::string usage;
     739           0 :     LOCK(cs_args);
     740           0 :     for (const auto& arg_map : m_available_args) {
     741           0 :         switch(arg_map.first) {
     742             :             case OptionsCategory::OPTIONS:
     743           0 :                 usage += HelpMessageGroup("Options:");
     744           0 :                 break;
     745             :             case OptionsCategory::CONNECTION:
     746           0 :                 usage += HelpMessageGroup("Connection options:");
     747           0 :                 break;
     748             :             case OptionsCategory::INDEXING:
     749           0 :                 usage += HelpMessageGroup("Indexing options:");
     750           0 :                 break;
     751             :             case OptionsCategory::MASTERNODE:
     752           0 :                 usage += HelpMessageGroup("Masternode options:");
     753           0 :                 break;
     754             :             case OptionsCategory::STATSD:
     755           0 :                 usage += HelpMessageGroup("Statsd options:");
     756           0 :                 break;
     757             :             case OptionsCategory::ZMQ:
     758           0 :                 usage += HelpMessageGroup("ZeroMQ notification options:");
     759           0 :                 break;
     760             :             case OptionsCategory::DEBUG_TEST:
     761           0 :                 usage += HelpMessageGroup("Debugging/Testing options:");
     762           0 :                 break;
     763             :             case OptionsCategory::NODE_RELAY:
     764           0 :                 usage += HelpMessageGroup("Node relay options:");
     765           0 :                 break;
     766             :             case OptionsCategory::BLOCK_CREATION:
     767           0 :                 usage += HelpMessageGroup("Block creation options:");
     768           0 :                 break;
     769             :             case OptionsCategory::RPC:
     770           0 :                 usage += HelpMessageGroup("RPC server options:");
     771           0 :                 break;
     772             :             case OptionsCategory::WALLET:
     773           0 :                 usage += HelpMessageGroup("Wallet options:");
     774           0 :                 break;
     775             :             case OptionsCategory::WALLET_FEE:
     776           0 :                 usage += HelpMessageGroup("Wallet fee options:");
     777           0 :                 break;
     778             :             case OptionsCategory::WALLET_HD:
     779           0 :                 usage += HelpMessageGroup("HD wallet options:");
     780           0 :                 break;
     781             :             case OptionsCategory::WALLET_COINJOIN:
     782           0 :                 usage += HelpMessageGroup("CoinJoin options:");
     783           0 :                 break;
     784             :             case OptionsCategory::WALLET_DEBUG_TEST:
     785           0 :                 if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
     786           0 :                 break;
     787             :             case OptionsCategory::CHAINPARAMS:
     788           0 :                 usage += HelpMessageGroup("Chain selection options:");
     789           0 :                 break;
     790             :             case OptionsCategory::GUI:
     791           0 :                 usage += HelpMessageGroup("UI Options:");
     792           0 :                 break;
     793             :             case OptionsCategory::COMMANDS:
     794           0 :                 usage += HelpMessageGroup("Commands:");
     795           0 :                 break;
     796             :             case OptionsCategory::REGISTER_COMMANDS:
     797           0 :                 usage += HelpMessageGroup("Register Commands:");
     798           0 :                 break;
     799             :             default:
     800           0 :                 break;
     801             :         }
     802             : 
     803             :         // When we get to the hidden options, stop
     804           0 :         if (arg_map.first == OptionsCategory::HIDDEN) break;
     805             : 
     806           0 :         for (const auto& arg : arg_map.second) {
     807           0 :             if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
     808           0 :                 std::string name;
     809           0 :                 if (arg.second.m_help_param.empty()) {
     810           0 :                     name = arg.first;
     811           0 :                 } else {
     812           0 :                     name = arg.first + arg.second.m_help_param;
     813             :                 }
     814           0 :                 usage += HelpMessageOpt(name, arg.second.m_help_text);
     815           0 :             }
     816             :         }
     817             :     }
     818           0 :     return usage;
     819           0 : }
     820             : 
     821         106 : void ArgsManager::ForceRemoveArg(const std::string& strArg)
     822             : {
     823         106 :     LOCK(cs_args);
     824         106 :     const auto& ov = m_settings.forced_settings.find(strArg);
     825         106 :     if (ov != m_settings.forced_settings.end()) {
     826          91 :         m_settings.forced_settings.erase(ov);
     827          91 :     }
     828             : 
     829         530 :     for (const auto& network : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST, CBaseChainParams::DEVNET }) {
     830         424 :         if (auto* section = util::FindKey(m_settings.ro_config, network)) {
     831           0 :             if (util::FindKey(*section, strArg)) {
     832           0 :                 section->erase(strArg);
     833           0 :             }
     834           0 :         }
     835             :     }
     836         106 : }
     837             : 
     838          75 : bool HelpRequested(const ArgsManager& args)
     839             : {
     840          75 :     return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
     841           0 : }
     842             : 
     843         704 : void SetupHelpOptions(ArgsManager& args)
     844             : {
     845         704 :     args.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS);
     846         704 :     args.AddHiddenArgs({"-h", "-help"});
     847         704 : }
     848             : 
     849             : static const int screenWidth = 79;
     850             : static const int optIndent = 2;
     851             : static const int msgIndent = 7;
     852             : 
     853           0 : std::string HelpMessageGroup(const std::string &message) {
     854           0 :     return std::string(message) + std::string("\n\n");
     855           0 : }
     856             : 
     857           0 : std::string HelpMessageOpt(const std::string &option, const std::string &message) {
     858           0 :     return std::string(optIndent,' ') + std::string(option) +
     859           0 :            std::string("\n") + std::string(msgIndent,' ') +
     860           0 :            FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
     861           0 :            std::string("\n\n");
     862           0 : }
     863             : 
     864           0 : void PrintExceptionContinue(const std::exception_ptr pex, const char* pszExceptionOrigin)
     865             : {
     866           0 :     std::string message = strprintf("\"%s\" raised an exception\n%s", pszExceptionOrigin, GetPrettyExceptionStr(pex));
     867           0 :     LogPrintf("\n\n************************\n%s\n", message);
     868           0 :     tfm::format(std::cerr, "\n\n************************\n%s\n", message);
     869           0 : }
     870             : 
     871           0 : fs::path GetDefaultDataDir()
     872             : {
     873             :     // Windows: C:\Users\Username\AppData\Roaming\DashCore
     874             :     // macOS: ~/Library/Application Support/DashCore
     875             :     // Unix-like: ~/.dashcore
     876             : #ifdef WIN32
     877             :     // Windows
     878             :     return GetSpecialFolderPath(CSIDL_APPDATA) / "DashCore";
     879             : #else
     880           0 :     fs::path pathRet;
     881           0 :     char* pszHome = getenv("HOME");
     882           0 :     if (pszHome == nullptr || strlen(pszHome) == 0)
     883           0 :         pathRet = fs::path("/");
     884             :     else
     885           0 :         pathRet = fs::path(pszHome);
     886             : #ifdef MAC_OSX
     887             :     // macOS
     888           0 :     return pathRet / "Library/Application Support/DashCore";
     889             : #else
     890             :     // Unix-like
     891             :     return pathRet / ".dashcore";
     892             : #endif
     893             : #endif
     894           0 : }
     895             : 
     896           0 : fs::path GetBackupsDir()
     897             : {
     898           0 :     return gArgs.GetBackupsDirPath();
     899             : }
     900             : 
     901           0 : bool CheckDataDirOption()
     902             : {
     903           0 :     const fs::path datadir{gArgs.GetPathArg("-datadir")};
     904           0 :     return datadir.empty() || fs::is_directory(fs::absolute(datadir));
     905           0 : }
     906             : 
     907           0 : fs::path GetConfigFile(const fs::path& configuration_file_path)
     908             : {
     909           0 :     return AbsPathForConfigVal(configuration_file_path, /*net_specific=*/false);
     910             : }
     911             : 
     912       28094 : static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
     913             : {
     914       28094 :     std::string str, prefix;
     915             :     std::string::size_type pos;
     916       28094 :     int linenr = 1;
     917      133417 :     while (std::getline(stream, str)) {
     918      105323 :         bool used_hash = false;
     919      105323 :         if ((pos = str.find('#')) != std::string::npos) {
     920           0 :             str = str.substr(0, pos);
     921           0 :             used_hash = true;
     922           0 :         }
     923             :         const static std::string pattern = " \t\r\n";
     924      105323 :         str = TrimString(str, pattern);
     925      105323 :         if (!str.empty()) {
     926      105322 :             if (*str.begin() == '[' && *str.rbegin() == ']') {
     927          12 :                 const std::string section = str.substr(1, str.size() - 2);
     928          12 :                 sections.emplace_back(SectionInfo{section, filepath, linenr});
     929          12 :                 prefix = section + '.';
     930      105322 :             } else if (*str.begin() == '-') {
     931           0 :                 error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
     932           0 :                 return false;
     933      105310 :             } else if ((pos = str.find('=')) != std::string::npos) {
     934      105310 :                 std::string name = prefix + TrimString(std::string_view{str}.substr(0, pos), pattern);
     935      105310 :                 std::string_view value = TrimStringView(std::string_view{str}.substr(pos + 1), pattern);
     936      105310 :                 if (used_hash && name.find("rpcpassword") != std::string::npos) {
     937           0 :                     error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
     938           0 :                     return false;
     939             :                 }
     940      105310 :                 options.emplace_back(name, value);
     941      105310 :                 if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
     942       51409 :                     sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
     943       51409 :                 }
     944      105310 :             } else {
     945           0 :                 error = strprintf("parse error on line %i: %s", linenr, str);
     946           0 :                 if (str.size() >= 2 && str.substr(0, 2) == "no") {
     947           0 :                     error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
     948           0 :                 }
     949           0 :                 return false;
     950             :             }
     951      105322 :         }
     952      105323 :         ++linenr;
     953             :     }
     954       28094 :     return true;
     955       28094 : }
     956             : 
     957      105310 : bool IsConfSupported(KeyInfo& key, std::string& error) {
     958      105310 :     if (key.name == "conf") {
     959           0 :         error = "conf cannot be set in the configuration file; use includeconf= if you want to include additional config files";
     960           0 :         return false;
     961             :     }
     962      105310 :     if (key.name == "reindex") {
     963             :         // reindex can be set in a config file but it is strongly discouraged as this will cause the node to reindex on
     964             :         // every restart. Allow the config but throw a warning
     965           0 :         LogPrintf("Warning: reindex=1 is set in the configuration file, which will significantly slow down startup. Consider removing or commenting out this option for better performance, unless there is currently a condition which makes rebuilding the indexes necessary\n");
     966           0 :         return true;
     967             :     }
     968      105310 :     return true;
     969      105310 : }
     970             : 
     971       28094 : bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
     972             : {
     973       28094 :     LOCK(cs_args);
     974       28094 :     std::vector<std::pair<std::string, std::string>> options;
     975       28094 :     if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
     976           0 :         return false;
     977             :     }
     978      133404 :     for (const std::pair<std::string, std::string>& option : options) {
     979      105310 :         KeyInfo key = InterpretKey(option.first);
     980      105310 :         std::optional<unsigned int> flags = GetArgFlags('-' + key.name);
     981      105310 :         if (!IsConfSupported(key, error)) return false;
     982      105310 :         if (flags) {
     983      105310 :             std::optional<util::SettingsValue> value = InterpretValue(key, &option.second, *flags, error);
     984      105310 :             if (!value) {
     985           0 :                 return false;
     986             :             }
     987      105310 :             m_settings.ro_config[key.section][key.name].push_back(*value);
     988      105310 :         } else {
     989           0 :             if (ignore_invalid_keys) {
     990           0 :                 LogPrintf("Ignoring unknown configuration value %s\n", option.first);
     991           0 :             } else {
     992           0 :                 error = strprintf("Invalid configuration value %s", option.first);
     993           0 :                 return false;
     994             :             }
     995             :         }
     996      105310 :     }
     997       28094 :     return true;
     998       28094 : }
     999             : 
    1000           0 : fs::path ArgsManager::GetConfigFilePath() const
    1001             : {
    1002           0 :     return GetConfigFile(GetPathArg("-conf", BITCOIN_CONF_FILENAME));
    1003           0 : }
    1004             : 
    1005           0 : bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
    1006             : {
    1007             :     {
    1008           0 :         LOCK(cs_args);
    1009           0 :         m_settings.ro_config.clear();
    1010           0 :         m_config_sections.clear();
    1011           0 :     }
    1012             : 
    1013           0 :     const auto conf_path{GetConfigFilePath()};
    1014           0 :     std::ifstream stream{conf_path};
    1015             : 
    1016             :     // not ok to have a config file specified that cannot be opened
    1017           0 :     if (IsArgSet("-conf") && !stream.good()) {
    1018           0 :         error = strprintf("specified config file \"%s\" could not be opened.", fs::PathToString(conf_path));
    1019           0 :         return false;
    1020             :     }
    1021             :     // ok to not have a config file
    1022           0 :     if (stream.good()) {
    1023           0 :         if (!ReadConfigStream(stream, fs::PathToString(conf_path), error, ignore_invalid_keys)) {
    1024           0 :             return false;
    1025             :         }
    1026             :         // `-includeconf` cannot be included in the command line arguments except
    1027             :         // as `-noincludeconf` (which indicates that no included conf file should be used).
    1028           0 :         bool use_conf_file{true};
    1029             :         {
    1030           0 :             LOCK(cs_args);
    1031           0 :             if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
    1032             :                 // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
    1033           0 :                 assert(util::SettingsSpan(*includes).last_negated());
    1034           0 :                 use_conf_file = false;
    1035           0 :             }
    1036           0 :         }
    1037           0 :         if (use_conf_file) {
    1038           0 :             std::string chain_id = GetChainName();
    1039           0 :             std::vector<std::string> conf_file_names;
    1040             : 
    1041           0 :             auto add_includes = [&](const std::string& network, size_t skip = 0) {
    1042           0 :                 size_t num_values = 0;
    1043           0 :                 LOCK(cs_args);
    1044           0 :                 if (auto* section = util::FindKey(m_settings.ro_config, network)) {
    1045           0 :                     if (auto* values = util::FindKey(*section, "includeconf")) {
    1046           0 :                         for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
    1047           0 :                             conf_file_names.push_back((*values)[i].get_str());
    1048           0 :                         }
    1049           0 :                         num_values = values->size();
    1050           0 :                     }
    1051           0 :                 }
    1052           0 :                 return num_values;
    1053           0 :             };
    1054             : 
    1055             :             // We haven't set m_network yet (that happens in SelectParams()), so manually check
    1056             :             // for network.includeconf args.
    1057           0 :             const size_t chain_includes = add_includes(chain_id);
    1058           0 :             const size_t default_includes = add_includes({});
    1059             : 
    1060           0 :             for (const std::string& conf_file_name : conf_file_names) {
    1061           0 :                 std::ifstream conf_file_stream{GetConfigFile(fs::PathFromString(conf_file_name))};
    1062           0 :                 if (conf_file_stream.good()) {
    1063           0 :                     if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
    1064           0 :                         return false;
    1065             :                     }
    1066           0 :                     LogPrintf("Included configuration file %s\n", conf_file_name);
    1067           0 :                 } else {
    1068           0 :                     error = "Failed to include configuration file " + conf_file_name;
    1069           0 :                     return false;
    1070             :                 }
    1071           0 :             }
    1072             : 
    1073             :             // Warn about recursive -includeconf
    1074           0 :             conf_file_names.clear();
    1075           0 :             add_includes(chain_id, /* skip= */ chain_includes);
    1076           0 :             add_includes({}, /* skip= */ default_includes);
    1077           0 :             std::string chain_id_final = GetChainName();
    1078           0 :             if (chain_id_final != chain_id) {
    1079             :                 // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
    1080           0 :                 add_includes(chain_id_final);
    1081           0 :             }
    1082           0 :             for (const std::string& conf_file_name : conf_file_names) {
    1083           0 :                 tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
    1084             :             }
    1085           0 :         }
    1086           0 :     } else {
    1087             :         // Create an empty dash.conf if it does not exist
    1088           0 :         std::ofstream configFile{GetConfigFile(conf_path), std::ios_base::app};
    1089           0 :         if (!configFile.good())
    1090           0 :             return false;
    1091           0 :         configFile.close();
    1092           0 :         return true; // Nothing to read, so just return
    1093           0 :     }
    1094             : 
    1095             :     // If datadir is changed in .conf file:
    1096           0 :     gArgs.ClearPathCache();
    1097           0 :     if (!CheckDataDirOption()) {
    1098           0 :         error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
    1099           0 :         return false;
    1100             :     }
    1101           0 :     return true;
    1102           0 : }
    1103             : 
    1104        2086 : std::string ArgsManager::GetChainName() const
    1105             : {
    1106        8344 :     auto get_net = [&](const std::string& arg, bool interpret_bool = true) {
    1107        6258 :         LOCK(cs_args);
    1108        6258 :         util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
    1109             :             /* ignore_default_section_config= */ false,
    1110             :             /*ignore_nonpersistent=*/false,
    1111             :             /* get_chain_name= */ true);
    1112        6258 :         return value.isNull() ? false : value.isBool() ? value.get_bool() : (!interpret_bool || InterpretBool(value.get_str()));
    1113        6258 :     };
    1114             : 
    1115        2086 :     const bool fRegTest = get_net("-regtest");
    1116        2086 :     const bool fDevNet = get_net("-devnet", false);
    1117        2086 :     const bool fTestNet = get_net("-testnet");
    1118        2086 :     const bool is_chain_arg_set = IsArgSet("-chain");
    1119             : 
    1120        2086 :     int nameParamsCount = (fRegTest ? 1 : 0) + (fDevNet ? 1 : 0) + (fTestNet ? 1 : 0) + (is_chain_arg_set ? 1 : 0);
    1121        2086 :     if (nameParamsCount > 1)
    1122         187 :         throw std::runtime_error("Only one of -regtest, -testnet, -devnet or -chain can be used.");
    1123             : 
    1124        1899 :     if (fRegTest)
    1125         323 :         return CBaseChainParams::REGTEST;
    1126        1576 :     if (fDevNet) {
    1127           0 :         return CBaseChainParams::DEVNET;
    1128             :     }
    1129        1576 :     if (fTestNet)
    1130         330 :         return CBaseChainParams::TESTNET;
    1131             : 
    1132        1246 :     return GetArg("-chain", CBaseChainParams::MAIN);
    1133        1899 : }
    1134             : 
    1135        1257 : std::string ArgsManager::GetDevNetName() const
    1136             : {
    1137        1257 :     std::string devNetName = GetArg("-devnet", "");
    1138        1257 :     return "devnet" + (devNetName.empty() ? "" : "-" + devNetName);
    1139        1257 : }
    1140             : 
    1141             : 
    1142           1 : void ArgsManager::logArgsPrefix(
    1143             :     const std::string& prefix,
    1144             :     const std::string& section,
    1145             :     const std::map<std::string, std::vector<util::SettingsValue>>& args) const
    1146             : {
    1147           1 :     std::string section_str = section.empty() ? "" : "[" + section + "] ";
    1148           5 :     for (const auto& arg : args) {
    1149           8 :         for (const auto& value : arg.second) {
    1150           4 :             std::optional<unsigned int> flags = GetArgFlags('-' + arg.first);
    1151           4 :             if (flags) {
    1152           4 :                 std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
    1153           4 :                 LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
    1154           4 :             }
    1155             :         }
    1156             :     }
    1157           1 : }
    1158             : 
    1159           1 : void ArgsManager::LogArgs() const
    1160             : {
    1161           1 :     LOCK(cs_args);
    1162           1 :     for (const auto& section : m_settings.ro_config) {
    1163           0 :         logArgsPrefix("Config file arg:", section.first, section.second);
    1164             :     }
    1165           1 :     for (const auto& setting : m_settings.rw_settings) {
    1166           0 :         LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
    1167             :     }
    1168           1 :     logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
    1169           1 : }
    1170             : 
    1171      673934 : bool ArgsManager::UseDefaultSection(const std::string& arg) const
    1172             : {
    1173      673934 :     return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0;
    1174             : }
    1175             : 
    1176      642498 : util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
    1177             : {
    1178      642498 :     LOCK(cs_args);
    1179      642498 :     return util::GetSetting(
    1180      642498 :         m_settings, m_network, SettingName(arg), !UseDefaultSection(arg),
    1181             :         /*ignore_nonpersistent=*/false, /*get_chain_name=*/false);
    1182      642498 : }
    1183             : 
    1184       31436 : std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
    1185             : {
    1186       31436 :     LOCK(cs_args);
    1187       31436 :     return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
    1188       31436 : }
    1189             : 
    1190           3 : bool RenameOver(fs::path src, fs::path dest)
    1191             : {
    1192           3 :     std::error_code error;
    1193           3 :     fs::rename(src, dest, error);
    1194           3 :     return !error;
    1195             : }
    1196             : 
    1197             : /**
    1198             :  * Ignores exceptions thrown by create_directories if the requested directory exists.
    1199             :  * Specifically handles case where path p exists, but it wasn't possible for the user to
    1200             :  * write to the parent directory.
    1201             :  */
    1202          28 : bool TryCreateDirectories(const fs::path& p)
    1203             : {
    1204             :     try
    1205             :     {
    1206          28 :         return fs::create_directories(p);
    1207           0 :     } catch (const fs::filesystem_error&) {
    1208           0 :         if (!fs::exists(p) || !fs::is_directory(p))
    1209           0 :             throw;
    1210           0 :     }
    1211             : 
    1212             :     // create_directories didn't create the directory, it had to have existed already
    1213           0 :     return false;
    1214          28 : }
    1215             : 
    1216          59 : bool FileCommit(FILE *file)
    1217             : {
    1218          59 :     if (fflush(file) != 0) { // harmless if redundantly called
    1219           0 :         LogPrintf("fflush failed: %s\n", SysErrorString(errno));
    1220           0 :         return false;
    1221             :     }
    1222             : #ifdef WIN32
    1223             :     HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
    1224             :     if (FlushFileBuffers(hFile) == 0) {
    1225             :         LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
    1226             :         return false;
    1227             :     }
    1228             : #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
    1229          59 :     if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
    1230           0 :         LogPrintf("fcntl F_FULLFSYNC failed: %s\n", SysErrorString(errno));
    1231           0 :         return false;
    1232             :     }
    1233             : #elif HAVE_FDATASYNC
    1234             :     if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
    1235             :         LogPrintf("fdatasync failed: %d\n", SysErrorString(errno));
    1236             :         return false;
    1237             :     }
    1238             : #else
    1239             :     if (fsync(fileno(file)) != 0 && errno != EINVAL) {
    1240             :         LogPrintf("fsync failed: %d\n", SysErrorString(errno));
    1241             :         return false;
    1242             :     }
    1243             : #endif
    1244          59 :     return true;
    1245          59 : }
    1246             : 
    1247          52 : void DirectoryCommit(const fs::path &dirname)
    1248             : {
    1249             : #ifndef WIN32
    1250          52 :     FILE* file = fsbridge::fopen(dirname, "r");
    1251          52 :     if (file) {
    1252          52 :         fsync(fileno(file));
    1253          52 :         fclose(file);
    1254          52 :     }
    1255             : #endif
    1256          52 : }
    1257             : 
    1258           5 : bool TruncateFile(FILE *file, unsigned int length) {
    1259             : #if defined(WIN32)
    1260             :     return _chsize(_fileno(file), length) == 0;
    1261             : #else
    1262           5 :     return ftruncate(fileno(file), length) == 0;
    1263             : #endif
    1264             : }
    1265             : 
    1266             : /**
    1267             :  * this function tries to raise the file descriptor limit to the requested number.
    1268             :  * It returns the actual file descriptor limit (which may be more or less than nMinFD)
    1269             :  */
    1270         627 : int RaiseFileDescriptorLimit(int nMinFD) {
    1271             : #if defined(WIN32)
    1272             :     return 2048;
    1273             : #else
    1274             :     struct rlimit limitFD;
    1275         627 :     if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
    1276         627 :         if (limitFD.rlim_cur < (rlim_t)nMinFD) {
    1277           0 :             limitFD.rlim_cur = nMinFD;
    1278           0 :             if (limitFD.rlim_cur > limitFD.rlim_max)
    1279           0 :                 limitFD.rlim_cur = limitFD.rlim_max;
    1280           0 :             setrlimit(RLIMIT_NOFILE, &limitFD);
    1281           0 :             getrlimit(RLIMIT_NOFILE, &limitFD);
    1282           0 :         }
    1283         627 :         return limitFD.rlim_cur;
    1284             :     }
    1285           0 :     return nMinFD; // getrlimit failed, assume it's fine
    1286             : #endif
    1287         627 : }
    1288             : 
    1289             : /**
    1290             :  * this function tries to make a particular range of a file allocated (corresponding to disk space)
    1291             :  * it is advisory, and the range specified in the arguments will never contain live data
    1292             :  */
    1293         261 : void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
    1294             : #if defined(WIN32)
    1295             :     // Windows-specific version
    1296             :     HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
    1297             :     LARGE_INTEGER nFileSize;
    1298             :     int64_t nEndPos = (int64_t)offset + length;
    1299             :     nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
    1300             :     nFileSize.u.HighPart = nEndPos >> 32;
    1301             :     SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
    1302             :     SetEndOfFile(hFile);
    1303             : #elif defined(MAC_OSX)
    1304             :     // OSX specific version
    1305             :     // NOTE: Contrary to other OS versions, the OSX version assumes that
    1306             :     // NOTE: offset is the size of the file.
    1307             :     fstore_t fst;
    1308         261 :     fst.fst_flags = F_ALLOCATECONTIG;
    1309         261 :     fst.fst_posmode = F_PEOFPOSMODE;
    1310         261 :     fst.fst_offset = 0;
    1311         261 :     fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
    1312         261 :     fst.fst_bytesalloc = 0;
    1313         261 :     if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
    1314           0 :         fst.fst_flags = F_ALLOCATEALL;
    1315           0 :         fcntl(fileno(file), F_PREALLOCATE, &fst);
    1316           0 :     }
    1317         261 :     ftruncate(fileno(file), static_cast<off_t>(offset) + length);
    1318             : #else
    1319             :     #if defined(HAVE_POSIX_FALLOCATE)
    1320             :     // Version using posix_fallocate
    1321             :     off_t nEndPos = (off_t)offset + length;
    1322             :     if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
    1323             :     #endif
    1324             :     // Fallback version
    1325             :     // TODO: just write one byte per block
    1326             :     static const char buf[65536] = {};
    1327             :     if (fseek(file, offset, SEEK_SET)) {
    1328             :         return;
    1329             :     }
    1330             :     while (length > 0) {
    1331             :         unsigned int now = 65536;
    1332             :         if (length < now)
    1333             :             now = length;
    1334             :         fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
    1335             :         length -= now;
    1336             :     }
    1337             : #endif
    1338         261 : }
    1339             : 
    1340             : #ifdef WIN32
    1341             : fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
    1342             : {
    1343             :     WCHAR pszPath[MAX_PATH] = L"";
    1344             : 
    1345             :     if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
    1346             :     {
    1347             :         return fs::path(pszPath);
    1348             :     }
    1349             : 
    1350             :     LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
    1351             :     return fs::path("");
    1352             : }
    1353             : #endif
    1354             : 
    1355             : #ifndef WIN32
    1356           0 : std::string ShellEscape(const std::string& arg)
    1357             : {
    1358           0 :     std::string escaped = arg;
    1359           0 :     ReplaceAll(escaped, "'", "'\"'\"'");
    1360           0 :     return "'" + escaped + "'";
    1361           0 : }
    1362             : #endif
    1363             : 
    1364             : #if HAVE_SYSTEM
    1365           0 : void runCommand(const std::string& strCommand)
    1366             : {
    1367           0 :     if (strCommand.empty()) return;
    1368             : #ifndef WIN32
    1369           0 :     int nErr = ::system(strCommand.c_str());
    1370             : #else
    1371             :     int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
    1372             : #endif
    1373           0 :     if (nErr)
    1374           0 :         LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
    1375           0 : }
    1376             : #endif
    1377             : 
    1378         180 : void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName)
    1379             : {
    1380         180 :     auto cond = std::make_shared<std::condition_variable>();
    1381         180 :     auto mutex = std::make_shared<std::mutex>();
    1382         180 :     std::atomic<int> doneCnt(0);
    1383         180 :     std::map<int, std::future<void> > futures;
    1384             : 
    1385         900 :     for (int i = 0; i < tp.size(); i++) {
    1386        1432 :         futures[i] = tp.push([baseName, i, cond, mutex, &doneCnt](int threadId) {
    1387         712 :             util::ThreadRename(strprintf("%s-%d", baseName, i));
    1388         712 :             std::unique_lock<std::mutex> l(*mutex);
    1389         712 :             doneCnt++;
    1390         712 :             cond->wait(l);
    1391         712 :         });
    1392         720 :     }
    1393             : 
    1394         180 :     do {
    1395             :         // Always sleep to let all threads acquire locks
    1396         180 :         UninterruptibleSleep(std::chrono::milliseconds{10});
    1397             :         // `doneCnt` should be at least `futures.size()` if tp size was increased (for whatever reason),
    1398             :         // or at least `tp.size()` if tp size was decreased and queue was cleared
    1399             :         // (which can happen on `stop()` if we were not fast enough to get all jobs to their threads).
    1400         180 :     } while (uint64_t(doneCnt) < futures.size() && doneCnt < tp.size());
    1401             : 
    1402         180 :     cond->notify_all();
    1403             : 
    1404             :     // Make sure no one is left behind, just in case
    1405         900 :     for (auto& pair : futures) {
    1406         720 :         auto& f = pair.second;
    1407         720 :         if (f.valid() && f.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
    1408           0 :             LogPrintf("%s: %s-%d timed out\n", __func__, baseName, pair.first);
    1409             :             // Notify everyone again
    1410           0 :             cond->notify_all();
    1411           0 :             break;
    1412             :         }
    1413             :     }
    1414         180 : }
    1415             : 
    1416         704 : void SetupEnvironment()
    1417             : {
    1418             : #ifdef HAVE_MALLOPT_ARENA_MAX
    1419             :     // glibc-specific: On 32-bit systems set the number of arenas to 1.
    1420             :     // By default, since glibc 2.10, the C library will create up to two heap
    1421             :     // arenas per core. This is known to cause excessive virtual address space
    1422             :     // usage in our usage. Work around it by setting the maximum number of
    1423             :     // arenas to 1.
    1424             :     if (sizeof(void*) == 4) {
    1425             :         mallopt(M_ARENA_MAX, 1);
    1426             :     }
    1427             : #endif
    1428             :     // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
    1429             :     // may be invalid, in which case the "C.UTF-8" locale is used as fallback.
    1430             : #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
    1431             :     try {
    1432             :         std::locale(""); // Raises a runtime error if current locale is invalid
    1433             :     } catch (const std::runtime_error&) {
    1434             :         setenv("LC_ALL", "C.UTF-8", 1);
    1435             :     }
    1436             : #elif defined(WIN32)
    1437             :     // Set the default input/output charset is utf-8
    1438             :     SetConsoleCP(CP_UTF8);
    1439             :     SetConsoleOutputCP(CP_UTF8);
    1440             : #endif
    1441             : 
    1442             : #ifndef WIN32
    1443         704 :     constexpr mode_t private_umask = 0077;
    1444         704 :     umask(private_umask);
    1445             : #endif
    1446         704 : }
    1447             : 
    1448         627 : bool SetupNetworking()
    1449             : {
    1450             : #ifdef WIN32
    1451             :     // Initialize Windows Sockets
    1452             :     WSADATA wsadata;
    1453             :     int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
    1454             :     if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
    1455             :         return false;
    1456             : #endif
    1457         627 :     return true;
    1458             : }
    1459             : 
    1460        1254 : int GetNumCores()
    1461             : {
    1462        1254 :     return std::thread::hardware_concurrency();
    1463             : }
    1464             : 
    1465             : // Obtain the application startup time (used for uptime calculation)
    1466           0 : int64_t GetStartupTime()
    1467             : {
    1468           0 :     return nStartupTime;
    1469             : }
    1470             : 
    1471         627 : fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
    1472             : {
    1473         627 :     if (path.is_absolute()) {
    1474           0 :         return path;
    1475             :     }
    1476         627 :     return fsbridge::AbsPathJoin(net_specific ? gArgs.GetDataDirNet() : gArgs.GetDataDirBase(), path);
    1477         627 : }
    1478             : 
    1479           0 : void ScheduleBatchPriority()
    1480             : {
    1481             : #ifdef SCHED_BATCH
    1482             :     const static sched_param param{};
    1483             :     const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param);
    1484             :     if (rc != 0) {
    1485             :         LogPrintf("Failed to pthread_setschedparam: %s\n", SysErrorString(rc));
    1486             :     }
    1487             : #endif
    1488           0 : }
    1489             : 
    1490             : namespace util {
    1491             : #ifdef WIN32
    1492             : WinCmdLineArgs::WinCmdLineArgs()
    1493             : {
    1494             :     wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
    1495             :     std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
    1496             :     argv = new char*[argc];
    1497             :     args.resize(argc);
    1498             :     for (int i = 0; i < argc; i++) {
    1499             :         args[i] = utf8_cvt.to_bytes(wargv[i]);
    1500             :         argv[i] = &*args[i].begin();
    1501             :     }
    1502             :     LocalFree(wargv);
    1503             : }
    1504             : 
    1505             : WinCmdLineArgs::~WinCmdLineArgs()
    1506             : {
    1507             :     delete[] argv;
    1508             : }
    1509             : 
    1510             : std::pair<int, char**> WinCmdLineArgs::get()
    1511             : {
    1512             :     return std::make_pair(argc, argv);
    1513             : }
    1514             : #endif
    1515             : } // namespace util

Generated by: LCOV version 1.16