// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers // Copyright (c) 2014-2017 The Dash 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 "config/dash-config.h" #endif #include "util.h" #include "support/allocators/secure.h" #include "chainparamsbase.h" #include "random.h" #include "serialize.h" #include "sync.h" #include "utilstrencodings.h" #include "utiltime.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 #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 #include // for to_lower() #include #include // for startswith() and endswith() #include #include #include #include #include #include #include #include #include #include #include // Work around clang compilation problem in Boost 1.46: // /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup // See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options // http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION namespace boost { namespace program_options { std::string to_internal(const std::string&); } } // namespace boost //Dash only features bool fMasternodeMode = false; bool fLiteMode = false; /** nWalletBackups: 1..10 - number of automatic backups to keep 0 - disabled by command-line -1 - disabled because of some error during run-time -2 - disabled because wallet was locked and we were not able to replenish keypool */ int nWalletBackups = 10; const char * const BITCOIN_CONF_FILENAME = "dash.conf"; const char * const BITCOIN_PID_FILENAME = "dashd.pid"; CCriticalSection cs_args; std::map mapArgs; static std::map > _mapMultiArgs; const std::map >& mapMultiArgs = _mapMultiArgs; bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogThreadNames = DEFAULT_LOGTHREADNAMES; bool fLogIPs = DEFAULT_LOGIPS; std::atomic fReopenDebugLog(false); CTranslationInterface translationInterface; /** Init OpenSSL library multithreading support */ static CCriticalSection** 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]); } else { LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); } } // Init 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(); 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(NULL); for (int i = 0; i < CRYPTO_num_locks(); i++) delete ppmutexOpenSSL[i]; OPENSSL_free(ppmutexOpenSSL); } } instance_of_cinit; /** * LogPrintf() has been broken a couple of times now * by well-meaning people adding mutexes in the most straightforward way. * It breaks because it may be called by global destructors during shutdown. * Since the order of destruction of static/global objects is undefined, * defining a mutex as a global object doesn't work (the mutex gets * destroyed, and then some later destructor calls OutputDebugStringF, * maybe indirectly, and you get a core dump at shutdown trying to lock * the mutex). */ static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; /** * We use boost::call_once() to make sure mutexDebugLog and * vMsgsBeforeOpenLog are initialized in a thread-safe manner. * * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog * are leaked on exit. This is ugly, but will be cleaned up by * the OS/libc. When the shutdown sequence is fully audited and * tested, explicit destruction of these objects can be implemented. */ static FILE* fileout = NULL; static boost::mutex* mutexDebugLog = NULL; static std::list* vMsgsBeforeOpenLog; static int FileWriteStr(const std::string &str, FILE *fp) { return fwrite(str.data(), 1, str.size(), fp); } static void DebugPrintInit() { assert(mutexDebugLog == NULL); mutexDebugLog = new boost::mutex(); vMsgsBeforeOpenLog = new std::list; } void OpenDebugLog() { boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); assert(fileout == NULL); assert(vMsgsBeforeOpenLog); boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; fileout = fopen(pathDebug.string().c_str(), "a"); if (fileout) { setbuf(fileout, NULL); // unbuffered // dump buffered messages from before we opened the log while (!vMsgsBeforeOpenLog->empty()) { FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); vMsgsBeforeOpenLog->pop_front(); } } delete vMsgsBeforeOpenLog; vMsgsBeforeOpenLog = NULL; } bool LogAcceptCategory(const char* category) { if (category != NULL) { // Give each thread quick access to -debug settings. // This helps prevent issues debugging global destructors, // where mapMultiArgs might be deleted before another // global destructor calls LogPrint() static boost::thread_specific_ptr > ptrCategory; if (!fDebug) { if (ptrCategory.get() != NULL) { LogPrintf("debug turned off: thread %s\n", GetThreadName()); ptrCategory.release(); } return false; } if (ptrCategory.get() == NULL) { if (mapMultiArgs.count("-debug")) { std::string strThreadName = GetThreadName(); LogPrintf("debug turned on:\n"); for (int i = 0; i < (int)mapMultiArgs.at("-debug").size(); ++i) LogPrintf(" thread %s category %s\n", strThreadName, mapMultiArgs.at("-debug")[i]); const std::vector& categories = mapMultiArgs.at("-debug"); ptrCategory.reset(new std::set(categories.begin(), categories.end())); // thread_specific_ptr automatically deletes the set when the thread ends. // "dash" is a composite category enabling all Dash-related debug output if(ptrCategory->count(std::string("dash"))) { ptrCategory->insert(std::string("privatesend")); ptrCategory->insert(std::string("instantsend")); ptrCategory->insert(std::string("masternode")); ptrCategory->insert(std::string("spork")); ptrCategory->insert(std::string("keepass")); ptrCategory->insert(std::string("mnpayments")); ptrCategory->insert(std::string("gobject")); } } else { ptrCategory.reset(new std::set()); } } const std::set& setCategories = *ptrCategory.get(); // if not debugging everything and not debugging specific category, LogPrint does nothing. if (setCategories.count(std::string("")) == 0 && setCategories.count(std::string("1")) == 0 && setCategories.count(std::string(category)) == 0) return false; } return true; } /** * fStartedNewLine is a state variable held by the calling context that will * suppress printing of the timestamp when multiple calls are made that don't * end in a newline. Initialize it to true, and hold/manage it, in the calling context. */ static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) { std::string strStamped; if (!fLogTimestamps) return str; if (*fStartedNewLine) { int64_t nTimeMicros = GetLogTimeMicros(); strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTimeMicros/1000000); if (fLogTimeMicros) strStamped += strprintf(".%06d", nTimeMicros%1000000); strStamped += ' ' + str; } else strStamped = str; return strStamped; } /** * fStartedNewLine is a state variable held by the calling context that will * suppress printing of the thread name when multiple calls are made that don't * end in a newline. Initialize it to true, and hold/manage it, in the calling context. */ static std::string LogThreadNameStr(const std::string &str, std::atomic_bool *fStartedNewLine) { std::string strThreadLogged; if (!fLogThreadNames) return str; std::string strThreadName = GetThreadName(); if (*fStartedNewLine) strThreadLogged = strprintf("%16s | %s", strThreadName.c_str(), str.c_str()); else strThreadLogged = str; return strThreadLogged; } int LogPrintStr(const std::string &str) { int ret = 0; // Returns total number of characters written static std::atomic_bool fStartedNewLine(true); std::string strThreadLogged = LogThreadNameStr(str, &fStartedNewLine); std::string strTimestamped = LogTimestampStr(strThreadLogged, &fStartedNewLine); if (!str.empty() && str[str.size()-1] == '\n') fStartedNewLine = true; else fStartedNewLine = false; if (fPrintToConsole) { // print to console ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } else if (fPrintToDebugLog) { boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); // buffer if we haven't opened the log yet if (fileout == NULL) { assert(vMsgsBeforeOpenLog); ret = strTimestamped.length(); vMsgsBeforeOpenLog->push_back(strTimestamped); } else { // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) setbuf(fileout, NULL); // unbuffered } ret = FileWriteStr(strTimestamped, fileout); } } return ret; } /** Interpret string as boolean, for argument parsing */ static bool InterpretBool(const std::string& strValue) { if (strValue.empty()) return true; return (atoi(strValue) != 0); } /** 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[]) { LOCK(cs_args); 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); mapArgs[str] = strValue; _mapMultiArgs[str].push_back(strValue); } } bool IsArgSet(const std::string& strArg) { LOCK(cs_args); return mapArgs.count(strArg); } std::string GetArg(const std::string& strArg, const std::string& strDefault) { LOCK(cs_args); if (mapArgs.count(strArg)) return mapArgs[strArg]; return strDefault; } int64_t GetArg(const std::string& strArg, int64_t nDefault) { LOCK(cs_args); if (mapArgs.count(strArg)) return atoi64(mapArgs[strArg]); return nDefault; } bool GetBoolArg(const std::string& strArg, bool fDefault) { LOCK(cs_args); if (mapArgs.count(strArg)) return InterpretBool(mapArgs[strArg]); return fDefault; } bool SoftSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); 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")); } void ForceSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); mapArgs[strArg] = strValue; } void ForceSetMultiArgs(const std::string& strArg, const std::vector& values) { LOCK(cs_args); _mapMultiArgs[strArg] = values; } void ForceRemoveArg(const std::string& strArg) { LOCK(cs_args); mapArgs.erase(strArg); _mapMultiArgs.erase(strArg); } 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(NULL, pszModule, sizeof(pszModule)); #else const char* pszModule = "dash"; #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 < Vista: C:\Documents and Settings\Username\Application Data\DashCore // Windows >= Vista: C:\Users\Username\AppData\Roaming\DashCore // Mac: ~/Library/Application Support/DashCore // Unix: ~/.dashcore #ifdef WIN32 // Windows return GetSpecialFolderPath(CSIDL_APPDATA) / "DashCore"; #else fs::path pathRet; char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) pathRet = fs::path("/"); else pathRet = fs::path(pszHome); #ifdef MAC_OSX // Mac return pathRet / "Library/Application Support/DashCore"; #else // Unix return pathRet / ".dashcore"; #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 (IsArgSet("-datadir")) { path = fs::system_complete(GetArg("-datadir", "")); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDefaultDataDir(); } if (fNetSpecific) path /= BaseParams().DataDir(); fs::create_directories(path); return path; } boost::filesystem::path GetBackupsDir() { namespace fs = boost::filesystem; if (!IsArgSet("-walletbackupsdir")) return GetDataDir() / "backups"; return fs::absolute(GetArg("-walletbackupsdir", "")); } void ClearDatadirCache() { LOCK(csPathCached); pathCached = boost::filesystem::path(); pathCachedNetSpecific = boost::filesystem::path(); } boost::filesystem::path GetConfigFile(const std::string& confPath) { boost::filesystem::path pathConfigFile(confPath); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; return pathConfigFile; } boost::filesystem::path GetMasternodeConfigFile() { boost::filesystem::path pathConfigFile(GetArg("-mnconf", "masternode.conf")); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; return pathConfigFile; } void ReadConfigFile(const std::string& confPath) { boost::filesystem::ifstream streamConfig(GetConfigFile(confPath)); if (!streamConfig.good()){ // Create empty dash.conf if it does not excist FILE* configFile = fopen(GetConfigFile(confPath).string().c_str(), "a"); if (configFile != NULL) fclose(configFile); return; // Nothing to read, so just return } { LOCK(cs_args); std::set setOptions; setOptions.insert("*"); 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 dash.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; InterpretNegativeSetting(strKey, strValue); if (mapArgs.count(strKey) == 0) mapArgs[strKey] = strValue; _mapMultiArgs[strKey].push_back(strValue); } } // If datadir is changed in .conf file: ClearDatadirCache(); } #ifndef WIN32 boost::filesystem::path GetPidFile() { boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; return pathPidFile; } void CreatePidFile(const boost::filesystem::path &path, pid_t pid) { FILE* file = fopen(path.string().c_str(), "w"); if (file) { fprintf(file, "%d\n", pid); fclose(file); } } #endif bool RenameOver(boost::filesystem::path src, boost::filesystem::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_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_directory(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; } void FileCommit(FILE *file) { fflush(file); // harmless if redundantly called #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; } return nMinFD; // getrlimit failed, assume it's fine #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; fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway length -= now; } #endif } void ShrinkDebugFile() { // Amount of debug.log to save at end when shrinking (must fit in memory) constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; // Scroll debug.log if it's getting too big boost::filesystem::path pathLog = GetDataDir() / "debug.log"; FILE* file = fopen(pathLog.string().c_str(), "r"); // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes if (file && boost::filesystem::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) { // Restart the file with some of the end std::vector vch(RECENT_DEBUG_HISTORY_SIZE, 0); fseek(file, -((long)vch.size()), SEEK_END); int nBytes = fread(vch.data(), 1, vch.size(), file); fclose(file); file = fopen(pathLog.string().c_str(), "w"); if (file) { fwrite(vch.data(), 1, nBytes, file); fclose(file); } } else if (file != NULL) fclose(file); } #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 runCommand(const std::string& strCommand) { 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 } std::string GetThreadName() { char name[16]; #if defined(PR_GET_NAME) // Only the first 15 characters are used (16 - NUL terminator) ::prctl(PR_GET_NAME, name, 0, 0, 0); #elif defined(MAC_OSX) pthread_getname_np(pthread_self(), name, 16); // #elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) // #else // no get_name here #endif return std::string(name); } 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 { std::locale(""); // Raises a runtime error if current locale is invalid } 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 // boost::filesystem::path, which is then used to explicitly imbue the path. std::locale loc = boost::filesystem::path::imbue(std::locale::classic()); boost::filesystem::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() { #if BOOST_VERSION >= 105600 return boost::thread::physical_concurrency(); #else // Must fall back to hardware_concurrency, which unfortunately counts virtual cores return boost::thread::hardware_concurrency(); #endif } std::string CopyrightHolders(const std::string& strPrefix, unsigned int nStartYear, unsigned int nEndYear) { std::string strCopyrightHolders = strPrefix + strprintf(" %u-%u ", nStartYear, nEndYear) + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION)); // Check for untranslated substitution to make sure Bitcoin Core copyright is not removed by accident if (strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION).find("Bitcoin Core") == std::string::npos) { strCopyrightHolders += "\n" + strPrefix + strprintf(" %u-%u ", 2009, nEndYear) + "The Bitcoin Core developers"; } return strCopyrightHolders; } uint32_t StringVersionToInt(const std::string& strVersion) { std::vector tokens; boost::split(tokens, strVersion, boost::is_any_of(".")); if(tokens.size() != 3) throw std::bad_cast(); uint32_t nVersion = 0; for(unsigned idx = 0; idx < 3; idx++) { if(tokens[idx].length() == 0) throw std::bad_cast(); uint32_t value = boost::lexical_cast(tokens[idx]); if(value > 255) throw std::bad_cast(); nVersion <<= 8; nVersion |= value; } return nVersion; } std::string IntVersionToString(uint32_t nVersion) { if((nVersion >> 24) > 0) // MSB is always 0 throw std::bad_cast(); if(nVersion == 0) throw std::bad_cast(); std::array tokens; for(unsigned idx = 0; idx < 3; idx++) { unsigned shift = (2 - idx) * 8; uint32_t byteValue = (nVersion >> shift) & 0xff; tokens[idx] = boost::lexical_cast(byteValue); } return boost::join(tokens, "."); } std::string SafeIntVersionToString(uint32_t nVersion) { try { return IntVersionToString(nVersion); } catch(const std::bad_cast&) { return "invalid_version"; } }