PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/server/bnetserver/Main.cpp

https://gitlab.com/Misahky/TrinityCore
C++ | 342 lines | 243 code | 55 blank | 44 comment | 36 complexity | 2c83520665ae113d3536d5d2555a4a12 MD5 | raw file
  1. /*
  2. * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. * more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * @file main.cpp
  19. * @brief Authentication Server main program
  20. *
  21. * This file contains the main program for the
  22. * authentication server
  23. */
  24. #include "SessionManager.h"
  25. #include "AppenderDB.h"
  26. #include "ProcessPriority.h"
  27. #include "RealmList.h"
  28. #include "GitRevision.h"
  29. #include "SslContext.h"
  30. #include "DatabaseLoader.h"
  31. #include "LoginRESTService.h"
  32. #include <iostream>
  33. #include <boost/program_options.hpp>
  34. #include <boost/filesystem/path.hpp>
  35. #include <google/protobuf/stubs/common.h>
  36. using boost::asio::ip::tcp;
  37. using namespace boost::program_options;
  38. namespace fs = boost::filesystem;
  39. #ifndef _TRINITY_BNET_CONFIG
  40. # define _TRINITY_BNET_CONFIG "bnetserver.conf"
  41. #endif
  42. #if PLATFORM == PLATFORM_WINDOWS
  43. #include "ServiceWin32.h"
  44. char serviceName[] = "bnetserver";
  45. char serviceLongName[] = "TrinityCore bnet service";
  46. char serviceDescription[] = "TrinityCore Battle.net emulator authentication service";
  47. /*
  48. * -1 - not in service mode
  49. * 0 - stopped
  50. * 1 - running
  51. * 2 - paused
  52. */
  53. int m_ServiceStatus = -1;
  54. static boost::asio::deadline_timer* _serviceStatusWatchTimer;
  55. void ServiceStatusWatcher(boost::system::error_code const& error);
  56. #endif
  57. bool StartDB();
  58. void StopDB();
  59. void SignalHandler(boost::system::error_code const& error, int signalNumber);
  60. void KeepDatabaseAliveHandler(boost::system::error_code const& error);
  61. void BanExpiryHandler(boost::system::error_code const& error);
  62. variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService);
  63. static boost::asio::io_service* _ioService;
  64. static boost::asio::deadline_timer* _dbPingTimer;
  65. static uint32 _dbPingInterval;
  66. static boost::asio::deadline_timer* _banExpiryCheckTimer;
  67. static uint32 _banExpiryCheckInterval;
  68. int main(int argc, char** argv)
  69. {
  70. signal(SIGABRT, &Trinity::AbortHandler);
  71. auto configFile = fs::absolute(_TRINITY_BNET_CONFIG);
  72. std::string configService;
  73. auto vm = GetConsoleArguments(argc, argv, configFile, configService);
  74. // exit if help or version is enabled
  75. if (vm.count("help") || vm.count("version"))
  76. return 0;
  77. GOOGLE_PROTOBUF_VERIFY_VERSION;
  78. #if PLATFORM == PLATFORM_WINDOWS
  79. if (configService.compare("install") == 0)
  80. return WinServiceInstall() ? 0 : 1;
  81. else if (configService.compare("uninstall") == 0)
  82. return WinServiceUninstall() ? 0 : 1;
  83. else if (configService.compare("run") == 0)
  84. return WinServiceRun() ? 0 : 1;
  85. #endif
  86. std::string configError;
  87. if (!sConfigMgr->LoadInitial(configFile.generic_string(),
  88. std::vector<std::string>(argv, argv + argc),
  89. configError))
  90. {
  91. printf("Error in config file: %s\n", configError.c_str());
  92. return 1;
  93. }
  94. sLog->RegisterAppender<AppenderDB>();
  95. sLog->Initialize(nullptr);
  96. TC_LOG_INFO("server.bnetserver", "%s (bnetserver)", GitRevision::GetFullVersion());
  97. TC_LOG_INFO("server.bnetserver", "<Ctrl-C> to stop.\n");
  98. TC_LOG_INFO("server.bnetserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str());
  99. TC_LOG_INFO("server.bnetserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
  100. TC_LOG_INFO("server.bnetserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
  101. // Seed the OpenSSL's PRNG here.
  102. // That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
  103. BigNumber seed;
  104. seed.SetRand(16 * 8);
  105. // bnetserver PID file creation
  106. std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
  107. if (!pidFile.empty())
  108. {
  109. if (uint32 pid = CreatePIDFile(pidFile))
  110. TC_LOG_INFO("server.bnetserver", "Daemon PID: %u\n", pid);
  111. else
  112. {
  113. TC_LOG_ERROR("server.bnetserver", "Cannot create PID file %s.\n", pidFile.c_str());
  114. return 1;
  115. }
  116. }
  117. if (!Battlenet::SslContext::Initialize())
  118. {
  119. TC_LOG_ERROR("server.bnetserver", "Failed to initialize SSL context");
  120. return 1;
  121. }
  122. // Initialize the database connection
  123. if (!StartDB())
  124. return 1;
  125. _ioService = new boost::asio::io_service();
  126. // Start the listening port (acceptor) for auth connections
  127. int32 bnport = sConfigMgr->GetIntDefault("BattlenetPort", 1119);
  128. if (bnport < 0 || bnport > 0xFFFF)
  129. {
  130. TC_LOG_ERROR("server.bnetserver", "Specified battle.net port (%d) out of allowed range (1-65535)", bnport);
  131. StopDB();
  132. delete _ioService;
  133. return 1;
  134. }
  135. if (!sLoginService.Start(*_ioService))
  136. {
  137. StopDB();
  138. delete _ioService;
  139. TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service");
  140. return 1;
  141. }
  142. // Get the list of realms for the server
  143. sRealmList->Initialize(*_ioService, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10));
  144. std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
  145. sSessionMgr.StartNetwork(*_ioService, bindIp, bnport);
  146. // Set signal handlers
  147. boost::asio::signal_set signals(*_ioService, SIGINT, SIGTERM);
  148. #if PLATFORM == PLATFORM_WINDOWS
  149. signals.add(SIGBREAK);
  150. #endif
  151. signals.async_wait(SignalHandler);
  152. // Set process priority according to configuration settings
  153. SetProcessPriority("server.bnetserver");
  154. // Enabled a timed callback for handling the database keep alive ping
  155. _dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30);
  156. _dbPingTimer = new boost::asio::deadline_timer(*_ioService);
  157. _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval));
  158. _dbPingTimer->async_wait(KeepDatabaseAliveHandler);
  159. _banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60);
  160. _banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService);
  161. _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval));
  162. _banExpiryCheckTimer->async_wait(BanExpiryHandler);
  163. #if PLATFORM == PLATFORM_WINDOWS
  164. if (m_ServiceStatus != -1)
  165. {
  166. _serviceStatusWatchTimer = new boost::asio::deadline_timer(*_ioService);
  167. _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1));
  168. _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher);
  169. }
  170. #endif
  171. // Start the io service worker loop
  172. _ioService->run();
  173. _banExpiryCheckTimer->cancel();
  174. _dbPingTimer->cancel();
  175. sLoginService.Stop();
  176. sSessionMgr.StopNetwork();
  177. sRealmList->Close();
  178. // Close the Database Pool and library
  179. StopDB();
  180. TC_LOG_INFO("server.bnetserver", "Halting process...");
  181. signals.cancel();
  182. delete _banExpiryCheckTimer;
  183. delete _dbPingTimer;
  184. delete _ioService;
  185. google::protobuf::ShutdownProtobufLibrary();
  186. return 0;
  187. }
  188. /// Initialize connection to the database
  189. bool StartDB()
  190. {
  191. MySQL::Library_Init();
  192. // Load databases
  193. DatabaseLoader loader("server.bnetserver", DatabaseLoader::DATABASE_NONE);
  194. loader
  195. .AddDatabase(LoginDatabase, "Login");
  196. if (!loader.Load())
  197. return false;
  198. TC_LOG_INFO("server.bnetserver", "Started auth database connection pool.");
  199. sLog->SetRealmId(0); // Enables DB appenders when realm is set.
  200. return true;
  201. }
  202. /// Close the connection to the database
  203. void StopDB()
  204. {
  205. LoginDatabase.Close();
  206. MySQL::Library_End();
  207. }
  208. void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
  209. {
  210. if (!error)
  211. _ioService->stop();
  212. }
  213. void KeepDatabaseAliveHandler(boost::system::error_code const& error)
  214. {
  215. if (!error)
  216. {
  217. TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive");
  218. LoginDatabase.KeepAlive();
  219. _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval));
  220. _dbPingTimer->async_wait(KeepDatabaseAliveHandler);
  221. }
  222. }
  223. void BanExpiryHandler(boost::system::error_code const& error)
  224. {
  225. if (!error)
  226. {
  227. LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
  228. LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
  229. _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval));
  230. _banExpiryCheckTimer->async_wait(BanExpiryHandler);
  231. }
  232. }
  233. #if PLATFORM == PLATFORM_WINDOWS
  234. void ServiceStatusWatcher(boost::system::error_code const& error)
  235. {
  236. if (!error)
  237. {
  238. if (m_ServiceStatus == 0)
  239. {
  240. _ioService->stop();
  241. delete _serviceStatusWatchTimer;
  242. }
  243. else
  244. {
  245. _serviceStatusWatchTimer->expires_from_now(boost::posix_time::seconds(1));
  246. _serviceStatusWatchTimer->async_wait(ServiceStatusWatcher);
  247. }
  248. }
  249. }
  250. #endif
  251. variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService)
  252. {
  253. (void)configService;
  254. options_description all("Allowed options");
  255. all.add_options()
  256. ("help,h", "print usage message")
  257. ("version,v", "print version build info")
  258. ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_BNET_CONFIG)),
  259. "use <arg> as configuration file")
  260. ;
  261. #if PLATFORM == PLATFORM_WINDOWS
  262. options_description win("Windows platform specific options");
  263. win.add_options()
  264. ("service,s", value<std::string>(&configService)->default_value(""), "Windows service options: [install | uninstall]")
  265. ;
  266. all.add(win);
  267. #endif
  268. variables_map variablesMap;
  269. try
  270. {
  271. store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap);
  272. notify(variablesMap);
  273. }
  274. catch (std::exception& e)
  275. {
  276. std::cerr << e.what() << "\n";
  277. }
  278. if (variablesMap.count("help"))
  279. {
  280. std::cout << all << "\n";
  281. }
  282. else if (variablesMap.count("version"))
  283. {
  284. std::cout << GitRevision::GetFullVersion() << "\n";
  285. }
  286. return variablesMap;
  287. }