|
|
@ -40,75 +40,65 @@ enum BindFlags { |
|
|
|
// Shutdown
|
|
|
|
// Shutdown
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
void ExitTimeout(void* parg) |
|
|
|
//
|
|
|
|
{ |
|
|
|
// Thread management and startup/shutdown:
|
|
|
|
#ifdef WIN32 |
|
|
|
//
|
|
|
|
MilliSleep(5000); |
|
|
|
// The network-processing threads are all part of a thread group
|
|
|
|
ExitProcess(0); |
|
|
|
// created by AppInit() or the Qt main() function.
|
|
|
|
#endif |
|
|
|
//
|
|
|
|
} |
|
|
|
// A clean exit happens when StartShutdown() or the SIGTERM
|
|
|
|
|
|
|
|
// signal handler sets fRequestShutdown, which triggers
|
|
|
|
|
|
|
|
// the DetectShutdownThread(), which interrupts the main thread group.
|
|
|
|
|
|
|
|
// DetectShutdownThread() then exits, which causes AppInit() to
|
|
|
|
|
|
|
|
// continue (it .joins the shutdown thread).
|
|
|
|
|
|
|
|
// Shutdown() is then
|
|
|
|
|
|
|
|
// called to clean up database connections, and stop other
|
|
|
|
|
|
|
|
// threads that should only be stopped after the main network-processing
|
|
|
|
|
|
|
|
// threads have exited.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Note that if running -daemon the parent process returns from AppInit2
|
|
|
|
|
|
|
|
// before adding any threads to the threadGroup, so .join_all() returns
|
|
|
|
|
|
|
|
// immediately and the parent exits from main().
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Shutdown for Qt is very similar, only it uses a QTimer to detect
|
|
|
|
|
|
|
|
// fRequestShutdown getting set (either by RPC stop or SIGTERM)
|
|
|
|
|
|
|
|
// and then does the normal Qt shutdown thing.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
volatile bool fRequestShutdown = false; |
|
|
|
|
|
|
|
|
|
|
|
void StartShutdown() |
|
|
|
void StartShutdown() |
|
|
|
{ |
|
|
|
{ |
|
|
|
#ifdef QT_GUI |
|
|
|
fRequestShutdown = true; |
|
|
|
// ensure we leave the Qt main loop for a clean GUI exit (Shutdown() is called in bitcoin.cpp afterwards)
|
|
|
|
|
|
|
|
uiInterface.QueueShutdown(); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
// Without UI, Shutdown() can simply be started in a new thread
|
|
|
|
|
|
|
|
NewThread(Shutdown, NULL); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static CCoinsViewDB *pcoinsdbview; |
|
|
|
static CCoinsViewDB *pcoinsdbview; |
|
|
|
|
|
|
|
|
|
|
|
void Shutdown(void* parg) |
|
|
|
void Shutdown() |
|
|
|
{ |
|
|
|
{ |
|
|
|
static CCriticalSection cs_Shutdown; |
|
|
|
static CCriticalSection cs_Shutdown; |
|
|
|
static bool fTaken; |
|
|
|
TRY_LOCK(cs_Shutdown, lockShutdown); |
|
|
|
|
|
|
|
if (!lockShutdown) return; |
|
|
|
|
|
|
|
|
|
|
|
// Make this thread recognisable as the shutdown thread
|
|
|
|
|
|
|
|
RenameThread("bitcoin-shutoff"); |
|
|
|
RenameThread("bitcoin-shutoff"); |
|
|
|
nTransactionsUpdated++; |
|
|
|
nTransactionsUpdated++; |
|
|
|
StopRPCThreads(); |
|
|
|
StopRPCThreads(); |
|
|
|
bitdb.Flush(false); |
|
|
|
bitdb.Flush(false); |
|
|
|
StopNode(); |
|
|
|
StopNode(); |
|
|
|
{ |
|
|
|
|
|
|
|
fShutdown = true; |
|
|
|
|
|
|
|
fRequestShutdown = true; |
|
|
|
|
|
|
|
nTransactionsUpdated++; |
|
|
|
|
|
|
|
StopRPCThreads(); |
|
|
|
|
|
|
|
bitdb.Flush(false); |
|
|
|
|
|
|
|
StopNode(); |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(cs_main); |
|
|
|
LOCK(cs_main); |
|
|
|
if (pblocktree) |
|
|
|
if (pblocktree) |
|
|
|
pblocktree->Flush(); |
|
|
|
pblocktree->Flush(); |
|
|
|
if (pcoinsTip) |
|
|
|
if (pcoinsTip) |
|
|
|
pcoinsTip->Flush(); |
|
|
|
pcoinsTip->Flush(); |
|
|
|
delete pcoinsTip; |
|
|
|
delete pcoinsTip; pcoinsTip = NULL; |
|
|
|
delete pcoinsdbview; |
|
|
|
delete pcoinsdbview; pcoinsdbview = NULL; |
|
|
|
delete pblocktree; |
|
|
|
delete pblocktree; pblocktree = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
bitdb.Flush(true); |
|
|
|
bitdb.Flush(true); |
|
|
|
boost::filesystem::remove(GetPidFile()); |
|
|
|
boost::filesystem::remove(GetPidFile()); |
|
|
|
UnregisterWallet(pwalletMain); |
|
|
|
UnregisterWallet(pwalletMain); |
|
|
|
delete pwalletMain; |
|
|
|
delete pwalletMain; |
|
|
|
NewThread(ExitTimeout, NULL); |
|
|
|
|
|
|
|
MilliSleep(50); |
|
|
|
|
|
|
|
printf("Bitcoin exited\n\n"); |
|
|
|
|
|
|
|
fExit = true; |
|
|
|
|
|
|
|
#ifndef QT_GUI |
|
|
|
|
|
|
|
// ensure non-UI client gets exited here, but let Bitcoin-Qt reach 'return 0;' in bitcoin.cpp
|
|
|
|
|
|
|
|
exit(0); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
while (!fExit) |
|
|
|
|
|
|
|
MilliSleep(500); |
|
|
|
|
|
|
|
MilliSleep(100); |
|
|
|
|
|
|
|
ExitThread(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -116,9 +106,13 @@ void Shutdown(void* parg) |
|
|
|
//
|
|
|
|
//
|
|
|
|
void DetectShutdownThread(boost::thread_group* threadGroup) |
|
|
|
void DetectShutdownThread(boost::thread_group* threadGroup) |
|
|
|
{ |
|
|
|
{ |
|
|
|
while (fRequestShutdown == false) |
|
|
|
// Tell the main threads to shutdown.
|
|
|
|
|
|
|
|
while (!fRequestShutdown) |
|
|
|
|
|
|
|
{ |
|
|
|
MilliSleep(200); |
|
|
|
MilliSleep(200); |
|
|
|
|
|
|
|
if (fRequestShutdown) |
|
|
|
threadGroup->interrupt_all(); |
|
|
|
threadGroup->interrupt_all(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void HandleSIGTERM(int) |
|
|
|
void HandleSIGTERM(int) |
|
|
@ -143,6 +137,8 @@ void HandleSIGHUP(int) |
|
|
|
bool AppInit(int argc, char* argv[]) |
|
|
|
bool AppInit(int argc, char* argv[]) |
|
|
|
{ |
|
|
|
{ |
|
|
|
boost::thread_group threadGroup; |
|
|
|
boost::thread_group threadGroup; |
|
|
|
|
|
|
|
boost::thread* detectShutdownThread = NULL; |
|
|
|
|
|
|
|
|
|
|
|
bool fRet = false; |
|
|
|
bool fRet = false; |
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
@ -154,7 +150,7 @@ bool AppInit(int argc, char* argv[]) |
|
|
|
if (!boost::filesystem::is_directory(GetDataDir(false))) |
|
|
|
if (!boost::filesystem::is_directory(GetDataDir(false))) |
|
|
|
{ |
|
|
|
{ |
|
|
|
fprintf(stderr, "Error: Specified directory does not exist\n"); |
|
|
|
fprintf(stderr, "Error: Specified directory does not exist\n"); |
|
|
|
Shutdown(NULL); |
|
|
|
Shutdown(); |
|
|
|
} |
|
|
|
} |
|
|
|
ReadConfigFile(mapArgs, mapMultiArgs); |
|
|
|
ReadConfigFile(mapArgs, mapMultiArgs); |
|
|
|
|
|
|
|
|
|
|
@ -184,7 +180,31 @@ bool AppInit(int argc, char* argv[]) |
|
|
|
int ret = CommandLineRPC(argc, argv); |
|
|
|
int ret = CommandLineRPC(argc, argv); |
|
|
|
exit(ret); |
|
|
|
exit(ret); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#if !defined(WIN32) |
|
|
|
|
|
|
|
fDaemon = GetBoolArg("-daemon"); |
|
|
|
|
|
|
|
if (fDaemon) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Daemonize
|
|
|
|
|
|
|
|
pid_t pid = fork(); |
|
|
|
|
|
|
|
if (pid < 0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (pid > 0) // Parent process, pid is child process id
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CreatePidFile(GetPidFile(), pid); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Child process falls through to rest of initialization
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pid_t sid = setsid(); |
|
|
|
|
|
|
|
if (sid < 0) |
|
|
|
|
|
|
|
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); |
|
|
|
fRet = AppInit2(threadGroup); |
|
|
|
fRet = AppInit2(threadGroup); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (std::exception& e) { |
|
|
|
catch (std::exception& e) { |
|
|
@ -192,12 +212,20 @@ bool AppInit(int argc, char* argv[]) |
|
|
|
} catch (...) { |
|
|
|
} catch (...) { |
|
|
|
PrintExceptionContinue(NULL, "AppInit()"); |
|
|
|
PrintExceptionContinue(NULL, "AppInit()"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (!fRet) |
|
|
|
if (!fRet) { |
|
|
|
{ |
|
|
|
if (detectShutdownThread) |
|
|
|
Shutdown(NULL); |
|
|
|
detectShutdownThread->interrupt(); |
|
|
|
threadGroup.interrupt_all(); |
|
|
|
threadGroup.interrupt_all(); |
|
|
|
threadGroup.join_all(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (detectShutdownThread) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
detectShutdownThread->join(); |
|
|
|
|
|
|
|
delete detectShutdownThread; |
|
|
|
|
|
|
|
detectShutdownThread = NULL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Shutdown(); |
|
|
|
|
|
|
|
|
|
|
|
return fRet; |
|
|
|
return fRet; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -214,7 +242,7 @@ int main(int argc, char* argv[]) |
|
|
|
if (fRet && fDaemon) |
|
|
|
if (fRet && fDaemon) |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
|
|
return 1; |
|
|
|
return (fRet ? 0 : 1); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
@ -302,7 +330,7 @@ std::string HelpMessage() |
|
|
|
" -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n" + |
|
|
|
" -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n" + |
|
|
|
" -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" + |
|
|
|
" -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" + |
|
|
|
" -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + |
|
|
|
" -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + |
|
|
|
" -rpcthreads=<n> " + _("Use this mean threads to service RPC calls (default: 4)") + "\n" + |
|
|
|
" -rpcthreads=<n> " + _("Use this many threads to service RPC calls (default: 4)") + "\n" + |
|
|
|
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + |
|
|
|
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + |
|
|
|
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + |
|
|
|
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + |
|
|
|
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + |
|
|
|
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + |
|
|
@ -440,8 +468,6 @@ bool AppInit2(boost::thread_group& threadGroup) |
|
|
|
sigaction(SIGHUP, &sa_hup, NULL); |
|
|
|
sigaction(SIGHUP, &sa_hup, NULL); |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
threadGroup.create_thread(boost::bind(&DetectShutdownThread, &threadGroup)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ********************************************************* Step 2: parameter interactions
|
|
|
|
// ********************************************************* Step 2: parameter interactions
|
|
|
|
|
|
|
|
|
|
|
|
fTestNet = GetBoolArg("-testnet"); |
|
|
|
fTestNet = GetBoolArg("-testnet"); |
|
|
@ -499,12 +525,6 @@ bool AppInit2(boost::thread_group& threadGroup) |
|
|
|
else |
|
|
|
else |
|
|
|
fDebugNet = GetBoolArg("-debugnet"); |
|
|
|
fDebugNet = GetBoolArg("-debugnet"); |
|
|
|
|
|
|
|
|
|
|
|
#if !defined(WIN32) && !defined(QT_GUI) |
|
|
|
|
|
|
|
fDaemon = GetBoolArg("-daemon"); |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
fDaemon = false; |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (fDaemon) |
|
|
|
if (fDaemon) |
|
|
|
fServer = true; |
|
|
|
fServer = true; |
|
|
|
else |
|
|
|
else |
|
|
@ -552,28 +572,6 @@ bool AppInit2(boost::thread_group& threadGroup) |
|
|
|
if (!lock.try_lock()) |
|
|
|
if (!lock.try_lock()) |
|
|
|
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); |
|
|
|
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); |
|
|
|
|
|
|
|
|
|
|
|
#if !defined(WIN32) && !defined(QT_GUI) |
|
|
|
|
|
|
|
if (fDaemon) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Daemonize
|
|
|
|
|
|
|
|
pid_t pid = fork(); |
|
|
|
|
|
|
|
if (pid < 0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (pid > 0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CreatePidFile(GetPidFile(), pid); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pid_t sid = setsid(); |
|
|
|
|
|
|
|
if (sid < 0) |
|
|
|
|
|
|
|
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (GetBoolArg("-shrinkdebugfile", !fDebug)) |
|
|
|
if (GetBoolArg("-shrinkdebugfile", !fDebug)) |
|
|
|
ShrinkDebugFile(); |
|
|
|
ShrinkDebugFile(); |
|
|
|
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); |
|
|
|
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); |
|
|
@ -1011,8 +1009,7 @@ bool AppInit2(boost::thread_group& threadGroup) |
|
|
|
printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); |
|
|
|
printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); |
|
|
|
printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); |
|
|
|
printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); |
|
|
|
|
|
|
|
|
|
|
|
if (!NewThread(StartNode, (void*)&threadGroup)) |
|
|
|
StartNode(threadGroup); |
|
|
|
InitError(_("Error: could not start node")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (fServer) |
|
|
|
if (fServer) |
|
|
|
StartRPCThreads(); |
|
|
|
StartRPCThreads(); |
|
|
@ -1030,12 +1027,8 @@ bool AppInit2(boost::thread_group& threadGroup) |
|
|
|
// Add wallet transactions that aren't already in a block to mapTransactions
|
|
|
|
// Add wallet transactions that aren't already in a block to mapTransactions
|
|
|
|
pwalletMain->ReacceptWalletTransactions(); |
|
|
|
pwalletMain->ReacceptWalletTransactions(); |
|
|
|
|
|
|
|
|
|
|
|
#if !defined(QT_GUI) |
|
|
|
// Run a thread to flush wallet periodically
|
|
|
|
// Loop until process is exit()ed from shutdown() function,
|
|
|
|
threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); |
|
|
|
// called from ThreadRPCServer thread when a "stop" command is received.
|
|
|
|
|
|
|
|
while (1) |
|
|
|
|
|
|
|
MilliSleep(5000); |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return !fRequestShutdown; |
|
|
|
} |
|
|
|
} |
|
|
|