GUI-less build target bitcoind that links to wxBase and shouldn't need GTK,
[novacoin.git] / init.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto\r
2 // Distributed under the MIT/X11 software license, see the accompanying\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
4 \r
5 #include "headers.h"\r
6 \r
7 \r
8 \r
9 \r
10 void ExitTimeout(void* parg)\r
11 {\r
12 #ifdef __WXMSW__\r
13     Sleep(5000);\r
14     ExitProcess(0);\r
15 #endif\r
16 }\r
17 \r
18 void Shutdown(void* parg)\r
19 {\r
20     static CCriticalSection cs_Shutdown;\r
21     static bool fTaken;\r
22     bool fFirstThread;\r
23     CRITICAL_BLOCK(cs_Shutdown)\r
24     {\r
25         fFirstThread = !fTaken;\r
26         fTaken = true;\r
27     }\r
28     static bool fExit;\r
29     if (fFirstThread)\r
30     {\r
31         fShutdown = true;\r
32         nTransactionsUpdated++;\r
33         DBFlush(false);\r
34         StopNode();\r
35         DBFlush(true);\r
36         CreateThread(ExitTimeout, NULL);\r
37         Sleep(50);\r
38         printf("Bitcoin exiting\n\n");\r
39         fExit = true;\r
40         exit(0);\r
41     }\r
42     else\r
43     {\r
44         while (!fExit)\r
45             Sleep(500);\r
46         Sleep(100);\r
47         ExitThread(0);\r
48     }\r
49 }\r
50 \r
51 \r
52 \r
53 \r
54 \r
55 \r
56 //////////////////////////////////////////////////////////////////////////////\r
57 //\r
58 // Startup folder\r
59 //\r
60 \r
61 #ifdef __WXMSW__\r
62 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);\r
63 \r
64 string MyGetSpecialFolderPath(int nFolder, bool fCreate)\r
65 {\r
66     char pszPath[MAX_PATH+100] = "";\r
67 \r
68     // SHGetSpecialFolderPath is not usually available on NT 4.0\r
69     HMODULE hShell32 = LoadLibraryA("shell32.dll");\r
70     if (hShell32)\r
71     {\r
72         PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =\r
73             (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");\r
74         if (pSHGetSpecialFolderPath)\r
75             (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);\r
76         FreeModule(hShell32);\r
77     }\r
78 \r
79     // Backup option\r
80     if (pszPath[0] == '\0')\r
81     {\r
82         if (nFolder == CSIDL_STARTUP)\r
83         {\r
84             strcpy(pszPath, getenv("USERPROFILE"));\r
85             strcat(pszPath, "\\Start Menu\\Programs\\Startup");\r
86         }\r
87         else if (nFolder == CSIDL_APPDATA)\r
88         {\r
89             strcpy(pszPath, getenv("APPDATA"));\r
90         }\r
91     }\r
92 \r
93     return pszPath;\r
94 }\r
95 \r
96 string StartupShortcutPath()\r
97 {\r
98     return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";\r
99 }\r
100 \r
101 bool GetStartOnSystemStartup()\r
102 {\r
103     return wxFileExists(StartupShortcutPath());\r
104 }\r
105 \r
106 void SetStartOnSystemStartup(bool fAutoStart)\r
107 {\r
108     // If the shortcut exists already, remove it for updating\r
109     remove(StartupShortcutPath().c_str());\r
110 \r
111     if (fAutoStart)\r
112     {\r
113         CoInitialize(NULL);\r
114 \r
115         // Get a pointer to the IShellLink interface.\r
116         IShellLink* psl = NULL;\r
117         HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,\r
118                                 CLSCTX_INPROC_SERVER, IID_IShellLink,\r
119                                 reinterpret_cast<void**>(&psl));\r
120 \r
121         if (SUCCEEDED(hres))\r
122         {\r
123             // Get the current executable path\r
124             TCHAR pszExePath[MAX_PATH];\r
125             GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));\r
126 \r
127             // Set the path to the shortcut target\r
128             psl->SetPath(pszExePath);\r
129             PathRemoveFileSpec(pszExePath);\r
130             psl->SetWorkingDirectory(pszExePath);\r
131             psl->SetShowCmd(SW_SHOWMINNOACTIVE);\r
132 \r
133             // Query IShellLink for the IPersistFile interface for\r
134             // saving the shortcut in persistent storage.\r
135             IPersistFile* ppf = NULL;\r
136             hres = psl->QueryInterface(IID_IPersistFile,\r
137                                        reinterpret_cast<void**>(&ppf));\r
138             if (SUCCEEDED(hres))\r
139             {\r
140                 WCHAR pwsz[MAX_PATH];\r
141                 // Ensure that the string is ANSI.\r
142                 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);\r
143                 // Save the link by calling IPersistFile::Save.\r
144                 hres = ppf->Save(pwsz, TRUE);\r
145                 ppf->Release();\r
146             }\r
147             psl->Release();\r
148         }\r
149         CoUninitialize();\r
150     }\r
151 }\r
152 #else\r
153 bool GetStartOnSystemStartup() { return false; }\r
154 void SetStartOnSystemStartup(bool fAutoStart) { }\r
155 #endif\r
156 \r
157 \r
158 \r
159 \r
160 \r
161 \r
162 \r
163 //////////////////////////////////////////////////////////////////////////////\r
164 //\r
165 // CMyApp\r
166 //\r
167 \r
168 // Define a new application\r
169 class CMyApp: public wxApp\r
170 {\r
171 public:\r
172     wxLocale m_locale;\r
173 \r
174     CMyApp(){};\r
175     ~CMyApp(){};\r
176     bool OnInit();\r
177     bool OnInit2();\r
178     int OnExit();\r
179 \r
180     // Hook Initialize so we can start without GUI\r
181     virtual bool Initialize(int& argc, wxChar** argv);\r
182 \r
183     // 2nd-level exception handling: we get all the exceptions occurring in any\r
184     // event handler here\r
185     virtual bool OnExceptionInMainLoop();\r
186 \r
187     // 3rd, and final, level exception handling: whenever an unhandled\r
188     // exception is caught, this function is called\r
189     virtual void OnUnhandledException();\r
190 \r
191     // and now for something different: this function is called in case of a\r
192     // crash (e.g. dereferencing null pointer, division by 0, ...)\r
193     virtual void OnFatalException();\r
194 };\r
195 \r
196 IMPLEMENT_APP(CMyApp)\r
197 \r
198 bool CMyApp::Initialize(int& argc, wxChar** argv)\r
199 {\r
200     if (argc > 1 && argv[1][0] != '-' && (!fWindows || argv[1][0] != '/') &&\r
201         wxString(argv[1]) != "start")\r
202     {\r
203         fCommandLine = true;\r
204     }\r
205     else if (!fGUI)\r
206     {\r
207         fDaemon = true;\r
208     }\r
209     else\r
210     {\r
211         // wxApp::Initialize will remove environment-specific parameters,\r
212         // so it's too early to call ParseParameters yet\r
213         for (int i = 1; i < argc; i++)\r
214         {\r
215             wxString str = argv[i];\r
216             #ifdef __WXMSW__\r
217             if (str.size() >= 1 && str[0] == '/')\r
218                 str[0] = '-';\r
219             str = str.MakeLower();\r
220             #endif\r
221             // haven't decided which argument to use for this yet\r
222             if (str == "-daemon" || str == "-d" || str == "start")\r
223                 fDaemon = true;\r
224         }\r
225     }\r
226 \r
227     if (fDaemon)\r
228         fprintf(stdout, "bitcoin server starting\n");\r
229 \r
230 #ifdef __WXGTK__\r
231     if (fDaemon || fCommandLine)\r
232     {\r
233         // Call the original Initialize while suppressing error messages\r
234         // and ignoring failure.  If unable to initialize GTK, it fails\r
235         // near the end so hopefully the last few things don't matter.\r
236         {\r
237             wxLogNull logNo;\r
238             wxApp::Initialize(argc, argv);\r
239         }\r
240 \r
241         if (fDaemon)\r
242         {\r
243             // Daemonize\r
244             pid_t pid = fork();\r
245             if (pid < 0)\r
246             {\r
247                 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);\r
248                 return false;\r
249             }\r
250             if (pid > 0)\r
251                 pthread_exit((void*)0);\r
252         }\r
253 \r
254         return true;\r
255     }\r
256 #endif\r
257 \r
258     return wxApp::Initialize(argc, argv);\r
259 }\r
260 \r
261 bool CMyApp::OnInit()\r
262 {\r
263     bool fRet = false;\r
264     try\r
265     {\r
266         fRet = OnInit2();\r
267     }\r
268     catch (std::exception& e) {\r
269         PrintException(&e, "OnInit()");\r
270     } catch (...) {\r
271         PrintException(NULL, "OnInit()");\r
272     }\r
273     if (!fRet)\r
274         Shutdown(NULL);\r
275     return fRet;\r
276 }\r
277 \r
278 extern int g_isPainting;\r
279 \r
280 bool CMyApp::OnInit2()\r
281 {\r
282 #ifdef _MSC_VER\r
283     // Turn off microsoft heap dump noise\r
284     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);\r
285     _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));\r
286 #endif\r
287 #if defined(__WXMSW__) && defined(__WXDEBUG__) && wxUSE_GUI\r
288     // Disable malfunctioning wxWidgets debug assertion\r
289     g_isPainting = 10000;\r
290 #endif\r
291 #if wxUSE_GUI\r
292     wxImage::AddHandler(new wxPNGHandler);\r
293 #endif\r
294 #ifdef __WXMSW__\r
295     SetAppName("Bitcoin");\r
296 #else\r
297     SetAppName("bitcoin");\r
298     umask(077);\r
299 #endif\r
300 #ifdef __WXMSW__\r
301 #if wxUSE_UNICODE\r
302     // Hack to set wxConvLibc codepage to UTF-8 on Windows,\r
303     // may break if wxMBConv_win32 implementation in strconv.cpp changes.\r
304     class wxMBConv_win32 : public wxMBConv\r
305     {\r
306     public:\r
307         long m_CodePage;\r
308         size_t m_minMBCharWidth;\r
309     };\r
310     if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)\r
311         ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;\r
312 #endif\r
313 #endif\r
314 \r
315     // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file\r
316     m_locale.Init(wxLANGUAGE_DEFAULT, 0);\r
317     m_locale.AddCatalogLookupPathPrefix("locale");\r
318     if (!fWindows)\r
319     {\r
320         m_locale.AddCatalogLookupPathPrefix("/usr/share/locale");\r
321         m_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");\r
322     }\r
323     m_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any\r
324     m_locale.AddCatalog("bitcoin");\r
325 \r
326     //\r
327     // Parameters\r
328     //\r
329     if (fCommandLine)\r
330     {\r
331         int ret = CommandLineRPC(argc, argv);\r
332         exit(ret);\r
333     }\r
334 \r
335     ParseParameters(argc, argv);\r
336     if (mapArgs.count("-?") || mapArgs.count("--help"))\r
337     {\r
338         wxString strUsage = string() +\r
339             _("Usage: bitcoin [options]") + "\t\t\t\t\t\t\n" +\r
340             _("Options:\n") +\r
341             "  -gen            \t\t  " + _("Generate coins\n") +\r
342             "  -gen=0          \t\t  " + _("Don't generate coins\n") +\r
343             "  -min            \t\t  " + _("Start minimized\n") +\r
344             "  -datadir=<dir>  \t  "   + _("Specify data directory\n") +\r
345             "  -proxy=<ip:port>\t  "   + _("Connect through socks4 proxy\n") +\r
346             "  -addnode=<ip>   \t  "   + _("Add a node to connect to\n") +\r
347             "  -connect=<ip>   \t  "   + _("Connect only to the specified node\n") +\r
348             "  -?              \t\t  " + _("This help message\n");\r
349 \r
350         if (fWindows && fGUI)\r
351         {\r
352             // Remove spaces, the tabs make the columns line up in the message box\r
353             for (int i = 0; i < 50; i++)\r
354                 strUsage.Replace(" \t", "\t");\r
355             wxMessageBox(strUsage, "Bitcoin", wxOK);\r
356         }\r
357         else\r
358         {\r
359             // Remove tabs\r
360             strUsage.Replace("\t", "");\r
361             fprintf(stderr, "%s", ((string)strUsage).c_str());\r
362         }\r
363         return false;\r
364     }\r
365 \r
366     if (mapArgs.count("-datadir"))\r
367         strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));\r
368 \r
369     if (mapArgs.count("-debug"))\r
370         fDebug = true;\r
371 \r
372     if (mapArgs.count("-printtodebugger"))\r
373         fPrintToDebugger = true;\r
374 \r
375     if (!fDebug && !pszSetDataDir[0])\r
376         ShrinkDebugFile();\r
377     printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");\r
378     printf("Bitcoin version 0.%d.%d%s beta, OS version %s\n", VERSION/100, VERSION%100, pszSubVer, ((string)wxGetOsDescription()).c_str());\r
379     printf("System default language is %d %s\n", m_locale.GetSystemLanguage(), ((string)m_locale.GetSysName()).c_str());\r
380     printf("Language file %s (%s)\n", (string("locale/") + (string)m_locale.GetCanonicalName() + "/LC_MESSAGES/bitcoin.mo").c_str(), ((string)m_locale.GetLocale()).c_str());\r
381 \r
382     if (mapArgs.count("-loadblockindextest"))\r
383     {\r
384         CTxDB txdb("r");\r
385         txdb.LoadBlockIndex();\r
386         PrintBlockTree();\r
387         return false;\r
388     }\r
389 \r
390     //\r
391     // Limit to single instance per user\r
392     // Required to protect the database files if we're going to keep deleting log.*\r
393     //\r
394 #ifdef __WXMSW__\r
395     // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file\r
396     //  maybe should go by whether successfully bind port 8333 instead\r
397     wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");\r
398     for (int i = 0; i < strMutexName.size(); i++)\r
399         if (!isalnum(strMutexName[i]))\r
400             strMutexName[i] = '.';\r
401     wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
402     if (psingleinstancechecker->IsAnotherRunning())\r
403     {\r
404         printf("Existing instance found\n");\r
405         unsigned int nStart = GetTime();\r
406         loop\r
407         {\r
408             // TODO: find out how to do this in Linux, or replace with wxWidgets commands\r
409             // Show the previous instance and exit\r
410             HWND hwndPrev = FindWindowA("wxWindowClassNR", "Bitcoin");\r
411             if (hwndPrev)\r
412             {\r
413                 if (IsIconic(hwndPrev))\r
414                     ShowWindow(hwndPrev, SW_RESTORE);\r
415                 SetForegroundWindow(hwndPrev);\r
416                 return false;\r
417             }\r
418 \r
419             if (GetTime() > nStart + 60)\r
420                 return false;\r
421 \r
422             // Resume this instance if the other exits\r
423             delete psingleinstancechecker;\r
424             Sleep(1000);\r
425             psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);\r
426             if (!psingleinstancechecker->IsAnotherRunning())\r
427                 break;\r
428         }\r
429     }\r
430 #endif\r
431 \r
432     // Bind to the port early so we can tell if another instance is already running.\r
433     // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.\r
434     string strErrors;\r
435     if (!BindListenPort(strErrors))\r
436     {\r
437         wxMessageBox(strErrors, "Bitcoin");\r
438         return false;\r
439     }\r
440 \r
441     //\r
442     // Load data files\r
443     //\r
444     bool fFirstRun;\r
445     strErrors = "";\r
446     int64 nStart;\r
447 \r
448     printf("Loading addresses...\n");\r
449     nStart = GetTimeMillis();\r
450     if (!LoadAddresses())\r
451         strErrors += _("Error loading addr.dat      \n");\r
452     printf(" addresses   %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
453 \r
454     printf("Loading block index...\n");\r
455     nStart = GetTimeMillis();\r
456     if (!LoadBlockIndex())\r
457         strErrors += _("Error loading blkindex.dat      \n");\r
458     printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
459 \r
460     printf("Loading wallet...\n");\r
461     nStart = GetTimeMillis();\r
462     if (!LoadWallet(fFirstRun))\r
463         strErrors += _("Error loading wallet.dat      \n");\r
464     printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);\r
465 \r
466     printf("Done loading\n");\r
467 \r
468         //// debug print\r
469         printf("mapBlockIndex.size() = %d\n",   mapBlockIndex.size());\r
470         printf("nBestHeight = %d\n",            nBestHeight);\r
471         printf("mapKeys.size() = %d\n",         mapKeys.size());\r
472         printf("mapPubKeys.size() = %d\n",      mapPubKeys.size());\r
473         printf("mapWallet.size() = %d\n",       mapWallet.size());\r
474         printf("mapAddressBook.size() = %d\n",  mapAddressBook.size());\r
475 \r
476     if (!strErrors.empty())\r
477     {\r
478         wxMessageBox(strErrors, "Bitcoin");\r
479         return false;\r
480     }\r
481 \r
482     // Add wallet transactions that aren't already in a block to mapTransactions\r
483     ReacceptWalletTransactions();\r
484 \r
485     //\r
486     // Parameters\r
487     //\r
488     if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))\r
489     {\r
490         PrintBlockTree();\r
491         return false;\r
492     }\r
493 \r
494     if (mapArgs.count("-printblock"))\r
495     {\r
496         string strMatch = mapArgs["-printblock"];\r
497         int nFound = 0;\r
498         for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)\r
499         {\r
500             uint256 hash = (*mi).first;\r
501             if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)\r
502             {\r
503                 CBlockIndex* pindex = (*mi).second;\r
504                 CBlock block;\r
505                 block.ReadFromDisk(pindex);\r
506                 block.BuildMerkleTree();\r
507                 block.print();\r
508                 printf("\n");\r
509                 nFound++;\r
510             }\r
511         }\r
512         if (nFound == 0)\r
513             printf("No blocks matching %s were found\n", strMatch.c_str());\r
514         return false;\r
515     }\r
516 \r
517     if (mapArgs.count("-gen"))\r
518     {\r
519         if (mapArgs["-gen"].empty())\r
520             fGenerateBitcoins = true;\r
521         else\r
522             fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);\r
523     }\r
524 \r
525     if (mapArgs.count("-proxy"))\r
526     {\r
527         fUseProxy = true;\r
528         addrProxy = CAddress(mapArgs["-proxy"]);\r
529         if (!addrProxy.IsValid())\r
530         {\r
531             wxMessageBox(_("Invalid -proxy address"), "Bitcoin");\r
532             return false;\r
533         }\r
534     }\r
535 \r
536     if (mapArgs.count("-addnode"))\r
537     {\r
538         foreach(string strAddr, mapMultiArgs["-addnode"])\r
539         {\r
540             CAddress addr(strAddr, NODE_NETWORK);\r
541             addr.nTime = 0; // so it won't relay unless successfully connected\r
542             if (addr.IsValid())\r
543                 AddAddress(addr);\r
544         }\r
545     }\r
546 \r
547     //\r
548     // Create the main window and start the node\r
549     //\r
550     if (!fDaemon)\r
551         CreateMainWindow();\r
552 \r
553     if (!CheckDiskSpace())\r
554         return false;\r
555 \r
556     RandAddSeedPerfmon();\r
557 \r
558     if (!CreateThread(StartNode, NULL))\r
559         wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");\r
560 \r
561     if (mapArgs.count("-server") || fDaemon)\r
562         CreateThread(ThreadRPCServer, NULL);\r
563 \r
564     if (fFirstRun)\r
565         SetStartOnSystemStartup(true);\r
566 \r
567     return true;\r
568 }\r
569 \r
570 int CMyApp::OnExit()\r
571 {\r
572     Shutdown(NULL);\r
573     return wxApp::OnExit();\r
574 }\r
575 \r
576 bool CMyApp::OnExceptionInMainLoop()\r
577 {\r
578     try\r
579     {\r
580         throw;\r
581     }\r
582     catch (std::exception& e)\r
583     {\r
584         PrintException(&e, "CMyApp::OnExceptionInMainLoop()");\r
585         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
586         Sleep(1000);\r
587         throw;\r
588     }\r
589     catch (...)\r
590     {\r
591         PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");\r
592         wxLogWarning("Unknown exception");\r
593         Sleep(1000);\r
594         throw;\r
595     }\r
596     return true;\r
597 }\r
598 \r
599 void CMyApp::OnUnhandledException()\r
600 {\r
601     // this shows how we may let some exception propagate uncaught\r
602     try\r
603     {\r
604         throw;\r
605     }\r
606     catch (std::exception& e)\r
607     {\r
608         PrintException(&e, "CMyApp::OnUnhandledException()");\r
609         wxLogWarning("Exception %s %s", typeid(e).name(), e.what());\r
610         Sleep(1000);\r
611         throw;\r
612     }\r
613     catch (...)\r
614     {\r
615         PrintException(NULL, "CMyApp::OnUnhandledException()");\r
616         wxLogWarning("Unknown exception");\r
617         Sleep(1000);\r
618         throw;\r
619     }\r
620 }\r
621 \r
622 void CMyApp::OnFatalException()\r
623 {\r
624     wxMessageBox(_("Program has crashed and will terminate.  "), "Bitcoin", wxOK | wxICON_ERROR);\r
625 }\r