Files
thehub/libs/server/util.cpp
T
TomZ 175096b2bd Refactor: move files
Move some files back to the server "library".
Merge the 'console' lib with server, as it doesn't really make sense with
just one file and nobody exclusively linking to it.

The server "libary" is not really a library, its the place we put all
the files shared by hub-qt hub-cli and hub.
We no longer depend on these files from other places (mostly due to
moving to the new logging framework) and as such we can move the files
back.
2019-11-13 19:09:24 +01:00

432 lines
12 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 Tom Zander <tomz@freedommail.ch>
*
* 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 "serialize.h"
#include "sync.h"
#include "utilstrencodings.h"
#include "utiltime.h"
#include <cstdarg>
#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 <algorithm>
#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;
fs::path pathConfigFile = filename;
if (filename.empty()) { // its the global config file.
pathConfigFile = GetArg("-conf", Settings::hubConfFilename());
if (pathConfigFile.is_complete())
return pathConfigFile;
}
#if defined(WIN32) || defined(MAC_OSX)
// Windows and Mac
return GetDataDir(true) / pathConfigFile;
#else
// Unix. First check datadir
fs::path pathConfInDatadir = GetDataDir(true) / pathConfigFile;
if (fs::exists(pathConfInDatadir))
return pathConfInDatadir;
// then check user-specific config dir
fs::path pathConfigHome;
char* pszConfigHome = getenv("XDG_CONFIG_HOME");
if (pszConfigHome == nullptr || strlen(pszConfigHome) == 0) {
fs::path pathHome;
char* pszHome = getenv("HOME");
if (pszHome == nullptr || strlen(pszHome) == 0)
pathHome = fs::path("/");
else
pathHome = fs::path(pszHome);
pathConfigHome = pathHome / ".config";
if (!fs::exists(pathConfigHome)) // fallback 3, $HOME/$filename
return pathHome / pathConfigFile;
} else {
pathConfigHome = fs::path(pszConfigHome);
}
return pathConfigHome / "flowee" / BaseParams().DataDir() / pathConfigFile;
#endif
}
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()
{
bool fRegTest = GetBoolArg("-regtest", false);
bool fTestNet = GetBoolArg("-testnet", false);
if (fTestNet && fRegTest)
throw std::runtime_error("Invalid combination of -regtest and/or -testnet.");
if (fRegTest)
return CBaseChainParams::REGTEST;
if (fTestNet)
return CBaseChainParams::TESTNET;
return CBaseChainParams::MAIN;
}