9af080c351
The best log rotation method formerly available was to configure logrotate with the copytruncate option. As described in the logrotate documentation, "there is a very small time slice between copying the file and truncating it, so some logging data might be lost". By sending SIGHUP to the server process, one can now reopen the debug log file without losing any data.
1116 lines
30 KiB
C++
1116 lines
30 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "util.h"
|
|
#include "sync.h"
|
|
#include "strlcpy.h"
|
|
#include "version.h"
|
|
#include "ui_interface.h"
|
|
#include <boost/algorithm/string/join.hpp>
|
|
|
|
// 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&);
|
|
}
|
|
}
|
|
|
|
#include <boost/program_options/detail/config_file.hpp>
|
|
#include <boost/program_options/parsers.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/filesystem/fstream.hpp>
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/thread.hpp>
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/rand.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef WIN32
|
|
#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
|
|
|
|
using namespace std;
|
|
|
|
map<string, string> mapArgs;
|
|
map<string, vector<string> > mapMultiArgs;
|
|
bool fDebug = false;
|
|
bool fPrintToConsole = false;
|
|
bool fPrintToDebugger = false;
|
|
bool fRequestShutdown = false;
|
|
bool fShutdown = false;
|
|
bool fDaemon = false;
|
|
bool fServer = false;
|
|
bool fCommandLine = false;
|
|
string strMiscWarning;
|
|
bool fTestNet = false;
|
|
bool fNoListen = false;
|
|
bool fLogTimestamps = false;
|
|
CMedianFilter<int64> vTimeOffsets(200,0);
|
|
bool fReopenDebugLog = false;
|
|
|
|
// Init openssl library multithreading support
|
|
static CCriticalSection** ppmutexOpenSSL;
|
|
void locking_callback(int mode, int i, const char* file, int line)
|
|
{
|
|
if (mode & CRYPTO_LOCK) {
|
|
ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
|
} else {
|
|
LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
|
}
|
|
}
|
|
|
|
// Init
|
|
class CInit
|
|
{
|
|
public:
|
|
CInit()
|
|
{
|
|
// Init openssl library multithreading support
|
|
ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*));
|
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
|
ppmutexOpenSSL[i] = new CCriticalSection();
|
|
CRYPTO_set_locking_callback(locking_callback);
|
|
|
|
#ifdef WIN32
|
|
// Seed random number generator with screen scrape and other hardware sources
|
|
RAND_screen();
|
|
#endif
|
|
|
|
// Seed random number generator with performance counter
|
|
RandAddSeed();
|
|
}
|
|
~CInit()
|
|
{
|
|
// Shutdown openssl library multithreading support
|
|
CRYPTO_set_locking_callback(NULL);
|
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
|
delete ppmutexOpenSSL[i];
|
|
OPENSSL_free(ppmutexOpenSSL);
|
|
}
|
|
}
|
|
instance_of_cinit;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void RandAddSeed()
|
|
{
|
|
// Seed with CPU performance counter
|
|
int64 nCounter = GetPerformanceCounter();
|
|
RAND_add(&nCounter, sizeof(nCounter), 1.5);
|
|
memset(&nCounter, 0, sizeof(nCounter));
|
|
}
|
|
|
|
void RandAddSeedPerfmon()
|
|
{
|
|
RandAddSeed();
|
|
|
|
// This can take up to 2 seconds, so only do it every 10 minutes
|
|
static int64 nLastPerfmon;
|
|
if (GetTime() < nLastPerfmon + 10 * 60)
|
|
return;
|
|
nLastPerfmon = GetTime();
|
|
|
|
#ifdef WIN32
|
|
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
|
// Seed with the entire set of perfmon data
|
|
unsigned char pdata[250000];
|
|
memset(pdata, 0, sizeof(pdata));
|
|
unsigned long nSize = sizeof(pdata);
|
|
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
|
|
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
RAND_add(pdata, nSize, nSize/100.0);
|
|
memset(pdata, 0, nSize);
|
|
printf("RandAddSeed() %d bytes\n", nSize);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint64 GetRand(uint64 nMax)
|
|
{
|
|
if (nMax == 0)
|
|
return 0;
|
|
|
|
// The range of the random source must be a multiple of the modulus
|
|
// to give every possible output value an equal possibility
|
|
uint64 nRange = (std::numeric_limits<uint64>::max() / nMax) * nMax;
|
|
uint64 nRand = 0;
|
|
do
|
|
RAND_bytes((unsigned char*)&nRand, sizeof(nRand));
|
|
while (nRand >= nRange);
|
|
return (nRand % nMax);
|
|
}
|
|
|
|
int GetRandInt(int nMax)
|
|
{
|
|
return GetRand(nMax);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline int OutputDebugStringF(const char* pszFormat, ...)
|
|
{
|
|
int ret = 0;
|
|
if (fPrintToConsole)
|
|
{
|
|
// print to console
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
ret = vprintf(pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
}
|
|
else
|
|
{
|
|
// print to debug.log
|
|
static FILE* fileout = NULL;
|
|
|
|
if (!fileout)
|
|
{
|
|
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
|
|
fileout = fopen(pathDebug.string().c_str(), "a");
|
|
if (fileout) setbuf(fileout, NULL); // unbuffered
|
|
}
|
|
if (fileout)
|
|
{
|
|
static bool fStartedNewLine = true;
|
|
static boost::mutex mutexDebugLog;
|
|
boost::mutex::scoped_lock scoped_lock(mutexDebugLog);
|
|
|
|
// 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
|
|
}
|
|
|
|
// Debug print useful for profiling
|
|
if (fLogTimestamps && fStartedNewLine)
|
|
fprintf(fileout, "%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
|
|
if (pszFormat[strlen(pszFormat) - 1] == '\n')
|
|
fStartedNewLine = true;
|
|
else
|
|
fStartedNewLine = false;
|
|
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
ret = vfprintf(fileout, pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if (fPrintToDebugger)
|
|
{
|
|
static CCriticalSection cs_OutputDebugStringF;
|
|
|
|
// accumulate a line at a time
|
|
{
|
|
LOCK(cs_OutputDebugStringF);
|
|
static char pszBuffer[50000];
|
|
static char* pend;
|
|
if (pend == NULL)
|
|
pend = pszBuffer;
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
int limit = END(pszBuffer) - pend - 2;
|
|
int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
if (ret < 0 || ret >= limit)
|
|
{
|
|
pend = END(pszBuffer) - 2;
|
|
*pend++ = '\n';
|
|
}
|
|
else
|
|
pend += ret;
|
|
*pend = '\0';
|
|
char* p1 = pszBuffer;
|
|
char* p2;
|
|
while ((p2 = strchr(p1, '\n')))
|
|
{
|
|
p2++;
|
|
char c = *p2;
|
|
*p2 = '\0';
|
|
OutputDebugStringA(p1);
|
|
*p2 = c;
|
|
p1 = p2;
|
|
}
|
|
if (p1 != pszBuffer)
|
|
memmove(pszBuffer, p1, pend - p1 + 1);
|
|
pend -= (p1 - pszBuffer);
|
|
}
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
// Safer snprintf
|
|
// - prints up to limit-1 characters
|
|
// - output string is always null terminated even if limit reached
|
|
// - return value is the number of characters actually printed
|
|
int my_snprintf(char* buffer, size_t limit, const char* format, ...)
|
|
{
|
|
if (limit == 0)
|
|
return 0;
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, format);
|
|
int ret = _vsnprintf(buffer, limit, format, arg_ptr);
|
|
va_end(arg_ptr);
|
|
if (ret < 0 || ret >= (int)limit)
|
|
{
|
|
ret = limit - 1;
|
|
buffer[limit-1] = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
string real_strprintf(const std::string &format, int dummy, ...)
|
|
{
|
|
char buffer[50000];
|
|
char* p = buffer;
|
|
int limit = sizeof(buffer);
|
|
int ret;
|
|
loop
|
|
{
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, dummy);
|
|
ret = _vsnprintf(p, limit, format.c_str(), arg_ptr);
|
|
va_end(arg_ptr);
|
|
if (ret >= 0 && ret < limit)
|
|
break;
|
|
if (p != buffer)
|
|
delete[] p;
|
|
limit *= 2;
|
|
p = new char[limit];
|
|
if (p == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
string str(p, p+ret);
|
|
if (p != buffer)
|
|
delete[] p;
|
|
return str;
|
|
}
|
|
|
|
bool error(const char *format, ...)
|
|
{
|
|
char buffer[50000];
|
|
int limit = sizeof(buffer);
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, format);
|
|
int ret = _vsnprintf(buffer, limit, format, arg_ptr);
|
|
va_end(arg_ptr);
|
|
if (ret < 0 || ret >= limit)
|
|
{
|
|
buffer[limit - 1] = 0;
|
|
}
|
|
printf("ERROR: %s\n", buffer);
|
|
return false;
|
|
}
|
|
|
|
|
|
void ParseString(const string& str, char c, vector<string>& v)
|
|
{
|
|
if (str.empty())
|
|
return;
|
|
string::size_type i1 = 0;
|
|
string::size_type i2;
|
|
loop
|
|
{
|
|
i2 = str.find(c, i1);
|
|
if (i2 == str.npos)
|
|
{
|
|
v.push_back(str.substr(i1));
|
|
return;
|
|
}
|
|
v.push_back(str.substr(i1, i2-i1));
|
|
i1 = i2+1;
|
|
}
|
|
}
|
|
|
|
|
|
string FormatMoney(int64 n, bool fPlus)
|
|
{
|
|
// Note: not using straight sprintf here because we do NOT want
|
|
// localized number formatting.
|
|
int64 n_abs = (n > 0 ? n : -n);
|
|
int64 quotient = n_abs/COIN;
|
|
int64 remainder = n_abs%COIN;
|
|
string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder);
|
|
|
|
// Right-trim excess 0's before the decimal point:
|
|
int nTrim = 0;
|
|
for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i)
|
|
++nTrim;
|
|
if (nTrim)
|
|
str.erase(str.size()-nTrim, nTrim);
|
|
|
|
if (n < 0)
|
|
str.insert((unsigned int)0, 1, '-');
|
|
else if (fPlus && n > 0)
|
|
str.insert((unsigned int)0, 1, '+');
|
|
return str;
|
|
}
|
|
|
|
|
|
bool ParseMoney(const string& str, int64& nRet)
|
|
{
|
|
return ParseMoney(str.c_str(), nRet);
|
|
}
|
|
|
|
bool ParseMoney(const char* pszIn, int64& nRet)
|
|
{
|
|
string strWhole;
|
|
int64 nUnits = 0;
|
|
const char* p = pszIn;
|
|
while (isspace(*p))
|
|
p++;
|
|
for (; *p; p++)
|
|
{
|
|
if (*p == '.')
|
|
{
|
|
p++;
|
|
int64 nMult = CENT*10;
|
|
while (isdigit(*p) && (nMult > 0))
|
|
{
|
|
nUnits += nMult * (*p++ - '0');
|
|
nMult /= 10;
|
|
}
|
|
break;
|
|
}
|
|
if (isspace(*p))
|
|
break;
|
|
if (!isdigit(*p))
|
|
return false;
|
|
strWhole.insert(strWhole.end(), *p);
|
|
}
|
|
for (; *p; p++)
|
|
if (!isspace(*p))
|
|
return false;
|
|
if (strWhole.size() > 10) // guard against 63 bit overflow
|
|
return false;
|
|
if (nUnits < 0 || nUnits > COIN)
|
|
return false;
|
|
int64 nWhole = atoi64(strWhole);
|
|
int64 nValue = nWhole*COIN + nUnits;
|
|
|
|
nRet = nValue;
|
|
return true;
|
|
}
|
|
|
|
|
|
static signed char phexdigit[256] =
|
|
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
|
|
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
|
|
|
|
bool IsHex(const string& str)
|
|
{
|
|
BOOST_FOREACH(unsigned char c, str)
|
|
{
|
|
if (phexdigit[c] < 0)
|
|
return false;
|
|
}
|
|
return (str.size() > 0) && (str.size()%2 == 0);
|
|
}
|
|
|
|
vector<unsigned char> ParseHex(const char* psz)
|
|
{
|
|
// convert hex dump to vector
|
|
vector<unsigned char> vch;
|
|
loop
|
|
{
|
|
while (isspace(*psz))
|
|
psz++;
|
|
signed char c = phexdigit[(unsigned char)*psz++];
|
|
if (c == (signed char)-1)
|
|
break;
|
|
unsigned char n = (c << 4);
|
|
c = phexdigit[(unsigned char)*psz++];
|
|
if (c == (signed char)-1)
|
|
break;
|
|
n |= c;
|
|
vch.push_back(n);
|
|
}
|
|
return vch;
|
|
}
|
|
|
|
vector<unsigned char> ParseHex(const string& str)
|
|
{
|
|
return ParseHex(str.c_str());
|
|
}
|
|
|
|
static void InterpretNegativeSetting(string name, map<string, string>& 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);
|
|
mapSettingsRet[positive] = (value ? "1" : "0");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParseParameters(int argc, const char* const argv[])
|
|
{
|
|
mapArgs.clear();
|
|
mapMultiArgs.clear();
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
char psz[10000];
|
|
strlcpy(psz, argv[i], sizeof(psz));
|
|
char* pszValue = (char*)"";
|
|
if (strchr(psz, '='))
|
|
{
|
|
pszValue = strchr(psz, '=');
|
|
*pszValue++ = '\0';
|
|
}
|
|
#ifdef WIN32
|
|
_strlwr(psz);
|
|
if (psz[0] == '/')
|
|
psz[0] = '-';
|
|
#endif
|
|
if (psz[0] != '-')
|
|
break;
|
|
|
|
mapArgs[psz] = pszValue;
|
|
mapMultiArgs[psz].push_back(pszValue);
|
|
}
|
|
|
|
// New 0.6 features:
|
|
BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs)
|
|
{
|
|
string name = entry.first;
|
|
|
|
// interpret --foo as -foo (as long as both are not set)
|
|
if (name.find("--") == 0)
|
|
{
|
|
std::string singleDash(name.begin()+1, name.end());
|
|
if (mapArgs.count(singleDash) == 0)
|
|
mapArgs[singleDash] = entry.second;
|
|
name = singleDash;
|
|
}
|
|
|
|
// interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set
|
|
InterpretNegativeSetting(name, mapArgs);
|
|
}
|
|
}
|
|
|
|
std::string GetArg(const std::string& strArg, const std::string& strDefault)
|
|
{
|
|
if (mapArgs.count(strArg))
|
|
return mapArgs[strArg];
|
|
return strDefault;
|
|
}
|
|
|
|
int64 GetArg(const std::string& strArg, int64 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"));
|
|
}
|
|
|
|
|
|
string EncodeBase64(const unsigned char* pch, size_t len)
|
|
{
|
|
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
string strRet="";
|
|
strRet.reserve((len+2)/3*4);
|
|
|
|
int mode=0, left=0;
|
|
const unsigned char *pchEnd = pch+len;
|
|
|
|
while (pch<pchEnd)
|
|
{
|
|
int enc = *(pch++);
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits
|
|
strRet += pbase64[enc >> 2];
|
|
left = (enc & 3) << 4;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have two bits
|
|
strRet += pbase64[left | (enc >> 4)];
|
|
left = (enc & 15) << 2;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have four bits
|
|
strRet += pbase64[left | (enc >> 6)];
|
|
strRet += pbase64[enc & 63];
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode)
|
|
{
|
|
strRet += pbase64[left];
|
|
strRet += '=';
|
|
if (mode == 1)
|
|
strRet += '=';
|
|
}
|
|
|
|
return strRet;
|
|
}
|
|
|
|
string EncodeBase64(const string& str)
|
|
{
|
|
return EncodeBase64((const unsigned char*)str.c_str(), str.size());
|
|
}
|
|
|
|
vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
|
|
{
|
|
static const int decode64_table[256] =
|
|
{
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
|
|
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
|
|
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
|
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
if (pfInvalid)
|
|
*pfInvalid = false;
|
|
|
|
vector<unsigned char> vchRet;
|
|
vchRet.reserve(strlen(p)*3/4);
|
|
|
|
int mode = 0;
|
|
int left = 0;
|
|
|
|
while (1)
|
|
{
|
|
int dec = decode64_table[(unsigned char)*p];
|
|
if (dec == -1) break;
|
|
p++;
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits and get 6
|
|
left = dec;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have 6 bits and keep 4
|
|
vchRet.push_back((left<<2) | (dec>>4));
|
|
left = dec & 15;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have 4 bits and get 6, we keep 2
|
|
vchRet.push_back((left<<4) | (dec>>2));
|
|
left = dec & 3;
|
|
mode = 3;
|
|
break;
|
|
|
|
case 3: // we have 2 bits and get 6
|
|
vchRet.push_back((left<<6) | dec);
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pfInvalid)
|
|
switch (mode)
|
|
{
|
|
case 0: // 4n base64 characters processed: ok
|
|
break;
|
|
|
|
case 1: // 4n+1 base64 character processed: impossible
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 2: // 4n+2 base64 characters processed: require '=='
|
|
if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 3: // 4n+3 base64 characters processed: require '='
|
|
if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
}
|
|
|
|
return vchRet;
|
|
}
|
|
|
|
string DecodeBase64(const string& str)
|
|
{
|
|
vector<unsigned char> vchRet = DecodeBase64(str.c_str());
|
|
return string((const char*)&vchRet[0], vchRet.size());
|
|
}
|
|
|
|
|
|
bool WildcardMatch(const char* psz, const char* mask)
|
|
{
|
|
loop
|
|
{
|
|
switch (*mask)
|
|
{
|
|
case '\0':
|
|
return (*psz == '\0');
|
|
case '*':
|
|
return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask));
|
|
case '?':
|
|
if (*psz == '\0')
|
|
return false;
|
|
break;
|
|
default:
|
|
if (*psz != *mask)
|
|
return false;
|
|
break;
|
|
}
|
|
psz++;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
bool WildcardMatch(const string& str, const string& mask)
|
|
{
|
|
return WildcardMatch(str.c_str(), mask.c_str());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FormatException(char* pszMessage, 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)
|
|
snprintf(pszMessage, 1000,
|
|
"EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
|
|
else
|
|
snprintf(pszMessage, 1000,
|
|
"UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
|
|
}
|
|
|
|
void LogException(std::exception* pex, const char* pszThread)
|
|
{
|
|
char pszMessage[10000];
|
|
FormatException(pszMessage, pex, pszThread);
|
|
printf("\n%s", pszMessage);
|
|
}
|
|
|
|
void PrintException(std::exception* pex, const char* pszThread)
|
|
{
|
|
char pszMessage[10000];
|
|
FormatException(pszMessage, pex, pszThread);
|
|
printf("\n\n************************\n%s\n", pszMessage);
|
|
fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
|
|
strMiscWarning = pszMessage;
|
|
throw;
|
|
}
|
|
|
|
void PrintExceptionContinue(std::exception* pex, const char* pszThread)
|
|
{
|
|
char pszMessage[10000];
|
|
FormatException(pszMessage, pex, pszThread);
|
|
printf("\n\n************************\n%s\n", pszMessage);
|
|
fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
|
|
strMiscWarning = pszMessage;
|
|
}
|
|
|
|
boost::filesystem::path GetDefaultDataDir()
|
|
{
|
|
namespace fs = boost::filesystem;
|
|
// 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 == NULL || strlen(pszHome) == 0)
|
|
pathRet = fs::path("/");
|
|
else
|
|
pathRet = fs::path(pszHome);
|
|
#ifdef MAC_OSX
|
|
// Mac
|
|
pathRet /= "Library/Application Support";
|
|
fs::create_directory(pathRet);
|
|
return pathRet / "Bitcoin";
|
|
#else
|
|
// Unix
|
|
return pathRet / ".bitcoin";
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
const boost::filesystem::path &GetDataDir(bool fNetSpecific)
|
|
{
|
|
namespace fs = boost::filesystem;
|
|
|
|
static fs::path pathCached[2];
|
|
static CCriticalSection csPathCached;
|
|
static bool cachedPath[2] = {false, false};
|
|
|
|
fs::path &path = pathCached[fNetSpecific];
|
|
|
|
// This can be called during exceptions by printf, so we cache the
|
|
// value so we don't have to do memory allocations after that.
|
|
if (cachedPath[fNetSpecific])
|
|
return path;
|
|
|
|
LOCK(csPathCached);
|
|
|
|
if (mapArgs.count("-datadir")) {
|
|
path = fs::system_complete(mapArgs["-datadir"]);
|
|
if (!fs::is_directory(path)) {
|
|
path = "";
|
|
return path;
|
|
}
|
|
} else {
|
|
path = GetDefaultDataDir();
|
|
}
|
|
if (fNetSpecific && GetBoolArg("-testnet", false))
|
|
path /= "testnet";
|
|
|
|
fs::create_directory(path);
|
|
|
|
cachedPath[fNetSpecific]=true;
|
|
return path;
|
|
}
|
|
|
|
boost::filesystem::path GetConfigFile()
|
|
{
|
|
boost::filesystem::path pathConfigFile(GetArg("-conf", "bitcoin.conf"));
|
|
if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile;
|
|
return pathConfigFile;
|
|
}
|
|
|
|
void ReadConfigFile(map<string, string>& mapSettingsRet,
|
|
map<string, vector<string> >& mapMultiSettingsRet)
|
|
{
|
|
boost::filesystem::ifstream streamConfig(GetConfigFile());
|
|
if (!streamConfig.good())
|
|
return; // No bitcoin.conf file is OK
|
|
|
|
set<string> 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 bitcoin.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]);
|
|
}
|
|
}
|
|
|
|
boost::filesystem::path GetPidFile()
|
|
{
|
|
boost::filesystem::path pathPidFile(GetArg("-pid", "bitcoind.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(file, "%d\n", pid);
|
|
fclose(file);
|
|
}
|
|
}
|
|
|
|
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);
|
|
#else
|
|
int rc = std::rename(src.string().c_str(), dest.string().c_str());
|
|
return (rc == 0);
|
|
#endif /* WIN32 */
|
|
}
|
|
|
|
void FileCommit(FILE *fileout)
|
|
{
|
|
fflush(fileout); // harmless if redundantly called
|
|
#ifdef WIN32
|
|
_commit(_fileno(fileout));
|
|
#else
|
|
fsync(fileno(fileout));
|
|
#endif
|
|
}
|
|
|
|
int GetFilesize(FILE* file)
|
|
{
|
|
int nSavePos = ftell(file);
|
|
int nFilesize = -1;
|
|
if (fseek(file, 0, SEEK_END) == 0)
|
|
nFilesize = ftell(file);
|
|
fseek(file, nSavePos, SEEK_SET);
|
|
return nFilesize;
|
|
}
|
|
|
|
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");
|
|
if (file && GetFilesize(file) > 10 * 1000000)
|
|
{
|
|
// Restart the file with some of the end
|
|
char pch[200000];
|
|
fseek(file, -sizeof(pch), SEEK_END);
|
|
int nBytes = fread(pch, 1, sizeof(pch), file);
|
|
fclose(file);
|
|
|
|
file = fopen(pathLog.string().c_str(), "w");
|
|
if (file)
|
|
{
|
|
fwrite(pch, 1, nBytes, file);
|
|
fclose(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// "Never go to sea with two chronometers; take one or three."
|
|
// Our three time sources are:
|
|
// - System clock
|
|
// - Median of other nodes's clocks
|
|
// - The user (asking the user to fix the system clock if the first two disagree)
|
|
//
|
|
static int64 nMockTime = 0; // For unit testing
|
|
|
|
int64 GetTime()
|
|
{
|
|
if (nMockTime) return nMockTime;
|
|
|
|
return time(NULL);
|
|
}
|
|
|
|
void SetMockTime(int64 nMockTimeIn)
|
|
{
|
|
nMockTime = nMockTimeIn;
|
|
}
|
|
|
|
static int64 nTimeOffset = 0;
|
|
|
|
int64 GetAdjustedTime()
|
|
{
|
|
return GetTime() + nTimeOffset;
|
|
}
|
|
|
|
void AddTimeData(const CNetAddr& ip, int64 nTime)
|
|
{
|
|
int64 nOffsetSample = nTime - GetTime();
|
|
|
|
// Ignore duplicates
|
|
static set<CNetAddr> setKnown;
|
|
if (!setKnown.insert(ip).second)
|
|
return;
|
|
|
|
// Add data
|
|
vTimeOffsets.input(nOffsetSample);
|
|
printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
|
|
if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
|
|
{
|
|
int64 nMedian = vTimeOffsets.median();
|
|
std::vector<int64> vSorted = vTimeOffsets.sorted();
|
|
// Only let other nodes change our time by so much
|
|
if (abs64(nMedian) < 70 * 60)
|
|
{
|
|
nTimeOffset = nMedian;
|
|
}
|
|
else
|
|
{
|
|
nTimeOffset = 0;
|
|
|
|
static bool fDone;
|
|
if (!fDone)
|
|
{
|
|
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
|
|
bool fMatch = false;
|
|
BOOST_FOREACH(int64 nOffset, vSorted)
|
|
if (nOffset != 0 && abs64(nOffset) < 5 * 60)
|
|
fMatch = true;
|
|
|
|
if (!fMatch)
|
|
{
|
|
fDone = true;
|
|
string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly.");
|
|
strMiscWarning = strMessage;
|
|
printf("*** %s\n", strMessage.c_str());
|
|
ThreadSafeMessageBox(strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
if (fDebug) {
|
|
BOOST_FOREACH(int64 n, vSorted)
|
|
printf("%+"PRI64d" ", n);
|
|
printf("| ");
|
|
}
|
|
printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string FormatVersion(int nVersion)
|
|
{
|
|
if (nVersion%100 == 0)
|
|
return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100);
|
|
else
|
|
return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100);
|
|
}
|
|
|
|
string FormatFullVersion()
|
|
{
|
|
return CLIENT_BUILD;
|
|
}
|
|
|
|
// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014)
|
|
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << "/";
|
|
ss << name << ":" << FormatVersion(nClientVersion);
|
|
if (!comments.empty())
|
|
ss << "(" << boost::algorithm::join(comments, "; ") << ")";
|
|
ss << "/";
|
|
return ss.str();
|
|
}
|
|
|
|
#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);
|
|
}
|
|
|
|
printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
|
|
return fs::path("");
|
|
}
|
|
#endif
|