diff --git a/src/util.cpp b/src/util.cpp index e15ebcbad..e8ed8dd90 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,1038 +1,1032 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #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 #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 #ifdef HAVE_MALLOPT_ARENA_MAX #include #endif // Application startup time (used for uptime calculation) const int64_t nStartupTime = GetTime(); const char *const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char *const BITCOIN_PID_FILENAME = "bitcoind.pid"; ArgsManager gArgs; CTranslationInterface translationInterface; /** Init OpenSSL library multithreading support */ -static CCriticalSection **ppmutexOpenSSL; +static std::unique_ptr ppmutexOpenSSL; void locking_callback(int mode, int i, const char *file, int line) NO_THREAD_SAFETY_ANALYSIS { if (mode & CRYPTO_LOCK) { - ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + ENTER_CRITICAL_SECTION(ppmutexOpenSSL[i]); } else { - LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); + LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]); } } -// Init +// Singleton for wrapping OpenSSL setup/teardown. class CInit { public: CInit() { // Init OpenSSL library multithreading support. - ppmutexOpenSSL = (CCriticalSection **)OPENSSL_malloc( - CRYPTO_num_locks() * sizeof(CCriticalSection *)); - for (int i = 0; i < CRYPTO_num_locks(); i++) { - ppmutexOpenSSL[i] = new CCriticalSection(); - } + ppmutexOpenSSL.reset(new CCriticalSection[CRYPTO_num_locks()]); CRYPTO_set_locking_callback(locking_callback); // OpenSSL can optionally load a config file which lists optional // loadable modules and engines. We don't use them so we don't require // the config. However some of our libs may call functions which attempt // to load the config file, possibly resulting in an exit() or crash if // it is missing or corrupt. Explicitly tell OpenSSL not to try to load // the file. The result for our libs will be that the config appears to // have been loaded and there are no modules/engines available. OPENSSL_no_config(); #ifdef WIN32 // Seed OpenSSL PRNG with current contents of the screen. RAND_screen(); #endif // Seed OpenSSL PRNG with performance counter. RandAddSeed(); } ~CInit() { // Securely erase the memory used by the PRNG. RAND_cleanup(); // Shutdown OpenSSL library multithreading support. CRYPTO_set_locking_callback(nullptr); - for (int i = 0; i < CRYPTO_num_locks(); i++) { - delete ppmutexOpenSSL[i]; - } - OPENSSL_free(ppmutexOpenSSL); + // Clear the set of locks now to maintain symmetry with the constructor. + ppmutexOpenSSL.reset(); } } instance_of_cinit; /** * A map that contains all the currently held directory locks. After successful * locking, these will be held here until the global destructor cleans them up * and thus automatically unlocks them, or ReleaseDirectoryLocks is called. */ static std::map> dir_locks; /** Mutex to protect dir_locks. */ static std::mutex cs_dir_locks; bool LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only) { std::lock_guard ulock(cs_dir_locks); fs::path pathLockFile = directory / lockfile_name; // If a lock for this directory already exists in the map, don't try to // re-lock it if (dir_locks.count(pathLockFile.string())) { return true; } // Create empty lock file if it doesn't exist. FILE *file = fsbridge::fopen(pathLockFile, "a"); if (file) { fclose(file); } try { auto lock = std::make_unique( pathLockFile.string().c_str()); if (!lock->try_lock()) { return false; } if (!probe_only) { // Lock successful and we're not just probing, put it into the map dir_locks.emplace(pathLockFile.string(), std::move(lock)); } } catch (const boost::interprocess::interprocess_exception &e) { return error("Error while attempting to lock directory %s: %s", directory.string(), e.what()); } return true; } void ReleaseDirectoryLocks() { std::lock_guard ulock(cs_dir_locks); dir_locks.clear(); } bool DirIsWritable(const fs::path &directory) { fs::path tmpFile = directory / fs::unique_path(); FILE *file = fsbridge::fopen(tmpFile, "a"); if (!file) { return false; } fclose(file); remove(tmpFile); return true; } /** * Interpret a string argument as a boolean. * * The definition of atoi() requires that non-numeric string values like "foo", * return 0. This means that if a user unintentionally supplies a non-integer * argument here, the return value is always false. This means that -foo=false * does what the user probably expects, but -foo=true is well defined but does * not do what they probably expected. * * The return value of atoi() is undefined when given input not representable as * an int. On most systems this means string value between "-2147483648" and * "2147483647" are well defined (this method will return true). Setting * -txindex=2147483648 on most systems, however, is probably undefined. * * For a more extensive discussion of this topic (and a wide range of opinions * on the Right Way to change this code), see PR12713. */ static bool InterpretBool(const std::string &strValue) { if (strValue.empty()) { return true; } return (atoi(strValue) != 0); } /** Internal helper functions for ArgsManager */ class ArgsManagerHelper { public: typedef std::map> MapArgs; /** Determine whether to use config settings in the default section, * See also comments around ArgsManager::ArgsManager() below. */ static inline bool UseDefaultSection(const ArgsManager &am, const std::string &arg) { return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); } /** Convert regular argument into the network-specific setting */ static inline std::string NetworkArg(const ArgsManager &am, const std::string &arg) { assert(arg.length() > 1 && arg[0] == '-'); return "-" + am.m_network + "." + arg.substr(1); } /** Find arguments in a map and add them to a vector */ static inline void AddArgs(std::vector &res, const MapArgs &map_args, const std::string &arg) { auto it = map_args.find(arg); if (it != map_args.end()) { res.insert(res.end(), it->second.begin(), it->second.end()); } } /** * Return true/false if an argument is set in a map, and also * return the first (or last) of the possibly multiple values it has */ static inline std::pair GetArgHelper(const MapArgs &map_args, const std::string &arg, bool getLast = false) { auto it = map_args.find(arg); if (it == map_args.end() || it->second.empty()) { return std::make_pair(false, std::string()); } if (getLast) { return std::make_pair(true, it->second.back()); } else { return std::make_pair(true, it->second.front()); } } /** * Get the string value of an argument, returning a pair of a boolean * indicating the argument was found, and the value for the argument * if it was found (or the empty string if not found). */ static inline std::pair GetArg(const ArgsManager &am, const std::string &arg) { LOCK(am.cs_args); std::pair found_result(false, std::string()); // We pass "true" to GetArgHelper in order to return the last // argument value seen from the command line (so "bitcoind -foo=bar // -foo=baz" gives GetArg(am,"foo")=={true,"baz"} found_result = GetArgHelper(am.m_override_args, arg, true); if (found_result.first) { return found_result; } // But in contrast we return the first argument seen in a config file, // so "foo=bar \n foo=baz" in the config file gives // GetArg(am,"foo")={true,"bar"} if (!am.m_network.empty()) { found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg)); if (found_result.first) { return found_result; } } if (UseDefaultSection(am, arg)) { found_result = GetArgHelper(am.m_config_args, arg); if (found_result.first) { return found_result; } } return found_result; } /* Special test for -testnet and -regtest args, because we don't want to be * confused by craziness like "[regtest] testnet=1" */ static inline bool GetNetBoolArg(const ArgsManager &am, const std::string &net_arg) { std::pair found_result(false, std::string()); found_result = GetArgHelper(am.m_override_args, net_arg, true); if (!found_result.first) { found_result = GetArgHelper(am.m_config_args, net_arg, true); if (!found_result.first) { // not set return false; } } // is set, so evaluate return InterpretBool(found_result.second); } }; /** * Interpret -nofoo as if the user supplied -foo=0. * * This method also tracks when the -no form was supplied, and if so, checks * whether there was a double-negative (-nofoo=0 -> -foo=1). * * If there was not a double negative, it removes the "no" from the key, and * returns true, indicating the caller should clear the args vector to indicate * a negated option. * * If there was a double negative, it removes "no" from the key, sets the value * to "1" and returns false. * * If there was no "no", it leaves key and value untouched and returns false. * * Where an option was negated can be later checked using the IsArgNegated() * method. One use case for this is to have a way to disable options that are * not normally boolean (e.g. using -nodebuglogfile to request that debug log * output is not sent to any file at all). */ static bool InterpretNegatedOption(std::string &key, std::string &val) { assert(key[0] == '-'); size_t option_index = key.find('.'); if (option_index == std::string::npos) { option_index = 1; } else { ++option_index; } if (key.substr(option_index, 2) == "no") { bool bool_val = InterpretBool(val); key.erase(option_index, 2); if (!bool_val) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf( "Warning: parsed potentially confusing double-negative %s=%s\n", key, val); val = "1"; } else { return true; } } return false; } ArgsManager::ArgsManager() : /* These options would cause cross-contamination if values for mainnet * were used while running on regtest/testnet (or vice-versa). * Setting them as section_only_args ensures that sharing a config file * between mainnet and regtest/testnet won't cause problems due to these * parameters by accident. */ m_network_only_args{ "-addnode", "-connect", "-port", "-bind", "-rpcport", "-rpcbind", "-wallet", } { // nothing to do } void ArgsManager::WarnForSectionOnlyArgs() { // if there's no section selected, don't worry if (m_network.empty()) { return; } // if it's okay to use the default section for this network, don't worry if (m_network == CBaseChainParams::MAIN) { return; } for (const auto &arg : m_network_only_args) { std::pair found_result; // if this option is overridden it's fine found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg); if (found_result.first) { continue; } // if there's a network-specific value for this option, it's fine found_result = ArgsManagerHelper::GetArgHelper( m_config_args, ArgsManagerHelper::NetworkArg(*this, arg)); if (found_result.first) { continue; } // if there isn't a default value for this option, it's fine found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg); if (!found_result.first) { continue; } // otherwise, issue a warning LogPrintf("Warning: Config setting for %s only applied on %s network " "when in [%s] section.\n", arg, m_network, m_network); } } void ArgsManager::SelectConfigNetwork(const std::string &network) { m_network = network; } void ArgsManager::ParseParameters(int argc, const char *const argv[]) { LOCK(cs_args); m_override_args.clear(); for (int i = 1; i < argc; i++) { std::string key(argv[i]); std::string val; size_t is_index = key.find('='); if (is_index != std::string::npos) { val = key.substr(is_index + 1); key.erase(is_index); } #ifdef WIN32 std::transform(key.begin(), key.end(), key.begin(), ::tolower); if (key[0] == '/') { key[0] = '-'; } #endif if (key[0] != '-') { break; } // Transform --foo to -foo if (key.length() > 1 && key[1] == '-') { key.erase(0, 1); } // Check for -nofoo if (InterpretNegatedOption(key, val)) { m_override_args[key].clear(); } else { m_override_args[key].push_back(val); } } } std::vector ArgsManager::GetArgs(const std::string &strArg) const { std::vector result = {}; // special case if (IsArgNegated(strArg)) { return result; } LOCK(cs_args); ArgsManagerHelper::AddArgs(result, m_override_args, strArg); if (!m_network.empty()) { ArgsManagerHelper::AddArgs( result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg)); } if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) { ArgsManagerHelper::AddArgs(result, m_config_args, strArg); } return result; } bool ArgsManager::IsArgSet(const std::string &strArg) const { // special case if (IsArgNegated(strArg)) { return true; } return ArgsManagerHelper::GetArg(*this, strArg).first; } bool ArgsManager::IsArgNegated(const std::string &strArg) const { LOCK(cs_args); const auto &ov = m_override_args.find(strArg); if (ov != m_override_args.end()) { return ov->second.empty(); } if (!m_network.empty()) { const auto &cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg)); if (cfs != m_config_args.end()) { return cfs->second.empty(); } } const auto &cf = m_config_args.find(strArg); if (cf != m_config_args.end()) { return cf->second.empty(); } return false; } std::string ArgsManager::GetArg(const std::string &strArg, const std::string &strDefault) const { if (IsArgNegated(strArg)) { return "0"; } std::pair found_res = ArgsManagerHelper::GetArg(*this, strArg); if (found_res.first) { return found_res.second; } return strDefault; } int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const { if (IsArgNegated(strArg)) { return 0; } std::pair found_res = ArgsManagerHelper::GetArg(*this, strArg); if (found_res.first) { return atoi64(found_res.second); } return nDefault; } bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const { if (IsArgNegated(strArg)) { return false; } std::pair found_res = ArgsManagerHelper::GetArg(*this, strArg); if (found_res.first) { return InterpretBool(found_res.second); } return fDefault; } bool ArgsManager::SoftSetArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); if (IsArgSet(strArg)) { return false; } ForceSetArg(strArg, strValue); return true; } bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) { if (fValue) { return SoftSetArg(strArg, std::string("1")); } else { return SoftSetArg(strArg, std::string("0")); } } void ArgsManager::ForceSetArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); m_override_args[strArg] = {strValue}; } /** * This function is only used for testing purpose so * so we should not worry about element uniqueness and * integrity of mapMultiArgs data structure */ void ArgsManager::ForceSetMultiArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); m_override_args[strArg].push_back(strValue); } void ArgsManager::ClearArg(const std::string &strArg) { LOCK(cs_args); m_override_args.erase(strArg); m_config_args.erase(strArg); } bool HelpRequested(const ArgsManager &args) { return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); } static const int screenWidth = 79; static const int optIndent = 2; static const int msgIndent = 7; std::string HelpMessageGroup(const std::string &message) { return std::string(message) + std::string("\n\n"); } std::string HelpMessageOpt(const std::string &option, const std::string &message) { return std::string(optIndent, ' ') + std::string(option) + std::string("\n") + std::string(msgIndent, ' ') + FormatParagraph(message, screenWidth - msgIndent, msgIndent) + std::string("\n\n"); } static std::string FormatException(const std::exception *pex, const char *pszThread) { #ifdef WIN32 char pszModule[MAX_PATH] = ""; GetModuleFileNameA(nullptr, 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()); } fs::path GetDefaultDataDir() { // Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin // Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin // Mac: ~/Library/Application Support/Bitcoin // Unix: ~/.bitcoin #ifdef WIN32 // Windows return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin"; #else fs::path pathRet; char *pszHome = getenv("HOME"); if (pszHome == nullptr || strlen(pszHome) == 0) { pathRet = fs::path("/"); } else { pathRet = fs::path(pszHome); } #ifdef MAC_OSX // Mac return pathRet / "Library/Application Support/Bitcoin"; #else // Unix return pathRet / ".bitcoin"; #endif #endif } static fs::path pathCached; static fs::path pathCachedNetSpecific; static CCriticalSection csPathCached; const fs::path &GetDataDir(bool fNetSpecific) { 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 (gArgs.IsArgSet("-datadir")) { path = fs::system_complete(gArgs.GetArg("-datadir", "")); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDefaultDataDir(); } if (fNetSpecific) { path /= BaseParams().DataDir(); } if (fs::create_directories(path)) { // This is the first run, create wallets subdirectory too fs::create_directories(path / "wallets"); } return path; } void ClearDatadirCache() { LOCK(csPathCached); pathCached = fs::path(); pathCachedNetSpecific = fs::path(); } fs::path GetConfigFile(const std::string &confPath) { fs::path pathConfigFile(confPath); if (!pathConfigFile.is_complete()) { pathConfigFile = GetDataDir(false) / pathConfigFile; } return pathConfigFile; } void ArgsManager::ReadConfigStream(std::istream &stream) { LOCK(cs_args); std::set setOptions; setOptions.insert("*"); for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it) { std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; if (InterpretNegatedOption(strKey, strValue)) { m_config_args[strKey].clear(); } else { m_config_args[strKey].push_back(strValue); } } } void ArgsManager::ReadConfigFile(const std::string &confPath) { { LOCK(cs_args); m_config_args.clear(); } fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { ReadConfigStream(stream); } // If datadir is changed in .conf file: ClearDatadirCache(); if (!fs::is_directory(GetDataDir(false))) { throw std::runtime_error( strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str())); } } std::string ArgsManager::GetChainName() const { bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); if (fTestNet && fRegTest) { throw std::runtime_error( "Invalid combination of -regtest and -testnet."); } if (fRegTest) { return CBaseChainParams::REGTEST; } if (fTestNet) { return CBaseChainParams::TESTNET; } return CBaseChainParams::MAIN; } #ifndef WIN32 fs::path GetPidFile() { fs::path pathPidFile(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)); if (!pathPidFile.is_complete()) { pathPidFile = GetDataDir() / pathPidFile; } return pathPidFile; } void CreatePidFile(const fs::path &path, pid_t pid) { FILE *file = fsbridge::fopen(path, "w"); if (file) { fprintf(file, "%d\n", pid); fclose(file); } } #endif bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 return MoveFileExA(src.string().c_str(), dest.string().c_str(), MOVEFILE_REPLACE_EXISTING) != 0; #else int rc = std::rename(src.string().c_str(), dest.string().c_str()); return (rc == 0); #endif /* WIN32 */ } /** * Ignores exceptions thrown by Boost's create_directories 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 TryCreateDirectories(const fs::path &p) { try { return fs::create_directories(p); } catch (const fs::filesystem_error &) { if (!fs::exists(p) || !fs::is_directory(p)) { throw; } } // create_directory didn't create the directory, it had to have existed // already. return false; } void FileCommit(FILE *file) { // Harmless if redundantly called. fflush(file); #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); FlushFileBuffers(hFile); #else #if defined(__linux__) || defined(__NetBSD__) fdatasync(fileno(file)); #elif defined(__APPLE__) && defined(F_FULLFSYNC) fcntl(fileno(file), F_FULLFSYNC, 0); #else fsync(fileno(file)); #endif #endif } bool TruncateFile(FILE *file, unsigned int length) { #if defined(WIN32) return _chsize(_fileno(file), length) == 0; #else return ftruncate(fileno(file), length) == 0; #endif } /** * This function tries to raise the file descriptor limit to the requested * number. It returns the actual file descriptor limit (which may be more or * less than nMinFD) */ int RaiseFileDescriptorLimit(int nMinFD) { #if defined(WIN32) return 2048; #else struct rlimit limitFD; if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) { if (limitFD.rlim_cur < (rlim_t)nMinFD) { limitFD.rlim_cur = nMinFD; if (limitFD.rlim_cur > limitFD.rlim_max) { limitFD.rlim_cur = limitFD.rlim_max; } setrlimit(RLIMIT_NOFILE, &limitFD); getrlimit(RLIMIT_NOFILE, &limitFD); } return limitFD.rlim_cur; } // getrlimit failed, assume it's fine. return nMinFD; #endif } /** * This function tries to make a particular range of a file allocated * (corresponding to disk space) it is advisory, and the range specified in the * arguments will never contain live data. */ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #if defined(WIN32) // Windows-specific version. HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); LARGE_INTEGER nFileSize; int64_t nEndPos = (int64_t)offset + length; nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; nFileSize.u.HighPart = nEndPos >> 32; SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); SetEndOfFile(hFile); #elif defined(MAC_OSX) // OSX specific version. fstore_t fst; fst.fst_flags = F_ALLOCATECONTIG; fst.fst_posmode = F_PEOFPOSMODE; fst.fst_offset = 0; fst.fst_length = (off_t)offset + length; fst.fst_bytesalloc = 0; if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) { fst.fst_flags = F_ALLOCATEALL; fcntl(fileno(file), F_PREALLOCATE, &fst); } ftruncate(fileno(file), fst.fst_length); #elif defined(__linux__) // Version using posix_fallocate. off_t nEndPos = (off_t)offset + length; posix_fallocate(fileno(file), 0, nEndPos); #else // Fallback version // TODO: just write one byte per block static const char buf[65536] = {}; fseek(file, offset, SEEK_SET); while (length > 0) { unsigned int now = 65536; if (length < now) { now = length; } // Allowed to fail; this function is advisory anyway. fwrite(buf, 1, now, file); length -= now; } #endif } #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { char pszPath[MAX_PATH] = ""; if (SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) { return fs::path(pszPath); } LogPrintf( "SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); return fs::path(""); } #endif void runCommand(const std::string &strCommand) { if (strCommand.empty()) { return; } int nErr = ::system(strCommand.c_str()); if (nErr) { LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); } } 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 SetupEnvironment() { #ifdef HAVE_MALLOPT_ARENA_MAX // glibc-specific: On 32-bit systems set the number of arenas to 1. By // default, since glibc 2.10, the C library will create up to two heap // arenas per core. This is known to cause excessive virtual address space // usage in our usage. Work around it by setting the maximum number of // arenas to 1. if (sizeof(void *) == 4) { mallopt(M_ARENA_MAX, 1); } #endif // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale may // be invalid, in which case the "C" locale is used as fallback. #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && \ !defined(__OpenBSD__) try { // Raises a runtime error if current locale is invalid. std::locale(""); } catch (const std::runtime_error &) { setenv("LC_ALL", "C", 1); } #endif // The path locale is lazy initialized and to avoid deinitialization errors // in multithreading environments, it is set explicitly by the main thread. // A dummy locale is used to extract the internal default locale, used by // fs::path, which is then used to explicitly imbue the path. std::locale loc = fs::path::imbue(std::locale::classic()); fs::path::imbue(loc); } bool SetupNetworking() { #ifdef WIN32 // Initialize Windows Sockets. WSADATA wsadata; int ret = WSAStartup(MAKEWORD(2, 2), &wsadata); if (ret != NO_ERROR || LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { return false; } #endif return true; } int GetNumCores() { return boost::thread::physical_concurrency(); } std::string CopyrightHolders(const std::string &strPrefix) { return strPrefix + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION)); } // Obtain the application startup time (used for uptime calculation) int64_t GetStartupTime() { return nStartupTime; }