diff options
Diffstat (limited to 'src/util.cpp')
-rw-r--r-- | src/util.cpp | 300 |
1 files changed, 225 insertions, 75 deletions
diff --git a/src/util.cpp b/src/util.cpp index 8b08827d73..2f1f522a61 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -3,12 +3,23 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef WIN32 +// for posix_fallocate +#ifdef __linux__ +#define _POSIX_C_SOURCE 200112L +#endif +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/resource.h> +#endif + #include "util.h" #include "sync.h" -#include "strlcpy.h" #include "version.h" #include "ui_interface.h" #include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/case_conv.hpp> // for to_lower() +#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() // 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 @@ -63,8 +74,6 @@ bool fDebug = false; bool fDebugNet = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; -bool fRequestShutdown = false; -bool fShutdown = false; bool fDaemon = false; bool fServer = false; bool fCommandLine = false; @@ -73,7 +82,8 @@ bool fTestNet = false; bool fNoListen = false; bool fLogTimestamps = false; CMedianFilter<int64> vTimeOffsets(200,0); -bool fReopenDebugLog = false; +volatile bool fReopenDebugLog = false; +bool fCachedPath[2] = {false, false}; // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; @@ -155,7 +165,7 @@ void RandAddSeedPerfmon() if (ret == ERROR_SUCCESS) { RAND_add(pdata, nSize, nSize/100.0); - memset(pdata, 0, nSize); + OPENSSL_cleanse(pdata, nSize); printf("RandAddSeed() %lu bytes\n", nSize); } #endif @@ -194,62 +204,76 @@ uint256 GetRandHash() +// +// OutputDebugStringF (aka printf -- there is a #define that we really +// should get rid of one day) 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 these are initialized in +// in a thread-safe manner the first time it is called: +static FILE* fileout = NULL; +static boost::mutex* mutexDebugLog = NULL; + +static void DebugPrintInit() +{ + assert(fileout == NULL); + assert(mutexDebugLog == NULL); -inline int OutputDebugStringF(const char* pszFormat, ...) + boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fileout = fopen(pathDebug.string().c_str(), "a"); + if (fileout) setbuf(fileout, NULL); // unbuffered + + mutexDebugLog = new boost::mutex(); +} + +int OutputDebugStringF(const char* pszFormat, ...) { - int ret = 0; + int ret = 0; // Returns total number of characters written if (fPrintToConsole) { // print to console va_list arg_ptr; va_start(arg_ptr, pszFormat); - ret = vprintf(pszFormat, arg_ptr); + ret += vprintf(pszFormat, arg_ptr); va_end(arg_ptr); } else if (!fPrintToDebugger) { - // print to debug.log - static FILE* fileout = NULL; + static bool fStartedNewLine = true; + boost::call_once(&DebugPrintInit, debugPrintInitFlag); - if (!fileout) - { + if (fileout == NULL) + return ret; + + boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); + + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; - fileout = fopen(pathDebug.string().c_str(), "a"); - if (fileout) setbuf(fileout, NULL); // unbuffered + if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) + setbuf(fileout, NULL); // unbuffered } - if (fileout) - { - static bool fStartedNewLine = true; - - // This routine may be called by global destructors during shutdown. - // Since the order of destruction of static/global objects is undefined, - // allocate mutexDebugLog on the heap the first time this routine - // is called to avoid crashes during shutdown. - static boost::mutex* mutexDebugLog = NULL; - if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex(); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - - // 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 - } - // Debug print useful for profiling - if (fLogTimestamps && fStartedNewLine) - fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - if (pszFormat[strlen(pszFormat) - 1] == '\n') - fStartedNewLine = true; - else - fStartedNewLine = false; + // Debug print useful for profiling + if (fLogTimestamps && fStartedNewLine) + ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); + if (pszFormat[strlen(pszFormat) - 1] == '\n') + fStartedNewLine = true; + else + fStartedNewLine = false; - va_list arg_ptr; - va_start(arg_ptr, pszFormat); - ret = vfprintf(fileout, pszFormat, arg_ptr); - va_end(arg_ptr); - } + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + ret += vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); } #ifdef WIN32 @@ -272,6 +296,7 @@ inline int OutputDebugStringF(const char* pszFormat, ...) { OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str()); line_start = line_end + 1; + ret += line_end-line_start; } buffer.erase(0, line_start); } @@ -493,7 +518,7 @@ static void InterpretNegativeSetting(string name, map<string, string>& mapSettin positive.append(name.begin()+3, name.end()); if (mapSettingsRet.count(positive) == 0) { - bool value = !GetBoolArg(name); + bool value = !GetBoolArg(name, false); mapSettingsRet[positive] = (value ? "1" : "0"); } } @@ -505,24 +530,24 @@ void ParseParameters(int argc, const char* const argv[]) mapMultiArgs.clear(); for (int i = 1; i < argc; i++) { - char psz[10000]; - strlcpy(psz, argv[i], sizeof(psz)); - char* pszValue = (char*)""; - if (strchr(psz, '=')) + std::string str(argv[i]); + std::string strValue; + size_t is_index = str.find('='); + if (is_index != std::string::npos) { - pszValue = strchr(psz, '='); - *pszValue++ = '\0'; + strValue = str.substr(is_index+1); + str = str.substr(0, is_index); } - #ifdef WIN32 - _strlwr(psz); - if (psz[0] == '/') - psz[0] = '-'; - #endif - if (psz[0] != '-') +#ifdef WIN32 + boost::to_lower(str); + if (boost::algorithm::starts_with(str, "/")) + str = "-" + str.substr(1); +#endif + if (str[0] != '-') break; - mapArgs[psz] = pszValue; - mapMultiArgs[psz].push_back(pszValue); + mapArgs[str] = strValue; + mapMultiArgs[str].push_back(strValue); } // New 0.6 features: @@ -1024,13 +1049,12 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) static fs::path pathCached[2]; static CCriticalSection csPathCached; - static bool cachedPath[2] = {false, false}; fs::path &path = pathCached[fNetSpecific]; // This can be called during exceptions by printf, so we cache the // value so we don't have to do memory allocations after that. - if (cachedPath[fNetSpecific]) + if (fCachedPath[fNetSpecific]) return path; LOCK(csPathCached); @@ -1047,9 +1071,9 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) if (fNetSpecific && GetBoolArg("-testnet", false)) path /= "testnet3"; - fs::create_directory(path); + fs::create_directories(path); - cachedPath[fNetSpecific]=true; + fCachedPath[fNetSpecific] = true; return path; } @@ -1067,6 +1091,9 @@ void ReadConfigFile(map<string, string>& mapSettingsRet, if (!streamConfig.good()) return; // No bitcoin.conf file is OK + // clear path cache after loading config file + fCachedPath[0] = fCachedPath[1] = false; + set<string> setOptions; setOptions.insert("*"); @@ -1118,7 +1145,11 @@ void FileCommit(FILE *fileout) #ifdef WIN32 _commit(_fileno(fileout)); #else + #if defined(__linux__) || defined(__NetBSD__) + fdatasync(fileno(fileout)); + #else fsync(fileno(fileout)); + #endif #endif } @@ -1132,6 +1163,79 @@ int GetFilesize(FILE* file) return nFilesize; } +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 nEndPos = (int64)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() { // Scroll debug.log if it's getting too big @@ -1152,6 +1256,8 @@ void ShrinkDebugFile() fclose(file); } } + else if (file != NULL) + fclose(file); } @@ -1184,9 +1290,14 @@ void SetMockTime(int64 nMockTimeIn) static int64 nTimeOffset = 0; +int64 GetTimeOffset() +{ + return nTimeOffset; +} + int64 GetAdjustedTime() { - return GetTime() + nTimeOffset; + return GetTime() + GetTimeOffset(); } void AddTimeData(const CNetAddr& ip, int64 nTime) @@ -1229,7 +1340,7 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly."); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } } @@ -1242,12 +1353,26 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) } } - - - - - - +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + //The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) + { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do { + RAND_bytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x9068ffffU); + insecure_rand_Rz = tmp; + do { + RAND_bytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x464fffffU); + insecure_rand_Rw = tmp; + } +} string FormatVersion(int nVersion) { @@ -1291,6 +1416,28 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) } #endif +boost::filesystem::path GetTempPath() { +#if BOOST_FILESYSTEM_VERSION == 3 + return boost::filesystem::temp_directory_path(); +#else + // TODO: remove when we don't support filesystem v2 anymore + boost::filesystem::path path; +#ifdef WIN32 + char pszPath[MAX_PATH] = ""; + + if (GetTempPathA(MAX_PATH, pszPath)) + path = boost::filesystem::path(pszPath); +#else + path = boost::filesystem::path("/tmp"); +#endif + if (path.empty() || !boost::filesystem::is_directory(path)) { + printf("GetTempPath(): failed to find temp path\n"); + return boost::filesystem::path(""); + } + return path; +#endif +} + void runCommand(std::string strCommand) { int nErr = ::system(strCommand.c_str()); @@ -1309,9 +1456,12 @@ void RenameThread(const char* name) // removed. pthread_set_name_np(pthread_self(), name); -// This is XCode 10.6-and-later; bring back if we drop 10.5 support: -// #elif defined(MAC_OSX) -// pthread_setname_np(name); +#elif defined(MAC_OSX) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +// pthread_setname_np is XCode 10.6-and-later +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + pthread_setname_np(name); +#endif #else // Prevent warnings for unused parameters... |