run as daemon without GUI,
hooked wxApp::Initialize to ignore gtk-init-check failure if no GUI, fork to daemonize, rpc getinfo, getconnectioncount, getbalance, getgenerate, setgenerate, -- version 0.2.6
This commit is contained in:
parent
ee618d7540
commit
30158c77e9
5
main.cpp
5
main.cpp
|
@ -1315,8 +1315,7 @@ bool CBlock::AcceptBlock()
|
||||||
if (nTime <= pindexPrev->GetMedianTimePast())
|
if (nTime <= pindexPrev->GetMedianTimePast())
|
||||||
return error("AcceptBlock() : block's timestamp is too early");
|
return error("AcceptBlock() : block's timestamp is too early");
|
||||||
|
|
||||||
// Check that all transactions are finalized (starting around Mar 2010)
|
// Check that all transactions are finalized
|
||||||
if (nBestHeight > 36000)
|
|
||||||
foreach(const CTransaction& tx, vtx)
|
foreach(const CTransaction& tx, vtx)
|
||||||
if (!tx.IsFinal(nTime))
|
if (!tx.IsFinal(nTime))
|
||||||
return error("AcceptBlock() : contains a non-final transaction");
|
return error("AcceptBlock() : contains a non-final transaction");
|
||||||
|
@ -1336,7 +1335,7 @@ bool CBlock::AcceptBlock()
|
||||||
return error("AcceptBlock() : AddToBlockIndex failed");
|
return error("AcceptBlock() : AddToBlockIndex failed");
|
||||||
|
|
||||||
// Don't relay old inventory during initial block download.
|
// Don't relay old inventory during initial block download.
|
||||||
// Please keep this constant updated to a few thousand below current block count.
|
// Please keep this number updated to a few thousand below current block count.
|
||||||
if (hashBestChain == hash && nBestHeight > 40000)
|
if (hashBestChain == hash && nBestHeight > 40000)
|
||||||
RelayInventory(CInv(MSG_BLOCK, hash));
|
RelayInventory(CInv(MSG_BLOCK, hash));
|
||||||
|
|
||||||
|
|
4
main.h
4
main.h
|
@ -1384,6 +1384,9 @@ public:
|
||||||
CPrivKey vchPrivKey;
|
CPrivKey vchPrivKey;
|
||||||
int64 nTimeCreated;
|
int64 nTimeCreated;
|
||||||
int64 nTimeExpires;
|
int64 nTimeExpires;
|
||||||
|
string strComment;
|
||||||
|
//// todo: add something to note what created it (user, getnewaddress, change)
|
||||||
|
//// maybe should have a map<string, string> property map
|
||||||
|
|
||||||
CWalletKey(int64 nTimeExpiresIn=0)
|
CWalletKey(int64 nTimeExpiresIn=0)
|
||||||
{
|
{
|
||||||
|
@ -1398,6 +1401,7 @@ public:
|
||||||
READWRITE(vchPrivKey);
|
READWRITE(vchPrivKey);
|
||||||
READWRITE(nTimeCreated);
|
READWRITE(nTimeCreated);
|
||||||
READWRITE(nTimeExpires);
|
READWRITE(nTimeExpires);
|
||||||
|
READWRITE(strComment);
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
112
rpc.cpp
112
rpc.cpp
|
@ -66,6 +66,17 @@ Value getblocknumber(const Array& params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value getconnectioncount(const Array& params)
|
||||||
|
{
|
||||||
|
if (params.size() != 0)
|
||||||
|
throw runtime_error(
|
||||||
|
"getconnectioncount (no parameters)\n"
|
||||||
|
"Returns the number of connections to other nodes.");
|
||||||
|
|
||||||
|
return (int)vNodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Value getdifficulty(const Array& params)
|
Value getdifficulty(const Array& params)
|
||||||
{
|
{
|
||||||
if (params.size() != 0)
|
if (params.size() != 0)
|
||||||
|
@ -85,6 +96,71 @@ Value getdifficulty(const Array& params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value getbalance(const Array& params)
|
||||||
|
{
|
||||||
|
if (params.size() != 0)
|
||||||
|
throw runtime_error(
|
||||||
|
"getbalance (no parameters)\n"
|
||||||
|
"Returns the server's available balance.");
|
||||||
|
|
||||||
|
return ((double)GetBalance() / (double)COIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value getgenerate(const Array& params)
|
||||||
|
{
|
||||||
|
if (params.size() != 0)
|
||||||
|
throw runtime_error(
|
||||||
|
"getgenerate (no parameters)\n"
|
||||||
|
"Returns true or false.");
|
||||||
|
|
||||||
|
return (bool)fGenerateBitcoins;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value setgenerate(const Array& params)
|
||||||
|
{
|
||||||
|
if (params.size() < 1 || params.size() > 2)
|
||||||
|
throw runtime_error(
|
||||||
|
"setgenerate <generate> [genproclimit]\n"
|
||||||
|
"<generate> is true or false to turn generation on or off.\n"
|
||||||
|
"Generation is limited to [genproclimit] processors, -1 is unlimited.");
|
||||||
|
|
||||||
|
bool fGenerate = true;
|
||||||
|
if (params.size() > 0)
|
||||||
|
fGenerate = params[0].get_bool();
|
||||||
|
|
||||||
|
if (params.size() > 1)
|
||||||
|
{
|
||||||
|
int nGenProcLimit = params[1].get_int();
|
||||||
|
fLimitProcessors = (nGenProcLimit != -1);
|
||||||
|
CWalletDB().WriteSetting("fLimitProcessors", fLimitProcessors);
|
||||||
|
if (nGenProcLimit != -1)
|
||||||
|
CWalletDB().WriteSetting("nLimitProcessors", nLimitProcessors = nGenProcLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateBitcoins(fGenerate);
|
||||||
|
return Value::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value getinfo(const Array& params)
|
||||||
|
{
|
||||||
|
if (params.size() != 0)
|
||||||
|
throw runtime_error(
|
||||||
|
"getinfo (no parameters)");
|
||||||
|
|
||||||
|
Object obj;
|
||||||
|
obj.push_back(Pair("balance", (double)GetBalance() / (double)COIN));
|
||||||
|
obj.push_back(Pair("blocks", (int)nBestHeight + 1));
|
||||||
|
obj.push_back(Pair("connections", (int)vNodes.size()));
|
||||||
|
obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
|
||||||
|
obj.push_back(Pair("generate", (bool)fGenerateBitcoins));
|
||||||
|
obj.push_back(Pair("genproclimit", (int)(fLimitProcessors ? nLimitProcessors : -1)));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Value getnewaddress(const Array& params)
|
Value getnewaddress(const Array& params)
|
||||||
{
|
{
|
||||||
if (params.size() > 1)
|
if (params.size() > 1)
|
||||||
|
@ -102,7 +178,6 @@ Value getnewaddress(const Array& params)
|
||||||
// Generate a new key that is added to wallet
|
// Generate a new key that is added to wallet
|
||||||
string strAddress = PubKeyToAddress(GenerateNewKey());
|
string strAddress = PubKeyToAddress(GenerateNewKey());
|
||||||
|
|
||||||
if (params.size() > 0)
|
|
||||||
SetAddressBookName(strAddress, strLabel);
|
SetAddressBookName(strAddress, strLabel);
|
||||||
return strAddress;
|
return strAddress;
|
||||||
}
|
}
|
||||||
|
@ -214,10 +289,10 @@ Value getallreceived(const Array& params)
|
||||||
"getallreceived [minconf=1]\n"
|
"getallreceived [minconf=1]\n"
|
||||||
"[minconf] is the minimum number of confirmations before payments are included.\n"
|
"[minconf] is the minimum number of confirmations before payments are included.\n"
|
||||||
"Returns an array of objects containing:\n"
|
"Returns an array of objects containing:\n"
|
||||||
" \"address\" : bitcoin address\n"
|
" \"address\" : receiving address\n"
|
||||||
" \"amount\" : total amount received by the address\n"
|
" \"amount\" : total amount received by the address\n"
|
||||||
" \"conf\" : number of confirmations\n"
|
" \"confirmations\" : number of confirmations of the most recent transaction included\n"
|
||||||
" \"label\" : the label set for this address when it was created by getnewaddress");
|
" \"label\" : the label of the receiving address");
|
||||||
|
|
||||||
// Minimum confirmations
|
// Minimum confirmations
|
||||||
int nMinDepth = 1;
|
int nMinDepth = 1;
|
||||||
|
@ -235,8 +310,17 @@ Value getallreceived(const Array& params)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int nDepth = wtx.GetDepthInMainChain();
|
int nDepth = wtx.GetDepthInMainChain();
|
||||||
if (nDepth >= nMinDepth)
|
if (nDepth < nMinDepth)
|
||||||
{
|
continue;
|
||||||
|
|
||||||
|
// Filter out debits and payments to self, which may have change return
|
||||||
|
// we don't want to count.
|
||||||
|
int64 nCredit = wtx.GetCredit(true);
|
||||||
|
int64 nDebit = wtx.GetDebit();
|
||||||
|
int64 nNet = nCredit - nDebit;
|
||||||
|
if (nNet <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
foreach(const CTxOut& txout, wtx.vout)
|
foreach(const CTxOut& txout, wtx.vout)
|
||||||
{
|
{
|
||||||
uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160();
|
uint160 hash160 = txout.scriptPubKey.GetBitcoinAddressHash160();
|
||||||
|
@ -249,7 +333,6 @@ Value getallreceived(const Array& params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
Array ret;
|
Array ret;
|
||||||
|
@ -266,7 +349,7 @@ Value getallreceived(const Array& params)
|
||||||
Object obj;
|
Object obj;
|
||||||
obj.push_back(Pair("address", strAddress));
|
obj.push_back(Pair("address", strAddress));
|
||||||
obj.push_back(Pair("amount", (double)(*it).second.nAmount / (double)COIN));
|
obj.push_back(Pair("amount", (double)(*it).second.nAmount / (double)COIN));
|
||||||
obj.push_back(Pair("conf", (*it).second.nConf));
|
obj.push_back(Pair("confirmations", (*it).second.nConf));
|
||||||
obj.push_back(Pair("label", strLabel));
|
obj.push_back(Pair("label", strLabel));
|
||||||
ret.push_back(obj);
|
ret.push_back(obj);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +373,12 @@ pair<string, rpcfn_type> pCallTable[] =
|
||||||
make_pair("stop", &stop),
|
make_pair("stop", &stop),
|
||||||
make_pair("getblockcount", &getblockcount),
|
make_pair("getblockcount", &getblockcount),
|
||||||
make_pair("getblocknumber", &getblocknumber),
|
make_pair("getblocknumber", &getblocknumber),
|
||||||
|
make_pair("getconnectioncount", &getconnectioncount),
|
||||||
make_pair("getdifficulty", &getdifficulty),
|
make_pair("getdifficulty", &getdifficulty),
|
||||||
|
make_pair("getbalance", &getbalance),
|
||||||
|
make_pair("getgenerate", &getgenerate),
|
||||||
|
make_pair("setgenerate", &setgenerate),
|
||||||
|
make_pair("getinfo", &getinfo),
|
||||||
make_pair("getnewaddress", &getnewaddress),
|
make_pair("getnewaddress", &getnewaddress),
|
||||||
make_pair("sendtoaddress", &sendtoaddress),
|
make_pair("sendtoaddress", &sendtoaddress),
|
||||||
make_pair("listtransactions", &listtransactions),
|
make_pair("listtransactions", &listtransactions),
|
||||||
|
@ -568,9 +656,13 @@ int CommandLineRPC(int argc, char *argv[])
|
||||||
Array params;
|
Array params;
|
||||||
for (int i = 2; i < argc; i++)
|
for (int i = 2; i < argc; i++)
|
||||||
params.push_back(argv[i]);
|
params.push_back(argv[i]);
|
||||||
|
|
||||||
// Special case other types
|
|
||||||
int n = params.size();
|
int n = params.size();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Special case other types
|
||||||
|
//
|
||||||
|
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
|
||||||
|
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||||
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
|
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
|
||||||
if (strMethod == "listtransactions" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
if (strMethod == "listtransactions" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||||
if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);
|
if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);
|
||||||
|
|
|
@ -19,7 +19,7 @@ class CScript;
|
||||||
class CDataStream;
|
class CDataStream;
|
||||||
class CAutoFile;
|
class CAutoFile;
|
||||||
|
|
||||||
static const int VERSION = 205;
|
static const int VERSION = 206;
|
||||||
static const char* pszSubVer = ".0";
|
static const char* pszSubVer = ".0";
|
||||||
|
|
||||||
|
|
||||||
|
|
94
ui.cpp
94
ui.cpp
|
@ -26,6 +26,20 @@ int fMinimizeOnClose = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int MyMessageBox(const wxString& message, const wxString& caption="Message", int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1)
|
||||||
|
{
|
||||||
|
if (fDaemon)
|
||||||
|
{
|
||||||
|
printf("wxMessageBox %s: %s\n", string(caption).c_str(), string(message).c_str());
|
||||||
|
fprintf(stderr, "%s: %s\n", string(caption).c_str(), string(message).c_str());
|
||||||
|
return wxOK;
|
||||||
|
}
|
||||||
|
return wxMessageBox(message, caption, style, parent, x, y);
|
||||||
|
}
|
||||||
|
#define wxMessageBox MyMessageBox
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,16 +223,10 @@ void CalledMessageBox(const string& message, const string& caption, int style, w
|
||||||
|
|
||||||
int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
|
int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
|
||||||
{
|
{
|
||||||
if (fDaemon)
|
|
||||||
{
|
|
||||||
printf("wxMessageBox %s: %s\n", caption.c_str(), message.c_str());
|
|
||||||
return wxOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
return wxMessageBox(message, caption, style, parent, x, y);
|
return wxMessageBox(message, caption, style, parent, x, y);
|
||||||
#else
|
#else
|
||||||
if (wxThread::IsMain())
|
if (wxThread::IsMain() || fDaemon)
|
||||||
{
|
{
|
||||||
return wxMessageBox(message, caption, style, parent, x, y);
|
return wxMessageBox(message, caption, style, parent, x, y);
|
||||||
}
|
}
|
||||||
|
@ -563,7 +571,7 @@ string SingleLine(const string& strIn)
|
||||||
bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
|
bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
|
||||||
{
|
{
|
||||||
int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
|
int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
|
||||||
int64 nCredit = wtx.GetCredit();
|
int64 nCredit = wtx.GetCredit(true);
|
||||||
int64 nDebit = wtx.GetDebit();
|
int64 nDebit = wtx.GetDebit();
|
||||||
int64 nNet = nCredit - nDebit;
|
int64 nNet = nCredit - nDebit;
|
||||||
uint256 hash = wtx.GetHash();
|
uint256 hash = wtx.GetHash();
|
||||||
|
@ -2571,6 +2579,9 @@ public:
|
||||||
bool OnInit2();
|
bool OnInit2();
|
||||||
int OnExit();
|
int OnExit();
|
||||||
|
|
||||||
|
// Hook Initialize so we can start without GUI
|
||||||
|
virtual bool Initialize(int& argc, wxChar** argv);
|
||||||
|
|
||||||
// 2nd-level exception handling: we get all the exceptions occurring in any
|
// 2nd-level exception handling: we get all the exceptions occurring in any
|
||||||
// event handler here
|
// event handler here
|
||||||
virtual bool OnExceptionInMainLoop();
|
virtual bool OnExceptionInMainLoop();
|
||||||
|
@ -2586,6 +2597,64 @@ public:
|
||||||
|
|
||||||
IMPLEMENT_APP(CMyApp)
|
IMPLEMENT_APP(CMyApp)
|
||||||
|
|
||||||
|
bool CMyApp::Initialize(int& argc, wxChar** argv)
|
||||||
|
{
|
||||||
|
if (argc > 1 && argv[1][0] != '-' && (!fWindows || argv[1][0] != '/') &&
|
||||||
|
wxString(argv[1]) != "start")
|
||||||
|
{
|
||||||
|
fCommandLine = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// wxApp::Initialize will remove environment-specific parameters,
|
||||||
|
// so it's too early to call ParseParameters yet
|
||||||
|
for (int i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
wxString str = argv[i];
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
if (str.size() >= 1 && str[0] == '/')
|
||||||
|
str[0] = '-';
|
||||||
|
str = str.MakeLower();
|
||||||
|
#endif
|
||||||
|
// haven't decided which argument to use for this yet
|
||||||
|
if (str == "-daemon" || str == "-d" || str == "start")
|
||||||
|
fDaemon = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
if (fDaemon || fCommandLine)
|
||||||
|
{
|
||||||
|
// Call the original Initialize while suppressing error messages
|
||||||
|
// and ignoring failure. If unable to initialize GTK, it fails
|
||||||
|
// near the end so hopefully the last few things don't matter.
|
||||||
|
{
|
||||||
|
wxLogNull logNo;
|
||||||
|
wxApp::Initialize(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fDaemon)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "bitcoin server starting\n");
|
||||||
|
|
||||||
|
// Daemonize
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pid > 0)
|
||||||
|
pthread_exit((void*)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return wxApp::Initialize(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
bool CMyApp::OnInit()
|
bool CMyApp::OnInit()
|
||||||
{
|
{
|
||||||
bool fRet = false;
|
bool fRet = false;
|
||||||
|
@ -2650,7 +2719,7 @@ bool CMyApp::OnInit2()
|
||||||
//
|
//
|
||||||
// Parameters
|
// Parameters
|
||||||
//
|
//
|
||||||
if (argc > 1 && argv[1][0] != '-' && argv[1][0] != '/')
|
if (fCommandLine)
|
||||||
{
|
{
|
||||||
int ret = CommandLineRPC(argc, argv);
|
int ret = CommandLineRPC(argc, argv);
|
||||||
exit(ret);
|
exit(ret);
|
||||||
|
@ -2696,13 +2765,6 @@ bool CMyApp::OnInit2()
|
||||||
if (mapArgs.count("-printtodebugger"))
|
if (mapArgs.count("-printtodebugger"))
|
||||||
fPrintToDebugger = true;
|
fPrintToDebugger = true;
|
||||||
|
|
||||||
if (mapArgs.count("-daemon") || mapArgs.count("-d"))
|
|
||||||
{
|
|
||||||
fDaemon = true;
|
|
||||||
/// todo: need to fork
|
|
||||||
/// should it fork after the bind/single instance stuff?
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fDebug && !pszSetDataDir[0])
|
if (!fDebug && !pszSetDataDir[0])
|
||||||
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");
|
||||||
|
|
4
util.cpp
4
util.cpp
|
@ -13,6 +13,7 @@ bool fPrintToDebugger = false;
|
||||||
char pszSetDataDir[MAX_PATH] = "";
|
char pszSetDataDir[MAX_PATH] = "";
|
||||||
bool fShutdown = false;
|
bool fShutdown = false;
|
||||||
bool fDaemon = false;
|
bool fDaemon = false;
|
||||||
|
bool fCommandLine = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -500,7 +501,8 @@ void PrintException(std::exception* pex, const char* pszThread)
|
||||||
char pszMessage[1000];
|
char pszMessage[1000];
|
||||||
FormatException(pszMessage, pex, pszThread);
|
FormatException(pszMessage, pex, pszThread);
|
||||||
printf("\n\n************************\n%s\n", pszMessage);
|
printf("\n\n************************\n%s\n", pszMessage);
|
||||||
if (wxTheApp)
|
fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
|
||||||
|
if (wxTheApp && !fDaemon)
|
||||||
wxMessageBox(pszMessage, "Error", wxOK | wxICON_ERROR);
|
wxMessageBox(pszMessage, "Error", wxOK | wxICON_ERROR);
|
||||||
throw;
|
throw;
|
||||||
//DebugBreak();
|
//DebugBreak();
|
||||||
|
|
1
util.h
1
util.h
|
@ -121,6 +121,7 @@ extern bool fPrintToDebugger;
|
||||||
extern char pszSetDataDir[MAX_PATH];
|
extern char pszSetDataDir[MAX_PATH];
|
||||||
extern bool fShutdown;
|
extern bool fShutdown;
|
||||||
extern bool fDaemon;
|
extern bool fDaemon;
|
||||||
|
extern bool fCommandLine;
|
||||||
|
|
||||||
void RandAddSeed();
|
void RandAddSeed();
|
||||||
void RandAddSeedPerfmon();
|
void RandAddSeedPerfmon();
|
||||||
|
|
Loading…
Reference in New Issue