/* * This file is part of the Flowee project * Copyright (C) 2009-2010 Satoshi Nakamoto * Copyright (C) 2009-2015 The Bitcoin Core developers * Copyright (C) 2017-2018,2024 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined(HAVE_CONFIG_H) #include "config/flowee-config.h" #endif #include "util.h" #include #include "chainparamsbase.h" #include "sync.h" #include "utilstrencodings.h" #include #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) #include #include #endif #ifndef WIN32 // for posix_fallocate #ifdef __linux__ #ifdef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #define _POSIX_C_SOURCE 200112L #endif // __linux__ #include #include #include #else #ifdef _MSC_VER #pragma warning(disable:4786) #pragma warning(disable:4804) #pragma warning(disable:4805) #pragma warning(disable:4717) #endif #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0501 #ifdef _WIN32_IE #undef _WIN32_IE #endif #define _WIN32_IE 0x0501 #define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif #include /* for _commit */ #include #endif #ifdef HAVE_SYS_PRCTL_H #include #endif #include // for to_lower() #include #include #include // for startswith() and endswith() #include #include #include #include #include #include std::map mapArgs; std::map > mapMultiArgs; /** Turn -noX into -X=0 */ static void InterpretNegativeSetting(std::string& strKey, std::string& strValue) { if (strKey.length()>3 && strKey[0]=='-' && strKey[1]=='n' && strKey[2]=='o') { strKey = "-" + strKey.substr(3); strValue = InterpretBool(strValue) ? "0" : "1"; } } void ParseParameters(int argc, const char* const argv[], const Settings::AllowedArgs& allowedArgs) { mapArgs.clear(); mapMultiArgs.clear(); for (int i = 1; i < argc; i++) { std::string str(argv[i]); std::string strValue; size_t is_index = str.find('='); if (is_index != std::string::npos) { strValue = str.substr(is_index+1); str = str.substr(0, is_index); } #ifdef WIN32 boost::to_lower(str); if (boost::algorithm::starts_with(str, "/")) str = "-" + str.substr(1); #endif if (str[0] != '-') break; // Interpret --foo as -foo. // If both --foo and -foo are set, the last takes effect. if (str.length() > 1 && str[1] == '-') str = str.substr(1); InterpretNegativeSetting(str, strValue); allowedArgs.checkArg(str.substr(1), strValue); mapArgs[str] = strValue; mapMultiArgs[str].push_back(strValue); } } std::string GetArg(const std::string& strArg, const std::string& strDefault) { if (mapArgs.count(strArg)) return mapArgs[strArg]; return strDefault; } int64_t GetArg(const std::string& strArg, int64_t nDefault) { if (mapArgs.count(strArg)) return atoi64(mapArgs[strArg]); return nDefault; } bool GetBoolArg(const std::string& strArg, bool fDefault) { if (mapArgs.count(strArg)) return InterpretBool(mapArgs[strArg]); return fDefault; } bool SoftSetArg(const std::string& strArg, const std::string& strValue) { if (mapArgs.count(strArg)) return false; mapArgs[strArg] = strValue; return true; } bool SoftSetBoolArg(const std::string& strArg, bool fValue) { if (fValue) return SoftSetArg(strArg, std::string("1")); else return SoftSetArg(strArg, std::string("0")); } static std::string FormatException(const std::exception* pex, const char* pszThread) { #ifdef WIN32 char pszModule[MAX_PATH] = ""; GetModuleFileNameA(NULL, pszModule, sizeof(pszModule)); #else const char* pszModule = "bitcoin"; #endif if (pex) return strprintf( "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); else return strprintf( "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); } void PrintExceptionContinue(const std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); LogPrintf("\n\n************************\n%s\n", message); fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); } boost::filesystem::path GetDefaultDataDir() { namespace fs = boost::filesystem; // Windows: C:\Users\Username\AppData\Roaming\flowee // Mac: ~/Library/Application Support/flowee // Unix: $XDG_DATA_HOME/flowee (typically $HOME/.local/share/flowee) std::string dirName = "flowee"; #ifdef WIN32 // Windows return GetSpecialFolderPath(CSIDL_APPDATA) / dirName; #else fs::path pathHome; char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) pathHome = fs::path("/"); else pathHome = fs::path(pszHome); #ifdef MAC_OSX // Mac return pathHome / "Library/Application Support" / dirName; #else fs::path pathDataHome; char* pszDataHome = getenv("XDG_DATA_HOME"); if (pszDataHome == NULL || strlen(pszDataHome) == 0) pathDataHome = pathHome / ".local/share"; else pathDataHome = fs::path(pszDataHome); return pathDataHome / dirName; #endif #endif } static boost::filesystem::path pathCached; static boost::filesystem::path pathCachedNetSpecific; static CCriticalSection csPathCached; const boost::filesystem::path &GetDataDir(bool fNetSpecific) { namespace fs = boost::filesystem; LOCK(csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; // This can be called during exceptions by LogPrintf(), so we cache the // value so we don't have to do memory allocations after that. if (!path.empty()) return path; if (mapArgs.count("-datadir")) { path = fs::system_complete(mapArgs["-datadir"]); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDefaultDataDir(); } if (fNetSpecific) path /= BaseParams().DataDir(); fs::create_directories(path); return path; } void ClearDatadirCache() { pathCached = boost::filesystem::path(); pathCachedNetSpecific = boost::filesystem::path(); } boost::filesystem::path GetConfigFile(const std::string &filename) { namespace fs = boost::filesystem; const fs::path floweeConfigFile = GetArg("-conf", Settings::hubConfFilename()); auto testValid = [=](fs::path &path, const char *envVar = nullptr, const char *subdir = nullptr) -> bool { auto filePart = filename; if (filePart.empty()) filePart = Settings::hubConfFilename(); if (envVar) { assert(subdir); const char *p = getenv(envVar); if (!p || strlen(p) == 0) return false; path = fs::path(p); path /= fs::path(subdir) / filePart; return fs::exists(path); } path /= filePart; return fs::exists(path); }; /* * Asking with an empty filename means we need just the hub general config * file. * We try: * 1. in case of user-set absolute filename, that is what we use. * 2. check XDG dir * 3. check $HOME/.config/flowee/ dir */ if (filename.empty()) { assert(!floweeConfigFile.empty()); if (floweeConfigFile.is_absolute() || fs::exists(floweeConfigFile)) return floweeConfigFile; fs::path path; if (testValid(path, "XDG_CONFIG_HOME", "flowee")) return path; if (testValid(path, "HOME", ".config/flowee")) return path; return floweeConfigFile; // doesn't exist, though. } /* * Any config file that is not the hubConfFilename will be tried in * 1. the effective data directory. (testnet4 subdir, for instance) * 2. the main datadir (which is the same as 1 in case of mainnet) * 3. the same dir that the flowee.conf lives in. * 4. The $XDG_CONFIG_HOME/ dir * 5. In $HOME/.config/flowee/ */ auto path = GetDataDir(true); if (testValid(path)) return path; path = GetDataDir(false); if (testValid(path)) return path; path = floweeConfigFile.parent_path(); if (testValid(path)) return path; if (testValid(path, "XDG_CONFIG_HOME", "flowee")) return path; if (testValid(path, "HOME", ".config/flowee")) return path; return floweeConfigFile; } void ReadConfigFile(std::map& mapSettingsRet, std::map >& mapMultiSettingsRet) { boost::filesystem::ifstream streamConfig(GetConfigFile()); if (!streamConfig.good()) return; // No flowee.conf file is OK std::set setOptions; setOptions.insert("*"); Settings::ConfigFile allowedArgs; for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) { // Don't overwrite existing settings so command line settings override flowee.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; InterpretNegativeSetting(strKey, strValue); allowedArgs.checkArg(strKey.substr(1), strValue); if (mapSettingsRet.count(strKey) == 0) mapSettingsRet[strKey] = strValue; mapMultiSettingsRet[strKey].push_back(strValue); } // If datadir is changed in .conf file: ClearDatadirCache(); } /** * Ignores exceptions thrown by Boost's create_directory if the requested directory exists. * Specifically handles case where path p exists, but it wasn't possible for the user to * write to the parent directory. */ bool TryCreateDirectory(const boost::filesystem::path& p) { try { return boost::filesystem::create_directories(p); } catch (const boost::filesystem::filesystem_error&) { if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) throw; } // create_directory didn't create the directory, it had to have existed already return false; } #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) { namespace fs = boost::filesystem; char pszPath[MAX_PATH] = ""; if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate)) { return fs::path(pszPath); } LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); return fs::path(""); } #endif void RenameThread(const char* name) { #if defined(PR_SET_NAME) // Only the first 15 characters are used (16 - NUL terminator) ::prctl(PR_SET_NAME, name, 0, 0, 0); #elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) pthread_set_name_np(pthread_self(), name); #elif defined(MAC_OSX) pthread_setname_np(name); #else // Prevent warnings for unused parameters... (void)name; #endif } void SetThreadPriority(int nPriority) { #ifdef WIN32 SetThreadPriority(GetCurrentThread(), nPriority); #else // WIN32 #ifdef PRIO_THREAD setpriority(PRIO_THREAD, 0, nPriority); #else // PRIO_THREAD setpriority(PRIO_PROCESS, 0, nPriority); #endif // PRIO_THREAD #endif // WIN32 } std::string ChainNameFromCommandLine() { int num_selected = 0; bool fRegTest = GetBoolArg("-regtest", false); if (fRegTest) num_selected++; bool fTestNet = GetBoolArg("-testnet", false); if (fTestNet) num_selected++; bool fTestNet4 = GetBoolArg("-testnet4", false); if (fTestNet4) num_selected++; bool fScaleNet = GetBoolArg("-scalenet", false); if (fScaleNet) num_selected++; if (num_selected > 1) throw std::runtime_error("Invalid combination of -regtest, -testnet, -testnet4 and -scalenet."); if (fRegTest) return CBaseChainParams::REGTEST; if (fTestNet) return CBaseChainParams::TESTNET; if (fTestNet4) return CBaseChainParams::TESTNET4; if (fScaleNet) return CBaseChainParams::SCALENET; return CBaseChainParams::MAIN; }