// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Copyright (c) 2016-2024 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "util.h" #include "chainparamsbase.h" #include "random.h" #include "serialize.h" #include "sync.h" #include "util/strencodings.h" #include "utiltime.h" #include "hush_defs.h" #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 #include // for to_lower() #include #include // for startswith() and endswith() #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 using namespace std; map mapArgs; map > mapMultiArgs; bool fDebug = false; bool fRandomXDebug = false; bool fZdebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; bool fDaemon = false; bool fServer = false; string strMiscWarning; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = DEFAULT_LOGIPS; std::atomic fReopenDebugLog(false); CTranslationInterface translationInterface; /** * 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 list *vMsgsBeforeOpenLog; [[noreturn]] void new_handler_terminate() { // Rather than throwing std::bad-alloc if allocation fails, terminate // immediately to (try to) avoid chain corruption. // Since LogPrintf may itself allocate memory, set the handler directly // to terminate first. std::set_new_handler(std::terminate); fputs("Error: Out of memory. Terminating.\n", stderr); LogPrintf("Error: Out of memory. Terminating.\n"); // The log was successful, terminate now. std::terminate(); }; 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 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) { if (!fDebug) return false; // 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 (ptrCategory.get() == NULL) { const vector& categories = mapMultiArgs["-debug"]; ptrCategory.reset(new set(categories.begin(), categories.end())); // thread_specific_ptr automatically deletes the set when the thread ends. } const set& setCategories = *ptrCategory.get(); // if not debugging everything and not debugging specific category, LogPrint does nothing. if (setCategories.count(string("")) == 0 && setCategories.count(string("1")) == 0 && setCategories.count(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 it, in the calling context. */ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine) { string strStamped; if (!fLogTimestamps) return str; if (*fStartedNewLine) strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + 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 bool fStartedNewLine = true; if (fPrintToConsole) { // print to console ret = fwrite(str.data(), 1, str.size(), stdout); fflush(stdout); } else if (fPrintToDebugLog) { boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); string strTimestamped = LogTimestampStr(str, &fStartedNewLine); // 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; } static void InterpretNegativeSetting(string name, map& mapSettingsRet) { // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set if (name.find("-no") == 0) { std::string positive("-"); positive.append(name.begin()+3, name.end()); if (mapSettingsRet.count(positive) == 0) { bool value = !GetBoolArg(name, false); mapSettingsRet[positive] = (value ? "1" : "0"); } } } void ParseParameters(int argc, const char* const argv[]) { 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); mapArgs[str] = strValue; mapMultiArgs[str].push_back(strValue); } // New 0.6 features: BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs) { // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set InterpretNegativeSetting(entry.first, mapArgs); } } // split string using by space or comma as a delimiter char void SplitStr(const std::string& strVal, std::vector &outVals) { stringstream ss(strVal); while (!ss.eof()) { int c; std::string str; while (std::isspace(ss.peek())) ss.ignore(); while ((c = ss.get()) != EOF && !std::isspace(c) && c != ',') str += c; if (!str.empty()) outVals.push_back(str); } } void Split(const std::string& strVal, int32_t outsize, uint64_t *outVals, const uint64_t nDefault) { stringstream ss(strVal); vector vec; uint64_t i, nLast, numVals = 0; while ( ss.peek() == ' ' ) ss.ignore(); while ( ss >> i ) { outVals[numVals] = i; numVals += 1; while ( ss.peek() == ' ' ) ss.ignore(); if ( ss.peek() == ',' ) ss.ignore(); while ( ss.peek() == ' ' ) ss.ignore(); } if ( numVals > 0 ) nLast = outVals[numVals - 1]; else nLast = nDefault; for ( i = numVals; i < outsize; i++ ) { outVals[i] = nLast; } } std::string GetArg(const std::string& strArg, const std::string& strDefault) { if (mapArgs.count(strArg)) return mapArgs[strArg]; return strDefault; } int64_t GetArg(const std::string& strArg, int64_t nDefault) { if (mapArgs.count(strArg)) return atoi64(mapArgs[strArg]); return nDefault; } bool GetBoolArg(const std::string& strArg, bool fDefault) { if (mapArgs.count(strArg)) { if (mapArgs[strArg].empty()) return true; return (atoi(mapArgs[strArg]) != 0); } return fDefault; } bool SoftSetArg(const std::string& strArg, const std::string& strValue) { if (mapArgs.count(strArg)) return false; mapArgs[strArg] = strValue; return true; } bool SoftSetBoolArg(const std::string& strArg, bool fValue) { if (fValue) return SoftSetArg(strArg, std::string("1")); else return SoftSetArg(strArg, std::string("0")); } static 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 = "Hush"; #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()); strMiscWarning = message; } extern char SMART_CHAIN_SYMBOL[HUSH_SMART_CHAIN_MAXLEN]; //int64_t MAX_MONEY = 200000000 * 100000000LL; boost::filesystem::path GetDefaultDataDir() { namespace fs = boost::filesystem; char symbol[HUSH_SMART_CHAIN_MAXLEN]; if ( SMART_CHAIN_SYMBOL[0] != 0 ) strcpy(symbol,SMART_CHAIN_SYMBOL); else symbol[0] = 0; // OLD NAMES: // Windows < Vista: C:\Documents and Settings\Username\Application Data\Komodo // Windows >= Vista: C:\Users\Username\AppData\Roaming\Komodo // Mac: ~/Library/Application Support/Komodo // Unix: ~/.komodo // NEW NAMES: // Windows < Vista: C:\Documents and Settings\Username\Application Data\Hush // Windows >= Vista: C:\Users\Username\AppData\Roaming\Hush // Mac: ~/Library/Application Support/Hush // Unix: ~/.hush // ~/.hush was actually used by the original 1.x version of Hush, but we will // only make subdirectories inside of it, so we won't be able to overwrite // an old wallet.dat from the Ice Ages :) fs::path pathRet; #ifdef _WIN32 // Windows pathRet = GetSpecialFolderPath(CSIDL_APPDATA) / "Hush" / symbol; // Always use .hush/HUSH3, if it exists (even if .komodo/HUSH3 exists) if(fs::is_directory(pathRet)) { return pathRet; } else { pathRet = GetSpecialFolderPath(CSIDL_APPDATA) / "Komodo" / symbol; if(fs::is_directory(pathRet)) { // existing legacy directory, use that for backward compat return pathRet; } else { // For new clones, use Hush/ACNAME pathRet = GetSpecialFolderPath(CSIDL_APPDATA) / "Hush" / symbol; return pathRet; } } return pathRet; #else char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) pathRet = fs::path("/"); else pathRet = fs::path(pszHome); #ifdef MAC_OSX // Mac pathRet /= "Library/Application Support"; TryCreateDirectory(pathRet); fs::path tmppath; tmppath = pathRet; tmppath /= "Hush"; // create Library/Application Support/Hush if it doesn't exist TryCreateDirectory(tmppath); // Always use Hush/HUSH3 if it exists if(fs::is_directory(tmppath / symbol)) { return tmppath / symbol; } else { // Check for legacy dir tmppath = pathRet; tmppath /= "Komodo"; //TryCreateDirectory(tmppath); if(fs::is_directory( tmppath / symbol)) { // Found legacy dir, use that return tmppath / symbol; } else { // For new clones, use Hush/ACNAME tmppath = pathRet / "Hush" / symbol; } return tmppath; } #else // Unix // New directory :) fs::path tmppath = pathRet / ".hush" / symbol; // Always use .hush/HUSH3, if it exists (even if .komodo/HUSH3 exists) if(fs::is_directory(tmppath)) { return tmppath; } else { // This is the legacy location tmppath = pathRet / ".komodo" / symbol; if(fs::is_directory(tmppath)) { // existing legacy directory, use that for backward compat return tmppath; } else { // For new clones, use .hush/ACNAME tmppath = pathRet / ".hush" / symbol; } return tmppath; } #endif #endif } static boost::filesystem::path pathCached; static boost::filesystem::path pathCachedNetSpecific; static boost::filesystem::path zc_paramsPathCached; static CCriticalSection csPathCached; static boost::filesystem::path ZC_GetBaseParamsDir() { // Copied from GetDefaultDataDir and adapted for zcash params. namespace fs = boost::filesystem; // Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams // Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams // Mac: ~/Library/Application Support/ZcashParams // Unix: ~/.zcash-params // Debian packages: /usr/share/hush fs::path pathRet; #ifdef _WIN32 return GetSpecialFolderPath(CSIDL_APPDATA) / "ZcashParams"; #else char* pszHome = getenv("HOME"); if (pszHome == NULL || strlen(pszHome) == 0) pathRet = fs::path("/"); else pathRet = fs::path(pszHome); #ifdef MAC_OSX // Mac pathRet /= "Library/Application Support"; TryCreateDirectory(pathRet); return pathRet / "ZcashParams"; #else // Unixy systems. Debian packages install params system-wide if (fs::exists("/usr/share/hush")) { pathRet = fs::path("/usr/share/hush"); return pathRet; } else { return pathRet / ".zcash-params"; } #endif #endif } const boost::filesystem::path &ZC_GetParamsDir() { namespace fs = boost::filesystem; LOCK(csPathCached); // Reuse the same lock as upstream. fs::path &path = zc_paramsPathCached; // 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; path = ZC_GetBaseParamsDir(); return path; } // Return the user specified export directory. Create directory if it doesn't exist. // If user did not set option, return an empty path. // If there is a filesystem problem, throw an exception. const boost::filesystem::path GetExportDir() { namespace fs = boost::filesystem; fs::path path; if (mapArgs.count("-exportdir")) { path = fs::system_complete(mapArgs["-exportdir"]); if (fs::exists(path) && !fs::is_directory(path)) { throw std::runtime_error(strprintf("The -exportdir '%s' already exists and is not a directory", path.string())); } if (!fs::exists(path) && !fs::create_directories(path)) { throw std::runtime_error(strprintf("Failed to create directory at -exportdir '%s'", path.string())); } } return path; } const boost::filesystem::path &GetDataDir(bool fNetSpecific) { namespace fs = boost::filesystem; LOCK(csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; // This can be called during exceptions by LogPrintf(), so we cache the // value so we don't have to do memory allocations after that. if (!path.empty()) return path; if (mapArgs.count("-datadir")) { path = fs::system_complete(mapArgs["-datadir"]); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDefaultDataDir(); } if (fNetSpecific) path /= BaseParams().DataDir(); fs::create_directories(path); return path; } void ClearDatadirCache() { pathCached = boost::filesystem::path(); pathCachedNetSpecific = boost::filesystem::path(); } boost::filesystem::path GetConfigFile() { char confname[512]; if ( SMART_CHAIN_SYMBOL[0] != 0 ) { sprintf(confname,"%s.conf",SMART_CHAIN_SYMBOL); } else { strcpy(confname,"HUSH3.conf"); } boost::filesystem::path pathConfigFile(GetArg("-conf",confname)); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; return pathConfigFile; } void ReadConfigFile(map& mapSettingsRet, map >& mapMultiSettingsRet) { boost::filesystem::ifstream streamConfig(GetConfigFile()); if (!streamConfig.good()) throw missing_hush_conf(); 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 HUSH3.conf string strKey = string("-") + it->string_key; if (mapSettingsRet.count(strKey) == 0) { mapSettingsRet[strKey] = it->value[0]; // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set) InterpretNegativeSetting(strKey, mapSettingsRet); } mapMultiSettingsRet[strKey].push_back(it->value[0]); } // If datadir is changed in .conf file: ClearDatadirCache(); extern uint16_t BITCOIND_RPCPORT; BITCOIND_RPCPORT = GetArg("-rpcport",BaseParams().RPCPort()); } #ifndef _WIN32 boost::filesystem::path GetPidFile() { boost::filesystem::path pathPidFile(GetArg("-pid", "hushd.pid")); 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(stderr,"Creating pid file %s\n", path.string().c_str()); fprintf(file, "%d\n", pid); fclose(file); } else { fprintf(stderr,"Unable to make a pidfile %s !\n", path.string().c_str()); exit(1); } } #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 *fileout) { fflush(fileout); // harmless if redundantly called #ifdef _WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(fileout)); FlushFileBuffers(hFile); #else #if defined(__linux__) || defined(__NetBSD__) fdatasync(fileno(fileout)); #elif defined(__APPLE__) && defined(F_FULLFSYNC) fcntl(fileno(fileout), F_FULLFSYNC, 0); #else fsync(fileno(fileout)); #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() { // Scroll debug.log if it's getting too big boost::filesystem::path pathLog = GetDataDir() / "debug.log"; FILE* file = fopen(pathLog.string().c_str(), "r"); int maxlogsize = GetArg("-maxdebugfilesize", 15); unsigned int MAX_DEBUG_LOG_SIZE = maxlogsize*(1024*1024); // convert to MB if (file && boost::filesystem::file_size(pathLog) > MAX_DEBUG_LOG_SIZE ) { fprintf(stderr,"Shrinking %s to be at most %d bytes\n", pathLog.string().c_str(), MAX_DEBUG_LOG_SIZE ); // Restart the file with some of the end std::vector vch(200000,0); fseek(file, -((long)vch.size()), SEEK_END); int nBytes = fread(begin_ptr(vch), 1, vch.size(), file); fclose(file); file = fopen(pathLog.string().c_str(), "w"); if (file) { fwrite(begin_ptr(vch), 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 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)) { LogPrintf("GetTempPath(): failed to find temp path\n"); return boost::filesystem::path(""); } return 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 } void SetupEnvironment() { // 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; } void SetThreadPriority(int nPriority) { #ifdef _WIN32 SetThreadPriority(GetCurrentThread(), nPriority); #else // _WIN32 #ifdef PRIO_THREAD setpriority(PRIO_THREAD, 0, nPriority); #else // PRIO_THREAD setpriority(PRIO_PROCESS, 0, nPriority); #endif // PRIO_THREAD #endif // _WIN32 } std::string PrivacyInfo() { return "\n" + FormatParagraph(strprintf(_("In order to ensure you are adequately protecting your privacy when using Hush, please see <%s>."), "https://hush.is/security/")) + "\n"; } std::string LicenseInfo() { return "\n" + FormatParagraph(strprintf(_("Copyright (C) 2016-%i Duke Leto and The Hush Developers"), COPYRIGHT_YEAR)) + "\n" + "\n" + FormatParagraph(strprintf(_("Copyright (C) 2016-2020 jl777 and SuperNET developers"))) + "\n" + "\n" + FormatParagraph(strprintf(_("Copyright (C) 2016-2018 The Zcash developers"))) + "\n" + "\n" + FormatParagraph(strprintf(_("Copyright (C) 2009-2014 The Bitcoin Core developers"))) + "\n" + "\n" + FormatParagraph(_("This is experimental Free Software! Fuck Yeah!!!!!")) + "\n" + "\n" + FormatParagraph(_("Distributed under the GPLv3 software license, see the accompanying file COPYING or .")) + "\n" + "\n"; } int GetNumCores() { return boost::thread::physical_concurrency(); }