PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/src/oregoncore/Master.cpp

https://code.google.com/p/oregoncore/
C++ | 462 lines | 319 code | 75 blank | 68 comment | 34 complexity | f7f17624960a9fb4f95f39f329a0e05a MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause, GPL-2.0, LGPL-2.1
  1. /*
  2. * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
  3. *
  4. * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
  5. *
  6. * Copyright (C) 2010 Oregon <http://www.oregoncore.com/>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. #include <ace/OS_NS_signal.h>
  23. #include "WorldSocketMgr.h"
  24. #include "Common.h"
  25. #include "Master.h"
  26. #include "WorldSocket.h"
  27. #include "WorldRunnable.h"
  28. #include "World.h"
  29. #include "Log.h"
  30. #include "Timer.h"
  31. #include "Policies/SingletonImp.h"
  32. #include "SystemConfig.h"
  33. #include "Config/Config.h"
  34. #include "Database/DatabaseEnv.h"
  35. #include "DBCStores.h"
  36. #include "CliRunnable.h"
  37. #include "RARunnable.h"
  38. #include "Util.h"
  39. #include "OCSoap.h"
  40. #ifdef _WIN32
  41. #include "ServiceWin32.h"
  42. extern int m_ServiceStatus;
  43. #endif
  44. INSTANTIATE_SINGLETON_1( Master );
  45. volatile uint32 Master::m_masterLoopCounter = 0;
  46. class FreezeDetectorRunnable : public ACE_Based::Runnable
  47. {
  48. public:
  49. FreezeDetectorRunnable() { _delaytime = 0; }
  50. uint32 m_loops, m_lastchange;
  51. uint32 w_loops, w_lastchange;
  52. uint32 _delaytime;
  53. void SetDelayTime(uint32 t) { _delaytime = t; }
  54. void run(void)
  55. {
  56. if (!_delaytime)
  57. return;
  58. sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...",_delaytime/1000);
  59. m_loops = 0;
  60. w_loops = 0;
  61. m_lastchange = 0;
  62. w_lastchange = 0;
  63. while (!World::IsStopped())
  64. {
  65. ACE_Based::Thread::Sleep(1000);
  66. uint32 curtime = getMSTime();
  67. // normal work
  68. if (w_loops != World::m_worldLoopCounter)
  69. {
  70. w_lastchange = curtime;
  71. w_loops = World::m_worldLoopCounter;
  72. }
  73. // possible freeze
  74. else if (getMSTimeDiff(w_lastchange,curtime) > _delaytime)
  75. {
  76. sLog.outError("World Thread is stuck. Terminating server!");
  77. *((uint32 volatile*)NULL) = 0; // bang crash
  78. }
  79. }
  80. sLog.outString("Anti-freeze thread exiting without problems.");
  81. }
  82. };
  83. Master::Master()
  84. {
  85. }
  86. Master::~Master()
  87. {
  88. }
  89. // Main function
  90. int Master::Run()
  91. {
  92. sLog.outString( "%s (core-daemon)", _FULLVERSION );
  93. sLog.outString( "<Ctrl-C> to stop.\n" );
  94. sLog.outString( " _____ " );
  95. sLog.outString( " /\\ __`\\ " );
  96. sLog.outString( " \\ \\ \\/\\ \\ _ __ __ __ ___ ___ " );
  97. sLog.outString( " \\ \\ \\ \\ \\/\\`'__\\'__`\\ /'_ `\\ / __`\\/' _ `\\ " );
  98. sLog.outString( " \\ \\ \\_\\ \\ \\ \\/\\ __//\\ \\L\\ \\/\\ \\L\\ \\\\ \\/\\ \\ " );
  99. sLog.outString( " \\ \\_____\\ \\_\\ \\____\\ \\____ \\ \\____/ \\_\\ \\_\\ " );
  100. sLog.outString( " \\/_____/\\/_/\\/____/\\/___L\\ \\/___/ \\/_/\\/_/ " );
  101. sLog.outString( " /\\____/ " );
  102. sLog.outString( " \\_/__/ " );
  103. sLog.outString( " http://www.oregoncore.com \n " );
  104. // worldd PID file creation
  105. std::string pidfile = sConfig.GetStringDefault("PidFile", "");
  106. if (!pidfile.empty())
  107. {
  108. uint32 pid = CreatePIDFile(pidfile);
  109. if (!pid)
  110. {
  111. sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() );
  112. return 1;
  113. }
  114. sLog.outString( "Daemon PID: %u\n", pid );
  115. }
  116. // Start the databases
  117. if (!_StartDB())
  118. return 1;
  119. // Initialize the World
  120. sWorld.SetInitialWorldSettings();
  121. // Catch termination signals
  122. _HookSignals();
  123. // Launch WorldRunnable thread
  124. ACE_Based::Thread world_thread(new WorldRunnable);
  125. world_thread.setPriority(ACE_Based::Highest);
  126. // set realmbuilds depend on OregonCore expected builds, and set server online
  127. std::string builds = AcceptableClientBuildsListStr();
  128. LoginDatabase.escape_string(builds);
  129. LoginDatabase.PExecute("UPDATE realmlist SET realmflags = realmflags & ~(%u), population = 0, realmbuilds = '%s' WHERE id = '%d'", REALM_FLAG_OFFLINE, builds.c_str(), realmID);
  130. ACE_Based::Thread* cliThread = NULL;
  131. #ifdef _WIN32
  132. if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
  133. #else
  134. if (sConfig.GetBoolDefault("Console.Enable", true))
  135. #endif
  136. {
  137. // Launch CliRunnable thread
  138. cliThread = new ACE_Based::Thread(new CliRunnable);
  139. }
  140. ACE_Based::Thread rar_thread(new RARunnable);
  141. // Handle affinity for multiple processors and process priority on Windows
  142. #ifdef _WIN32
  143. {
  144. HANDLE hProcess = GetCurrentProcess();
  145. uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
  146. if (Aff > 0)
  147. {
  148. ULONG_PTR appAff;
  149. ULONG_PTR sysAff;
  150. if (GetProcessAffinityMask(hProcess,&appAff,&sysAff))
  151. {
  152. ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
  153. if (!curAff )
  154. {
  155. sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for OregonCore. Accessible processors bitmask (hex): %x",Aff,appAff);
  156. }
  157. else
  158. {
  159. if (SetProcessAffinityMask(hProcess,curAff))
  160. sLog.outString("Using processors (bitmask, hex): %x", curAff);
  161. else
  162. sLog.outError("Can't set used processors (hex): %x",curAff);
  163. }
  164. }
  165. sLog.outString();
  166. }
  167. bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
  168. if (Prio)
  169. {
  170. if (SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS))
  171. sLog.outString("OregonCore process priority class set to HIGH");
  172. else
  173. sLog.outError("ERROR: Can't set OregonCore process priority class.");
  174. sLog.outString();
  175. }
  176. }
  177. #endif
  178. // Start soap serving thread
  179. ACE_Based::Thread* soap_thread = NULL;
  180. if (sConfig.GetBoolDefault("SOAP.Enabled", false))
  181. {
  182. OCSoapRunnable *runnable = new OCSoapRunnable();
  183. runnable->setListenArguments(sConfig.GetStringDefault("SOAP.IP", "127.0.0.1"), sConfig.GetIntDefault("SOAP.Port", 7878));
  184. soap_thread = new ACE_Based::Thread(runnable);
  185. }
  186. uint32 realCurrTime, realPrevTime;
  187. realCurrTime = realPrevTime = getMSTime();
  188. uint32 socketSelecttime = sWorld.getConfig(CONFIG_SOCKET_SELECTTIME);
  189. // Start up freeze catcher thread
  190. ACE_Based::Thread* freeze_thread = NULL;
  191. if (uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0))
  192. {
  193. FreezeDetectorRunnable *fdr = new FreezeDetectorRunnable();
  194. fdr->SetDelayTime(freeze_delay*1000);
  195. freeze_thread = new ACE_Based::Thread(fdr);
  196. freeze_thread->setPriority(ACE_Based::Highest);
  197. }
  198. // Launch the world listener socket
  199. uint16 wsport = sWorld.getConfig(CONFIG_PORT_WORLD);
  200. std::string bind_ip = sConfig.GetStringDefault ("BindIP", "0.0.0.0");
  201. if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1)
  202. {
  203. sLog.outError ("Failed to start network");
  204. World::StopNow(ERROR_EXIT_CODE);
  205. // go down and shutdown the server
  206. }
  207. sWorldSocketMgr->Wait();
  208. // Stop freeze protection before shutdown tasks
  209. if (freeze_thread)
  210. {
  211. freeze_thread->destroy();
  212. delete freeze_thread;
  213. }
  214. // Stop soap thread
  215. if (soap_thread)
  216. {
  217. soap_thread->wait();
  218. soap_thread->destroy();
  219. delete soap_thread;
  220. }
  221. // Set server offline in realmlist
  222. LoginDatabase.PExecute("UPDATE realmlist SET realmflags = realmflags | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID);
  223. // Remove signal handling before leaving
  224. _UnhookSignals();
  225. // when the main thread closes the singletons get unloaded
  226. // since worldrunnable uses them, it will crash if unloaded after master
  227. world_thread.wait();
  228. rar_thread.wait ();
  229. // Clean account database before leaving
  230. clearOnlineAccounts();
  231. // Wait for delay threads to end
  232. CharacterDatabase.HaltDelayThread();
  233. WorldDatabase.HaltDelayThread();
  234. LoginDatabase.HaltDelayThread();
  235. sLog.outString( "Halting process..." );
  236. if (cliThread)
  237. {
  238. #ifdef _WIN32
  239. // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
  240. //_exit(1);
  241. // send keyboard input to safely unblock the CLI thread
  242. INPUT_RECORD b[5];
  243. HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  244. b[0].EventType = KEY_EVENT;
  245. b[0].Event.KeyEvent.bKeyDown = TRUE;
  246. b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
  247. b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
  248. b[0].Event.KeyEvent.wRepeatCount = 1;
  249. b[1].EventType = KEY_EVENT;
  250. b[1].Event.KeyEvent.bKeyDown = FALSE;
  251. b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
  252. b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
  253. b[1].Event.KeyEvent.wRepeatCount = 1;
  254. b[2].EventType = KEY_EVENT;
  255. b[2].Event.KeyEvent.bKeyDown = TRUE;
  256. b[2].Event.KeyEvent.dwControlKeyState = 0;
  257. b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
  258. b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
  259. b[2].Event.KeyEvent.wRepeatCount = 1;
  260. b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
  261. b[3].EventType = KEY_EVENT;
  262. b[3].Event.KeyEvent.bKeyDown = FALSE;
  263. b[3].Event.KeyEvent.dwControlKeyState = 0;
  264. b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
  265. b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
  266. b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
  267. b[3].Event.KeyEvent.wRepeatCount = 1;
  268. DWORD numb;
  269. WriteConsoleInput(hStdIn, b, 4, &numb);
  270. cliThread->wait();
  271. #else
  272. cliThread->destroy();
  273. #endif
  274. delete cliThread;
  275. }
  276. // for some unknown reason, unloading scripts here and not in worldrunnable
  277. // fixes a memory leak related to detaching threads from the module
  278. //UnloadScriptingModule();
  279. // Exit the process with specified return value
  280. return World::GetExitCode();
  281. }
  282. // Initialize connection to the databases
  283. bool Master::_StartDB()
  284. {
  285. sLog.SetLogDB(false);
  286. // Get world database info from configuration file
  287. std::string dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
  288. if (dbstring.empty())
  289. {
  290. sLog.outError("World database not specified in configuration file");
  291. return false;
  292. }
  293. // Initialise the world database
  294. if (!WorldDatabase.Initialize(dbstring.c_str()))
  295. {
  296. sLog.outError("Cannot connect to world database %s",dbstring.c_str());
  297. return false;
  298. }
  299. // Get character database info from configuration file
  300. dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
  301. if (dbstring.empty())
  302. {
  303. sLog.outError("Character database not specified in configuration file");
  304. return false;
  305. }
  306. // Initialise the Character database
  307. if (!CharacterDatabase.Initialize(dbstring.c_str()))
  308. {
  309. sLog.outError("Cannot connect to Character database %s",dbstring.c_str());
  310. return false;
  311. }
  312. // Get login database info from configuration file
  313. dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
  314. if (dbstring.empty())
  315. {
  316. sLog.outError("Login database not specified in configuration file");
  317. return false;
  318. }
  319. // Initialise the login database
  320. if (!LoginDatabase.Initialize(dbstring.c_str()))
  321. {
  322. sLog.outError("Cannot connect to login database %s",dbstring.c_str());
  323. return false;
  324. }
  325. // Get the realm Id from the configuration file
  326. realmID = sConfig.GetIntDefault("RealmID", 0);
  327. if (!realmID)
  328. {
  329. sLog.outError("Realm ID not defined in configuration file");
  330. return false;
  331. }
  332. sLog.outString("Realm running as realm ID %d", realmID);
  333. // Initialize the DB logging system
  334. sLog.SetLogDBLater(sConfig.GetBoolDefault("EnableLogDB", false)); // set var to enable DB logging once startup finished.
  335. sLog.SetLogDB(false);
  336. sLog.SetRealmID(realmID);
  337. // Clean the database before starting
  338. clearOnlineAccounts();
  339. // Insert version info into DB
  340. WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _REVISION);
  341. sWorld.LoadDBVersion();
  342. sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
  343. return true;
  344. }
  345. // Clear 'online' status for all accounts with characters in this realm
  346. void Master::clearOnlineAccounts()
  347. {
  348. // Cleanup online status for characters hosted at current realm
  349. // todo - Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'?
  350. LoginDatabase.PExecute("UPDATE account SET active_realm_id = 0 WHERE active_realm_id = '%d'", realmID);
  351. CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
  352. }
  353. // Handle termination signals
  354. void Master::_OnSignal(int s)
  355. {
  356. switch (s)
  357. {
  358. case SIGINT:
  359. World::StopNow(RESTART_EXIT_CODE);
  360. break;
  361. case SIGTERM:
  362. #ifdef _WIN32
  363. case SIGBREAK:
  364. #endif
  365. World::StopNow(SHUTDOWN_EXIT_CODE);
  366. break;
  367. }
  368. signal(s, _OnSignal);
  369. }
  370. // Define hook '_OnSignal' for all termination signals
  371. void Master::_HookSignals()
  372. {
  373. signal(SIGINT, _OnSignal);
  374. signal(SIGTERM, _OnSignal);
  375. #ifdef _WIN32
  376. signal(SIGBREAK, _OnSignal);
  377. #endif
  378. }
  379. // Unhook the signals before leaving
  380. void Master::_UnhookSignals()
  381. {
  382. signal(SIGINT, 0);
  383. signal(SIGTERM, 0);
  384. #ifdef _WIN32
  385. signal(SIGBREAK, 0);
  386. #endif
  387. }