// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #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 #include #include #include #include #include #include #include // 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"; const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; ArgsManager gArgs; bool fPrintToConsole = false; bool fPrintToDebugLog = true; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = DEFAULT_LOGIPS; std::atomic fReopenDebugLog(false); CTranslationInterface translationInterface; /** Log categories bitfield. */ std::atomic logCategories(0); /** Init OpenSSL library multithreading support */ 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]); } else { LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]); } } // Singleton for wrapping OpenSSL setup/teardown. class CInit { public: CInit() { // Init OpenSSL library multithreading support 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); // Clear the set of locks now to maintain symmetry with the constructor. ppmutexOpenSSL.reset(); } } 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 std::once_flag debugPrintInitFlag; /** * We use std::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 = nullptr; static std::mutex* mutexDebugLog = nullptr; 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 == nullptr); mutexDebugLog = new std::mutex(); vMsgsBeforeOpenLog = new std::list; } fs::path GetDebugLogPath() { fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); return AbsPathForConfigVal(logfile); } bool OpenDebugLog() { std::call_once(debugPrintInitFlag, &DebugPrintInit); std::lock_guard scoped_lock(*mutexDebugLog); assert(fileout == nullptr); assert(vMsgsBeforeOpenLog); fs::path pathDebug = GetDebugLogPath(); fileout = fsbridge::fopen(pathDebug, "a"); if (!fileout) { return false; } setbuf(fileout, nullptr); // unbuffered // dump buffered messages from before we opened the log while (!vMsgsBeforeOpenLog->empty()) { FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); vMsgsBeforeOpenLog->pop_front(); } delete vMsgsBeforeOpenLog; vMsgsBeforeOpenLog = nullptr; return true; } struct CLogCategoryDesc { uint32_t flag; std::string category; }; const CLogCategoryDesc LogCategories[] = { {BCLog::NONE, "0"}, {BCLog::NONE, "none"}, {BCLog::NET, "net"}, {BCLog::TOR, "tor"}, {BCLog::MEMPOOL, "mempool"}, {BCLog::HTTP, "http"}, {BCLog::BENCH, "bench"}, {BCLog::ZMQ, "zmq"}, {BCLog::DB, "db"}, {BCLog::RPC, "rpc"}, {BCLog::ESTIMATEFEE, "estimatefee"}, {BCLog::ADDRMAN, "addrman"}, {BCLog::SELECTCOINS, "selectcoins"}, {BCLog::REINDEX, "reindex"}, {BCLog::CMPCTBLOCK, "cmpctblock"}, {BCLog::RAND, "rand"}, {BCLog::PRUNE, "prune"}, {BCLog::PROXY, "proxy"}, {BCLog::MEMPOOLREJ, "mempoolrej"}, {BCLog::LIBEVENT, "libevent"}, {BCLog::COINDB, "coindb"}, {BCLog::QT, "qt"}, {BCLog::LEVELDB, "leveldb"}, {BCLog::ALL, "1"}, {BCLog::ALL, "all"}, }; bool GetLogCategory(uint32_t *f, const std::string *str) { if (f && str) { if (*str == "") { *f = BCLog::ALL; return true; } for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { if (LogCategories[i].category == *str) { *f = LogCategories[i].flag; return true; } } } return false; } std::string ListLogCategories() { std::string ret; int outcount = 0; for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { // Omit the special cases. if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { if (outcount != 0) ret += ", "; ret += LogCategories[i].category; outcount++; } } return ret; } std::vector ListActiveLogCategories() { std::vector ret; for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { // Omit the special cases. if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { CLogCategoryActive catActive; catActive.category = LogCategories[i].category; catActive.active = LogAcceptCategory(LogCategories[i].flag); ret.push_back(catActive); } } return ret; } /** * 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 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 = GetTimeMicros(); strStamped = FormatISO8601DateTime(nTimeMicros/1000000); if (fLogTimeMicros) { strStamped.pop_back(); strStamped += strprintf(".%06dZ", nTimeMicros%1000000); } int64_t mocktime = GetMockTime(); if (mocktime) { strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; } strStamped += ' ' + str; } else strStamped = str; if (!str.empty() && str[str.size()-1] == '\n') *fStartedNewLine = true; else *fStartedNewLine = false; return strStamped; } int LogPrintStr(const std::string &str) { int ret = 0; // Returns total number of characters written static std::atomic_bool fStartedNewLine(true); std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); if (fPrintToConsole) { // print to console ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } else if (fPrintToDebugLog) { std::call_once(debugPrintInitFlag, &DebugPrintInit); std::lock_guard scoped_lock(*mutexDebugLog); // buffer if we haven't opened the log yet if (fileout == nullptr) { assert(vMsgsBeforeOpenLog); ret = strTimestamped.length(); vMsgsBeforeOpenLog->push_back(strTimestamped); } else { // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; fs::path pathDebug = GetDebugLogPath(); if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) setbuf(fileout, nullptr); // unbuffered } ret = FileWriteStr(strTimestamped, fileout); } } return ret; } /** 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 = MakeUnique(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); } /** * Interpret -nofoo as if the user supplied -foo=0. * * This method also tracks when the -no form was supplied, and treats "-foo" as * a negated option when this happens. This 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). */ void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val) { if (key.substr(0, 3) == "-no") { bool bool_val = InterpretBool(val); if (!bool_val ) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val); } key.erase(1, 2); m_negated_args.insert(key); val = bool_val ? "0" : "1"; } else { // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo // as negated when we see the second option. m_negated_args.erase(key); } } void ArgsManager::ParseParameters(int argc, const char* const argv[]) { LOCK(cs_args); mapArgs.clear(); mapMultiArgs.clear(); m_negated_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); // Transform -nofoo to -foo=0 InterpretNegatedOption(key, val); mapArgs[key] = val; mapMultiArgs[key].push_back(val); } } std::vector ArgsManager::GetArgs(const std::string& strArg) const { LOCK(cs_args); auto it = mapMultiArgs.find(strArg); if (it != mapMultiArgs.end()) return it->second; return {}; } bool ArgsManager::IsArgSet(const std::string& strArg) const { LOCK(cs_args); return mapArgs.count(strArg); } bool ArgsManager::IsArgNegated(const std::string& strArg) const { LOCK(cs_args); return m_negated_args.find(strArg) != m_negated_args.end(); } std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) return it->second; return strDefault; } int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) return atoi64(it->second); return nDefault; } bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) return InterpretBool(it->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); mapArgs[strArg] = strValue; mapMultiArgs[strArg] = {strValue}; } 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 g_blocks_path_cached; static fs::path g_blocks_path_cache_net_specific; static fs::path pathCached; static fs::path pathCachedNetSpecific; static CCriticalSection csPathCached; const fs::path &GetBlocksDir(bool fNetSpecific) { LOCK(csPathCached); fs::path &path = fNetSpecific ? g_blocks_path_cache_net_specific : g_blocks_path_cached; // 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("-blocksdir")) { path = fs::system_complete(gArgs.GetArg("-blocksdir", "")); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDataDir(false); } if (fNetSpecific) path /= BaseParams().DataDir(); path /= "blocks"; fs::create_directories(path); return path; } 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(); g_blocks_path_cached = fs::path(); g_blocks_path_cache_net_specific = fs::path(); } fs::path GetConfigFile(const std::string& confPath) { return AbsPathForConfigVal(fs::path(confPath), false); } void ArgsManager::ReadConfigFile(const std::string& confPath) { fs::ifstream streamConfig(GetConfigFile(confPath)); if (!streamConfig.good()) return; // No bitcoin.conf file is OK { 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 bitcoin.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; InterpretNegatedOption(strKey, strValue); if (mapArgs.count(strKey) == 0) mapArgs[strKey] = strValue; mapMultiArgs[strKey].push_back(strValue); } } // 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 = GetBoolArg("-regtest", false); bool fTestNet = GetBoolArg("-testnet", false); 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() { return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME))); } 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_directories 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 fs::path pathLog = GetDebugLogPath(); FILE* file = fsbridge::fopen(pathLog, "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 && fs::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 = fsbridge::fopen(pathLog, "w"); if (file) { fwrite(vch.data(), 1, nBytes, file); fclose(file); } } else if (file != nullptr) fclose(file); } #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 { 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 // 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 std::thread::hardware_concurrency(); } std::string CopyrightHolders(const std::string& strPrefix) { std::string strCopyrightHolders = strPrefix + 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 + "The Bitcoin Core developers"; } return strCopyrightHolders; } // Obtain the application startup time (used for uptime calculation) int64_t GetStartupTime() { return nStartupTime; } fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) { return fs::absolute(path, GetDataDir(net_specific)); }