Files
thehub/hub/server/util.cpp

469 lines
13 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2009-2010 Satoshi Nakamoto
* Copyright (C) 2009-2015 The Bitcoin Core developers
* Copyright (C) 2017-2018,2024 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(HAVE_CONFIG_H)
#include "config/flowee-config.h"
#endif
#include "util.h"
#include <SettingsDefaults.h>
#include "chainparamsbase.h"
#include "sync.h"
#include "utilstrencodings.h"
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
#include <pthread.h>
#include <pthread_np.h>
#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 <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#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 <io.h> /* for _commit */
#include <shlobj.h>
#endif
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/foreach.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/thread.hpp>
std::map<std::string, std::string> mapArgs;
std::map<std::string, std::vector<std::string> > mapMultiArgs;
/** 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[], const Settings::AllowedArgs& allowedArgs)
{
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);
allowedArgs.checkArg(str.substr(1), strValue);
mapArgs[str] = strValue;
mapMultiArgs[str].push_back(strValue);
}
}
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))
return InterpretBool(mapArgs[strArg]);
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 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 = "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());
}
boost::filesystem::path GetDefaultDataDir()
{
namespace fs = boost::filesystem;
// Windows: C:\Users\Username\AppData\Roaming\flowee
// Mac: ~/Library/Application Support/flowee
// Unix: $XDG_DATA_HOME/flowee (typically $HOME/.local/share/flowee)
std::string dirName = "flowee";
#ifdef WIN32
// Windows
return GetSpecialFolderPath(CSIDL_APPDATA) / dirName;
#else
fs::path pathHome;
char* pszHome = getenv("HOME");
if (pszHome == NULL || strlen(pszHome) == 0)
pathHome = fs::path("/");
else
pathHome = fs::path(pszHome);
#ifdef MAC_OSX
// Mac
return pathHome / "Library/Application Support" / dirName;
#else
fs::path pathDataHome;
char* pszDataHome = getenv("XDG_DATA_HOME");
if (pszDataHome == NULL || strlen(pszDataHome) == 0)
pathDataHome = pathHome / ".local/share";
else
pathDataHome = fs::path(pszDataHome);
return pathDataHome / dirName;
#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 (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(const std::string &filename)
{
namespace fs = boost::filesystem;
const fs::path floweeConfigFile = GetArg("-conf", Settings::hubConfFilename());
auto testValid = [=](fs::path &path, const char *envVar = nullptr, const char *subdir = nullptr) -> bool {
auto filePart = filename;
if (filePart.empty())
filePart = Settings::hubConfFilename();
if (envVar) {
assert(subdir);
const char *p = getenv(envVar);
if (!p || strlen(p) == 0)
return false;
path = fs::path(p);
path /= fs::path(subdir) / filePart;
return fs::exists(path);
}
path /= filePart;
return fs::exists(path);
};
/*
* Asking with an empty filename means we need just the hub general config
* file.
* We try:
* 1. in case of user-set absolute filename, that is what we use.
* 2. check XDG dir
* 3. check $HOME/.config/flowee/ dir
*/
if (filename.empty()) {
assert(!floweeConfigFile.empty());
if (floweeConfigFile.is_absolute() || fs::exists(floweeConfigFile))
return floweeConfigFile;
fs::path path;
if (testValid(path, "XDG_CONFIG_HOME", "flowee"))
return path;
if (testValid(path, "HOME", ".config/flowee"))
return path;
return floweeConfigFile; // doesn't exist, though.
}
/*
* Any config file that is not the hubConfFilename will be tried in
* 1. the effective data directory. (testnet4 subdir, for instance)
* 2. the main datadir (which is the same as 1 in case of mainnet)
* 3. the same dir that the flowee.conf lives in.
* 4. The $XDG_CONFIG_HOME/ dir
* 5. In $HOME/.config/flowee/
*/
auto path = GetDataDir(true);
if (testValid(path))
return path;
path = GetDataDir(false);
if (testValid(path))
return path;
path = floweeConfigFile.parent_path();
if (testValid(path))
return path;
if (testValid(path, "XDG_CONFIG_HOME", "flowee"))
return path;
if (testValid(path, "HOME", ".config/flowee"))
return path;
return floweeConfigFile;
}
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet,
std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet)
{
boost::filesystem::ifstream streamConfig(GetConfigFile());
if (!streamConfig.good())
return; // No flowee.conf file is OK
std::set<std::string> setOptions;
setOptions.insert("*");
Settings::ConfigFile allowedArgs;
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 flowee.conf
std::string strKey = std::string("-") + it->string_key;
std::string strValue = it->value[0];
InterpretNegativeSetting(strKey, strValue);
allowedArgs.checkArg(strKey.substr(1), strValue);
if (mapSettingsRet.count(strKey) == 0)
mapSettingsRet[strKey] = strValue;
mapMultiSettingsRet[strKey].push_back(strValue);
}
// If datadir is changed in .conf file:
ClearDatadirCache();
}
/**
* 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_directories(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;
}
#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 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 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 ChainNameFromCommandLine()
{
int num_selected = 0;
bool fRegTest = GetBoolArg("-regtest", false);
if (fRegTest) num_selected++;
bool fTestNet = GetBoolArg("-testnet", false);
if (fTestNet) num_selected++;
bool fTestNet4 = GetBoolArg("-testnet4", false);
if (fTestNet4) num_selected++;
bool fScaleNet = GetBoolArg("-scalenet", false);
if (fScaleNet) num_selected++;
bool fChipNet = GetBoolArg("-chipnet", false);
if (fChipNet) num_selected++;
if (num_selected > 1)
throw std::runtime_error("Invalid combination of -regtest, -testnet, -testnet4, -scalenet and -chipnet.");
if (fRegTest)
return CBaseChainParams::REGTEST;
if (fTestNet)
return CBaseChainParams::TESTNET;
if (fTestNet4)
return CBaseChainParams::TESTNET4;
if (fScaleNet)
return CBaseChainParams::SCALENET;
if (fChipNet)
return CBaseChainParams::CHIPNET;
return CBaseChainParams::MAIN;
}