Page MenuHomePhabricator

util.cpp
No OneTemporary

util.cpp

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <util.h>
#include <chainparamsbase.h>
#include <fs.h>
#include <random.h>
#include <serialize.h>
#include <utilstrencodings.h>
#include <utiltime.h>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
#include <openssl/conf.h>
#include <openssl/rand.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 <codecvt>
#include <io.h> /* for _commit */
#include <shlobj.h>
#endif
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
const char *const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char *const BITCOIN_PID_FILENAME = "bitcoind.pid";
ArgsManager gArgs;
CTranslationInterface translationInterface;
/** Init OpenSSL library multithreading support */
static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
void locking_callback(int mode, int i, const char *file,
int line) NO_THREAD_SAFETY_ANALYSIS {
if (mode & CRYPTO_LOCK) {
ENTER_CRITICAL_SECTION(ppmutexOpenSSL[i]);
} else {
LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]);
}
}
// Singleton for wrapping OpenSSL setup/teardown.
class CInit {
public:
CInit() {
// Init OpenSSL library multithreading support.
ppmutexOpenSSL.reset(new CCriticalSection[CRYPTO_num_locks()]);
CRYPTO_set_locking_callback(locking_callback);
// OpenSSL can optionally load a config file which lists optional
// loadable modules and engines. We don't use them so we don't require
// the config. However some of our libs may call functions which attempt
// to load the config file, possibly resulting in an exit() or crash if
// it is missing or corrupt. Explicitly tell OpenSSL not to try to load
// the file. The result for our libs will be that the config appears to
// have been loaded and there are no modules/engines available.
OPENSSL_no_config();
#ifdef WIN32
// Seed OpenSSL PRNG with current contents of the screen.
RAND_screen();
#endif
// Seed OpenSSL PRNG with performance counter.
RandAddSeed();
}
~CInit() {
// Securely erase the memory used by the PRNG.
RAND_cleanup();
// Shutdown OpenSSL library multithreading support.
CRYPTO_set_locking_callback(nullptr);
// Clear the set of locks now to maintain symmetry with the constructor.
ppmutexOpenSSL.reset();
}
} instance_of_cinit;
/**
* A map that contains all the currently held directory locks. After successful
* locking, these will be held here until the global destructor cleans them up
* and thus automatically unlocks them, or ReleaseDirectoryLocks is called.
*/
static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>>
dir_locks;
/** Mutex to protect dir_locks. */
static std::mutex cs_dir_locks;
bool LockDirectory(const fs::path &directory, const std::string lockfile_name,
bool probe_only) {
std::lock_guard<std::mutex> ulock(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
// If a lock for this directory already exists in the map, don't try to
// re-lock it
if (dir_locks.count(pathLockFile.string())) {
return true;
}
// Create empty lock file if it doesn't exist.
FILE *file = fsbridge::fopen(pathLockFile, "a");
if (file) {
fclose(file);
}
try {
auto lock = std::make_unique<boost::interprocess::file_lock>(
pathLockFile.string().c_str());
if (!lock->try_lock()) {
return false;
}
if (!probe_only) {
// Lock successful and we're not just probing, put it into the map
dir_locks.emplace(pathLockFile.string(), std::move(lock));
}
} catch (const boost::interprocess::interprocess_exception &e) {
return error("Error while attempting to lock directory %s: %s",
directory.string(), e.what());
}
return true;
}
void ReleaseDirectoryLocks() {
std::lock_guard<std::mutex> ulock(cs_dir_locks);
dir_locks.clear();
}
bool DirIsWritable(const fs::path &directory) {
fs::path tmpFile = directory / fs::unique_path();
FILE *file = fsbridge::fopen(tmpFile, "a");
if (!file) {
return false;
}
fclose(file);
remove(tmpFile);
return true;
}
bool CheckDiskSpace(const fs::path &dir, uint64_t additional_bytes) {
// 50 MiB
constexpr uint64_t min_disk_space = 52428800;
uint64_t free_bytes_available = fs::space(dir).available;
return free_bytes_available >= min_disk_space + additional_bytes;
}
/**
* Interpret a string argument as a boolean.
*
* The definition of atoi() requires that non-numeric string values like "foo",
* return 0. This means that if a user unintentionally supplies a non-integer
* argument here, the return value is always false. This means that -foo=false
* does what the user probably expects, but -foo=true is well defined but does
* not do what they probably expected.
*
* The return value of atoi() is undefined when given input not representable as
* an int. On most systems this means string value between "-2147483648" and
* "2147483647" are well defined (this method will return true). Setting
* -txindex=2147483648 on most systems, however, is probably undefined.
*
* For a more extensive discussion of this topic (and a wide range of opinions
* on the Right Way to change this code), see PR12713.
*/
static bool InterpretBool(const std::string &strValue) {
if (strValue.empty()) {
return true;
}
return (atoi(strValue) != 0);
}
/** Internal helper functions for ArgsManager */
class ArgsManagerHelper {
public:
typedef std::map<std::string, std::vector<std::string>> MapArgs;
/** Determine whether to use config settings in the default section,
* See also comments around ArgsManager::ArgsManager() below. */
static inline bool UseDefaultSection(const ArgsManager &am,
const std::string &arg) {
return (am.m_network == CBaseChainParams::MAIN ||
am.m_network_only_args.count(arg) == 0);
}
/** Convert regular argument into the network-specific setting */
static inline std::string NetworkArg(const ArgsManager &am,
const std::string &arg) {
assert(arg.length() > 1 && arg[0] == '-');
return "-" + am.m_network + "." + arg.substr(1);
}
/** Find arguments in a map and add them to a vector */
static inline void AddArgs(std::vector<std::string> &res,
const MapArgs &map_args,
const std::string &arg) {
auto it = map_args.find(arg);
if (it != map_args.end()) {
res.insert(res.end(), it->second.begin(), it->second.end());
}
}
/**
* Return true/false if an argument is set in a map, and also
* return the first (or last) of the possibly multiple values it has
*/
static inline std::pair<bool, std::string>
GetArgHelper(const MapArgs &map_args, const std::string &arg,
bool getLast = false) {
auto it = map_args.find(arg);
if (it == map_args.end() || it->second.empty()) {
return std::make_pair(false, std::string());
}
if (getLast) {
return std::make_pair(true, it->second.back());
} else {
return std::make_pair(true, it->second.front());
}
}
/**
* Get the string value of an argument, returning a pair of a boolean
* indicating the argument was found, and the value for the argument
* if it was found (or the empty string if not found).
*/
static inline std::pair<bool, std::string> GetArg(const ArgsManager &am,
const std::string &arg) {
LOCK(am.cs_args);
std::pair<bool, std::string> found_result(false, std::string());
// We pass "true" to GetArgHelper in order to return the last
// argument value seen from the command line (so "bitcoind -foo=bar
// -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
found_result = GetArgHelper(am.m_override_args, arg, true);
if (found_result.first) {
return found_result;
}
// But in contrast we return the first argument seen in a config file,
// so "foo=bar \n foo=baz" in the config file gives
// GetArg(am,"foo")={true,"bar"}
if (!am.m_network.empty()) {
found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
if (found_result.first) {
return found_result;
}
}
if (UseDefaultSection(am, arg)) {
found_result = GetArgHelper(am.m_config_args, arg);
if (found_result.first) {
return found_result;
}
}
return found_result;
}
/* Special test for -testnet and -regtest args, because we don't want to be
* confused by craziness like "[regtest] testnet=1"
*/
static inline bool GetNetBoolArg(const ArgsManager &am,
const std::string &net_arg) {
std::pair<bool, std::string> found_result(false, std::string());
found_result = GetArgHelper(am.m_override_args, net_arg, true);
if (!found_result.first) {
found_result = GetArgHelper(am.m_config_args, net_arg, true);
if (!found_result.first) {
// not set
return false;
}
}
// is set, so evaluate
return InterpretBool(found_result.second);
}
};
/**
* Interpret -nofoo as if the user supplied -foo=0.
*
* This method also tracks when the -no form was supplied, and if so, checks
* whether there was a double-negative (-nofoo=0 -> -foo=1).
*
* If there was not a double negative, it removes the "no" from the key, and
* returns true, indicating the caller should clear the args vector to indicate
* a negated option.
*
* If there was a double negative, it removes "no" from the key, sets the value
* to "1" and returns false.
*
* If there was no "no", it leaves key and value untouched and returns false.
*
* Where an option was negated can be later checked using the IsArgNegated()
* method. One use case for this is to have a way to disable options that are
* not normally boolean (e.g. using -nodebuglogfile to request that debug log
* output is not sent to any file at all).
*/
static bool InterpretNegatedOption(std::string &key, std::string &val) {
assert(key[0] == '-');
size_t option_index = key.find('.');
if (option_index == std::string::npos) {
option_index = 1;
} else {
++option_index;
}
if (key.substr(option_index, 2) == "no") {
bool bool_val = InterpretBool(val);
key.erase(option_index, 2);
if (!bool_val) {
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf(
"Warning: parsed potentially confusing double-negative %s=%s\n",
key, val);
val = "1";
} else {
return true;
}
}
return false;
}
ArgsManager::ArgsManager()
: /* These options would cause cross-contamination if values for mainnet
* were used while running on regtest/testnet (or vice-versa).
* Setting them as section_only_args ensures that sharing a config file
* between mainnet and regtest/testnet won't cause problems due to these
* parameters by accident. */
m_network_only_args{
"-addnode", "-connect", "-port", "-bind",
"-rpcport", "-rpcbind", "-wallet",
} {
// nothing to do
}
void ArgsManager::WarnForSectionOnlyArgs() {
// if there's no section selected, don't worry
if (m_network.empty()) {
return;
}
// if it's okay to use the default section for this network, don't worry
if (m_network == CBaseChainParams::MAIN) {
return;
}
for (const auto &arg : m_network_only_args) {
std::pair<bool, std::string> found_result;
// if this option is overridden it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
if (found_result.first) {
continue;
}
// if there's a network-specific value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(
m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
if (found_result.first) {
continue;
}
// if there isn't a default value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
if (!found_result.first) {
continue;
}
// otherwise, issue a warning
LogPrintf("Warning: Config setting for %s only applied on %s network "
"when in [%s] section.\n",
arg, m_network, m_network);
}
}
void ArgsManager::SelectConfigNetwork(const std::string &network) {
m_network = network;
}
bool ArgsManager::ParseParameters(int argc, const char *const argv[],
std::string &error) {
LOCK(cs_args);
m_override_args.clear();
for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
std::string val;
size_t is_index = key.find('=');
if (is_index != std::string::npos) {
val = key.substr(is_index + 1);
key.erase(is_index);
}
#ifdef WIN32
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
if (key[0] == '/') {
key[0] = '-';
}
#endif
if (key[0] != '-') {
break;
}
// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-') {
key.erase(0, 1);
}
// Check for -nofoo
if (InterpretNegatedOption(key, val)) {
m_override_args[key].clear();
} else {
m_override_args[key].push_back(val);
}
// Check that the arg is known
if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
if (!IsArgKnown(key, error)) {
error = strprintf("Invalid parameter %s", key.c_str());
return false;
}
}
}
// we do not allow -includeconf from command line, so we clear it here
auto it = m_override_args.find("-includeconf");
if (it != m_override_args.end()) {
if (it->second.size() > 0) {
for (const auto &ic : it->second) {
fprintf(stderr,
"warning: -includeconf cannot be used from "
"commandline; ignoring -includeconf=%s\n",
ic.c_str());
}
m_override_args.erase(it);
}
}
return true;
}
bool ArgsManager::IsArgKnown(const std::string &key, std::string &error) {
size_t option_index = key.find('.');
std::string arg_no_net;
if (option_index == std::string::npos) {
arg_no_net = key;
} else {
arg_no_net =
std::string("-") + key.substr(option_index + 1, std::string::npos);
}
for (const auto &arg_map : m_available_args) {
if (arg_map.second.count(arg_no_net)) {
return true;
}
}
return false;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string &strArg) const {
std::vector<std::string> result = {};
// special case
if (IsArgNegated(strArg)) {
return result;
}
LOCK(cs_args);
ArgsManagerHelper::AddArgs(result, m_override_args, strArg);
if (!m_network.empty()) {
ArgsManagerHelper::AddArgs(
result, m_config_args,
ArgsManagerHelper::NetworkArg(*this, strArg));
}
if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
}
return result;
}
bool ArgsManager::IsArgSet(const std::string &strArg) const {
// special case
if (IsArgNegated(strArg)) {
return true;
}
return ArgsManagerHelper::GetArg(*this, strArg).first;
}
bool ArgsManager::IsArgNegated(const std::string &strArg) const {
LOCK(cs_args);
const auto &ov = m_override_args.find(strArg);
if (ov != m_override_args.end()) {
return ov->second.empty();
}
if (!m_network.empty()) {
const auto &cfs =
m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
if (cfs != m_config_args.end()) {
return cfs->second.empty();
}
}
const auto &cf = m_config_args.find(strArg);
if (cf != m_config_args.end()) {
return cf->second.empty();
}
return false;
}
std::string ArgsManager::GetArg(const std::string &strArg,
const std::string &strDefault) const {
if (IsArgNegated(strArg)) {
return "0";
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return found_res.second;
}
return strDefault;
}
int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const {
if (IsArgNegated(strArg)) {
return 0;
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return atoi64(found_res.second);
}
return nDefault;
}
bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const {
if (IsArgNegated(strArg)) {
return false;
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return InterpretBool(found_res.second);
}
return fDefault;
}
bool ArgsManager::SoftSetArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
if (IsArgSet(strArg)) {
return false;
}
ForceSetArg(strArg, strValue);
return true;
}
bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) {
if (fValue) {
return SoftSetArg(strArg, std::string("1"));
} else {
return SoftSetArg(strArg, std::string("0"));
}
}
void ArgsManager::ForceSetArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
m_override_args[strArg] = {strValue};
}
/**
* This function is only used for testing purpose so
* so we should not worry about element uniqueness and
* integrity of mapMultiArgs data structure
*/
void ArgsManager::ForceSetMultiArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
m_override_args[strArg].push_back(strValue);
}
void ArgsManager::AddArg(const std::string &name, const std::string &help,
const bool debug_only, const OptionsCategory &cat) {
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
eq_index = name.size();
}
std::map<std::string, Arg> &arg_map = m_available_args[cat];
auto ret = arg_map.emplace(
name.substr(0, eq_index),
Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only));
// Make sure an insertion actually happened.
assert(ret.second);
}
void ArgsManager::ClearArg(const std::string &strArg) {
LOCK(cs_args);
m_override_args.erase(strArg);
m_config_args.erase(strArg);
}
std::string ArgsManager::GetHelpMessage() {
const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
std::string usage = "";
for (const auto &arg_map : m_available_args) {
switch (arg_map.first) {
case OptionsCategory::OPTIONS:
usage += HelpMessageGroup("Options:");
break;
case OptionsCategory::CONNECTION:
usage += HelpMessageGroup("Connection options:");
break;
case OptionsCategory::ZMQ:
usage += HelpMessageGroup("ZeroMQ notification options:");
break;
case OptionsCategory::DEBUG_TEST:
usage += HelpMessageGroup("Debugging/Testing options:");
break;
case OptionsCategory::NODE_RELAY:
usage += HelpMessageGroup("Node relay options:");
break;
case OptionsCategory::BLOCK_CREATION:
usage += HelpMessageGroup("Block creation options:");
break;
case OptionsCategory::RPC:
usage += HelpMessageGroup("RPC server options:");
break;
case OptionsCategory::WALLET:
usage += HelpMessageGroup("Wallet options:");
break;
case OptionsCategory::WALLET_DEBUG_TEST:
if (show_debug) {
usage +=
HelpMessageGroup("Wallet debugging/testing options:");
}
break;
case OptionsCategory::CHAINPARAMS:
usage += HelpMessageGroup("Chain selection options:");
break;
case OptionsCategory::GUI:
usage += HelpMessageGroup("UI Options:");
break;
case OptionsCategory::COMMANDS:
usage += HelpMessageGroup("Commands:");
break;
case OptionsCategory::REGISTER_COMMANDS:
usage += HelpMessageGroup("Register Commands:");
break;
default:
break;
}
// When we get to the hidden options, stop
if (arg_map.first == OptionsCategory::HIDDEN) {
break;
}
for (const auto &arg : arg_map.second) {
if (show_debug || !arg.second.m_debug_only) {
std::string name;
if (arg.second.m_help_param.empty()) {
name = arg.first;
} else {
name = arg.first + arg.second.m_help_param;
}
usage += HelpMessageOpt(name, arg.second.m_help_text);
}
}
}
return usage;
}
bool HelpRequested(const ArgsManager &args) {
return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help");
}
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(nullptr, 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());
}
fs::path GetDefaultDataDir() {
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
// Mac: ~/Library/Application Support/Bitcoin
// Unix: ~/.bitcoin
#ifdef WIN32
// Windows
return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
#else
fs::path pathRet;
char *pszHome = getenv("HOME");
if (pszHome == nullptr || strlen(pszHome) == 0) {
pathRet = fs::path("/");
} else {
pathRet = fs::path(pszHome);
}
#ifdef MAC_OSX
// Mac
return pathRet / "Library/Application Support/Bitcoin";
#else
// Unix
return pathRet / ".bitcoin";
#endif
#endif
}
static fs::path g_blocks_path_cache_net_specific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
static CCriticalSection csPathCached;
const fs::path &GetBlocksDir() {
LOCK(csPathCached);
fs::path &path = g_blocks_path_cache_net_specific;
// 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 (gArgs.IsArgSet("-blocksdir")) {
path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
if (!fs::is_directory(path)) {
path = "";
return path;
}
} else {
path = GetDataDir(false);
}
path /= BaseParams().DataDir();
path /= "blocks";
fs::create_directories(path);
return path;
}
const fs::path &GetDataDir(bool fNetSpecific) {
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 (gArgs.IsArgSet("-datadir")) {
path = fs::system_complete(gArgs.GetArg("-datadir", ""));
if (!fs::is_directory(path)) {
path = "";
return path;
}
} else {
path = GetDefaultDataDir();
}
if (fNetSpecific) {
path /= BaseParams().DataDir();
}
if (fs::create_directories(path)) {
// This is the first run, create wallets subdirectory too
//
// TODO: this is an ugly way to create the wallets/ directory and
// really shouldn't be done here. Once this is fixed, please
// also remove the corresponding line in bitcoind.cpp AppInit.
// See more info at:
// https://reviews.bitcoinabc.org/D3312
fs::create_directories(path / "wallets");
}
return path;
}
void ClearDatadirCache() {
LOCK(csPathCached);
pathCached = fs::path();
pathCachedNetSpecific = fs::path();
g_blocks_path_cache_net_specific = fs::path();
}
fs::path GetConfigFile(const std::string &confPath) {
fs::path pathConfigFile(confPath);
if (!pathConfigFile.is_complete()) {
pathConfigFile = GetDataDir(false) / pathConfigFile;
}
return pathConfigFile;
}
static std::string TrimString(const std::string &str,
const std::string &pattern) {
std::string::size_type front = str.find_first_not_of(pattern);
if (front == std::string::npos) {
return std::string();
}
std::string::size_type end = str.find_last_not_of(pattern);
return str.substr(front, end - front + 1);
}
static std::vector<std::pair<std::string, std::string>>
GetConfigOptions(std::istream &stream) {
std::vector<std::pair<std::string, std::string>> options;
std::string str, prefix;
std::string::size_type pos;
while (std::getline(stream, str)) {
if ((pos = str.find('#')) != std::string::npos) {
str = str.substr(0, pos);
}
const static std::string pattern = " \t\r\n";
str = TrimString(str, pattern);
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
prefix = str.substr(1, str.size() - 2) + '.';
} else if ((pos = str.find('=')) != std::string::npos) {
std::string name =
prefix + TrimString(str.substr(0, pos), pattern);
std::string value = TrimString(str.substr(pos + 1), pattern);
options.emplace_back(name, value);
}
}
}
return options;
}
bool ArgsManager::ReadConfigStream(std::istream &stream, std::string &error,
bool ignore_invalid_keys) {
LOCK(cs_args);
for (const std::pair<std::string, std::string> &option :
GetConfigOptions(stream)) {
std::string strKey = std::string("-") + option.first;
std::string strValue = option.second;
if (InterpretNegatedOption(strKey, strValue)) {
m_config_args[strKey].clear();
} else {
m_config_args[strKey].push_back(strValue);
}
// Check that the arg is known
if (!IsArgKnown(strKey, error) && !ignore_invalid_keys) {
error = strprintf("Invalid configuration value %s",
option.first.c_str());
return false;
}
}
return true;
}
bool ArgsManager::ReadConfigFiles(std::string &error,
bool ignore_invalid_keys) {
{
LOCK(cs_args);
m_config_args.clear();
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
fs::ifstream stream(GetConfigFile(confPath));
// ok to not have a config file
if (stream.good()) {
if (!ReadConfigStream(stream, error, ignore_invalid_keys)) {
return false;
}
// if there is an -includeconf in the override args, but it is empty,
// that means the user passed '-noincludeconf' on the command line, in
// which case we should not include anything
bool emptyIncludeConf;
{
LOCK(cs_args);
emptyIncludeConf = m_override_args.count("-includeconf") == 0;
}
if (emptyIncludeConf) {
std::vector<std::string> includeconf(GetArgs("-includeconf"));
{
// We haven't set m_network yet (that happens in
// SelectParams()), so manually check for network.includeconf
// args.
std::vector<std::string> includeconf_net(GetArgs(
std::string("-") + GetChainName() + ".includeconf"));
includeconf.insert(includeconf.end(), includeconf_net.begin(),
includeconf_net.end());
}
for (const std::string &to_include : includeconf) {
fs::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
if (!ReadConfigStream(include_config, error,
ignore_invalid_keys)) {
return false;
}
LogPrintf("Included configuration file %s\n",
to_include.c_str());
} else {
fprintf(stderr, "Failed to include configuration file %s\n",
to_include.c_str());
}
}
}
}
// If datadir is changed in .conf file:
ClearDatadirCache();
if (!fs::is_directory(GetDataDir(false))) {
error = strprintf("specified data directory \"%s\" does not exist.",
gArgs.GetArg("-datadir", "").c_str());
return false;
}
return true;
}
std::string ArgsManager::GetChainName() const {
bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
if (fTestNet && fRegTest) {
throw std::runtime_error(
"Invalid combination of -regtest and -testnet.");
}
if (fRegTest) {
return CBaseChainParams::REGTEST;
}
if (fTestNet) {
return CBaseChainParams::TESTNET;
}
return CBaseChainParams::MAIN;
}
#ifndef WIN32
fs::path GetPidFile() {
fs::path pathPidFile(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME));
if (!pathPidFile.is_complete()) {
pathPidFile = GetDataDir() / pathPidFile;
}
return pathPidFile;
}
void CreatePidFile(const fs::path &path, pid_t pid) {
FILE *file = fsbridge::fopen(path, "w");
if (file) {
fprintf(file, "%d\n", pid);
fclose(file);
}
}
#endif
bool RenameOver(fs::path src, fs::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_directories 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 TryCreateDirectories(const fs::path &p) {
try {
return fs::create_directories(p);
} catch (const fs::filesystem_error &) {
if (!fs::exists(p) || !fs::is_directory(p)) {
throw;
}
}
// create_directory didn't create the directory, it had to have existed
// already.
return false;
}
bool FileCommit(FILE *file) {
// harmless if redundantly called
if (fflush(file) != 0) {
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
return false;
}
#ifdef WIN32
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
if (FlushFileBuffers(hFile) == 0) {
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__,
GetLastError());
return false;
}
#else
#if defined(__linux__) || defined(__NetBSD__)
// Ignore EINVAL for filesystems that don't support sync
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
}
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
// Manpage says "value other than -1" is returned on success
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) {
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
return false;
}
#else
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
return false;
}
#endif
#endif
return true;
}
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;
}
// getrlimit failed, assume it's fine.
return nMinFD;
#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;
}
// Allowed to fail; this function is advisory anyway.
fwrite(buf, 1, now, file);
length -= now;
}
#endif
}
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate) {
char pszPath[MAX_PATH] = "";
if (SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) {
return fs::path(pszPath);
}
LogPrintf(
"SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
return fs::path("");
}
#endif
void runCommand(const std::string &strCommand) {
if (strCommand.empty()) {
return;
}
#ifndef WIN32
int nErr = ::system(strCommand.c_str());
#else
int nErr = ::_wsystem(
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
.from_bytes(strCommand)
.c_str());
#endif
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() {
#ifdef HAVE_MALLOPT_ARENA_MAX
// glibc-specific: On 32-bit systems set the number of arenas to 1. By
// default, since glibc 2.10, the C library will create up to two heap
// arenas per core. This is known to cause excessive virtual address space
// usage in our usage. Work around it by setting the maximum number of
// arenas to 1.
if (sizeof(void *) == 4) {
mallopt(M_ARENA_MAX, 1);
}
#endif
// 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 {
// Raises a runtime error if current locale is invalid.
std::locale("");
} 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
// fs::path, which is then used to explicitly imbue the path.
std::locale loc = fs::path::imbue(std::locale::classic());
#ifndef WIN32
fs::path::imbue(loc);
#else
fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
#endif
}
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;
}
int GetNumCores() {
return boost::thread::physical_concurrency();
}
std::string CopyrightHolders(const std::string &strPrefix) {
return strPrefix +
strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION));
}
// Obtain the application startup time (used for uptime calculation)
int64_t GetStartupTime() {
return nStartupTime;
}

File Metadata

Mime Type
text/x-c++
Expires
Sun, Mar 2, 11:45 (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187686
Default Alt Text
util.cpp (40 KB)

Event Timeline