469 lines
13 KiB
C++
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;
|
|
}
|