TrinityCore
Loading...
Searching...
No Matches
Main.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
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
26#include "AppenderDB.h"
27#include "Banner.h"
28#include "BigNumber.h"
29#include "Config.h"
30#include "DatabaseEnv.h"
31#include "DatabaseLoader.h"
32#include "DeadlineTimer.h"
33#include "GitRevision.h"
34#include "IPLocation.h"
35#include "IpNetwork.h"
36#include "Locales.h"
37#include "LoginRESTService.h"
38#include "Memory.h"
39#include "MySQLThreading.h"
40#include "OpenSSLCrypto.h"
41#include "ProcessPriority.h"
42#include "RealmList.h"
43#include "SecretMgr.h"
44#include "SessionManager.h"
45#include "SslContext.h"
46#include "Util.h"
47#include <boost/asio/signal_set.hpp>
48#include <boost/dll/runtime_symbol_info.hpp>
49#include <boost/program_options.hpp>
50#include <boost/filesystem/operations.hpp>
51#include <google/protobuf/stubs/common.h>
52#include <iostream>
53#include <csignal>
54
56
57using namespace boost::program_options;
58namespace fs = boost::filesystem;
59
60#ifndef _TRINITY_BNET_CONFIG
61# define _TRINITY_BNET_CONFIG "bnetserver.conf"
62#endif
63#ifndef _TRINITY_BNET_CONFIG_DIR
64 #define _TRINITY_BNET_CONFIG_DIR "bnetserver.conf.d"
65#endif
66
67#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
68#include "ServiceWin32.h"
69#include <tchar.h>
70TCHAR serviceName[] = _T("bnetserver");
71TCHAR serviceLongName[] = _T("TrinityCore bnet service");
72TCHAR serviceDescription[] = _T("TrinityCore Battle.net emulator authentication service");
73/*
74* -1 - not in service mode
75* 0 - stopped
76* 1 - running
77* 2 - paused
78*/
80
81void ServiceStatusWatcher(std::weak_ptr<Trinity::Asio::DeadlineTimer> serviceStatusWatchTimerRef, std::weak_ptr<Trinity::Asio::IoContext> ioContextRef, boost::system::error_code const& error);
82#endif
83
84bool StartDB();
85void StopDB();
86void SignalHandler(boost::system::error_code const& error, int signalNumber);
87void KeepDatabaseAliveHandler(std::weak_ptr<Trinity::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error);
88void BanExpiryHandler(std::weak_ptr<Trinity::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error);
89variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, fs::path& configDir, std::string& winServiceAction);
90
91std::atomic<bool> Stopped;
92
93int main(int argc, char** argv)
94{
95 signal(SIGABRT, &Trinity::AbortHandler);
96
98
100
101 auto configFile = fs::absolute(_TRINITY_BNET_CONFIG);
102 auto configDir = fs::absolute(_TRINITY_BNET_CONFIG_DIR);
103 std::string winServiceAction;
104 auto vm = GetConsoleArguments(argc, argv, configFile, configDir, winServiceAction);
105 // exit if help or version is enabled
106 if (vm.count("help") || vm.count("version"))
107 return 0;
108
109 uint32 dummy = 0;
110
111 GOOGLE_PROTOBUF_VERIFY_VERSION;
112
113 auto protobufHandle = Trinity::make_unique_ptr_with_deleter<[](void*) { google::protobuf::ShutdownProtobufLibrary(); }>(&dummy);
114
115#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
117 if (winServiceAction == "install")
119 if (winServiceAction == "uninstall")
121 if (winServiceAction == "run")
122 return Trinity::Service::Run();
123#endif
124
125 std::string configError;
126 if (!sConfigMgr->LoadInitial(configFile.generic_string(),
127 std::vector<std::string>(argv, argv + argc),
128 configError))
129 {
130 printf("Error in config file: %s\n", configError.c_str());
131 return 1;
132 }
133
134 std::vector<std::string> loadedConfigFiles;
135 std::vector<std::string> configDirErrors;
136 bool additionalConfigFileLoadSuccess = sConfigMgr->LoadAdditionalDir(configDir.generic_string(), true, loadedConfigFiles, configDirErrors);
137 for (std::string const& loadedConfigFile : loadedConfigFiles)
138 printf("Loaded additional config file %s\n", loadedConfigFile.c_str());
139
140 if (!additionalConfigFileLoadSuccess)
141 {
142 for (std::string const& configDirError : configDirErrors)
143 printf("Error in additional config files: %s\n", configDirError.c_str());
144
145 return 1;
146 }
147
148 std::vector<std::string> overriddenKeys = sConfigMgr->OverrideWithEnvVariablesIfAny();
149
150 sLog->RegisterAppender<AppenderDB>();
151 sLog->Initialize(nullptr);
152
153 Trinity::Banner::Show("bnetserver",
154 [](char const* text)
155 {
156 TC_LOG_INFO("server.bnetserver", "{}", text);
157 },
158 []()
159 {
160 TC_LOG_INFO("server.bnetserver", "Using configuration file {}.", sConfigMgr->GetFilename());
161 TC_LOG_INFO("server.bnetserver", "Using SSL version: {} (library: {})", OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION));
162 TC_LOG_INFO("server.bnetserver", "Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
163 }
164 );
165
166 for (std::string const& key : overriddenKeys)
167 TC_LOG_INFO("server.authserver", "Configuration field '{}' was overridden with environment variable.", key);
168
169 OpenSSLCrypto::threadsSetup(boost::dll::program_location().remove_filename());
170
171 auto opensslHandle = Trinity::make_unique_ptr_with_deleter<[](void*) { OpenSSLCrypto::threadsCleanup(); }>(&dummy);
172
173 // bnetserver PID file creation
174 std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
175 if (!pidFile.empty())
176 {
177 if (uint32 pid = CreatePIDFile(pidFile))
178 TC_LOG_INFO("server.bnetserver", "Daemon PID: {}\n", pid);
179 else
180 {
181 TC_LOG_ERROR("server.bnetserver", "Cannot create PID file {}.\n", pidFile);
182 return 1;
183 }
184 }
185
187 {
188 TC_LOG_ERROR("server.bnetserver", "Failed to initialize SSL context");
189 return 1;
190 }
191
192 // Initialize the database connection
193 if (!StartDB())
194 return 1;
195
196 auto dbHandle = Trinity::make_unique_ptr_with_deleter<[](void*) { StopDB(); }>(&dummy);
197
198 if (vm.count("update-databases-only"))
199 return 0;
200
202
203 // Load IP Location Database
204 sIPLocation->Load();
205
206 std::shared_ptr<Trinity::Asio::IoContext> ioContext = std::make_shared<Trinity::Asio::IoContext>();
207
208 auto ioContextWork = boost::asio::make_work_guard(ioContext->get_executor());
209
210 std::thread ioContextThread(&Trinity::Asio::IoContext::run, ioContext.get());
211
212 auto threadJoinHandle = Trinity::make_unique_ptr_with_deleter<[](std::thread* t) { t->join(); }>(&ioContextThread);
213
214 auto ioContextWorkGuardHandle = Trinity::make_unique_ptr_with_deleter<[](auto* wg) { wg->reset(); }>(&ioContextWork);
215
217
218 std::string httpBindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
219 int32 httpPort = sConfigMgr->GetIntDefault("LoginREST.Port", 8081);
220 if (httpPort <= 0 || httpPort > 0xFFFF)
221 {
222 TC_LOG_ERROR("server.bnetserver", "Specified login service port ({}) out of allowed range (1-65535)", httpPort);
223 return 1;
224 }
225
226 if (!sLoginService.StartNetwork(*ioContext, httpBindIp, httpPort))
227 {
228 TC_LOG_ERROR("server.bnetserver", "Failed to initialize login service");
229 return 1;
230 }
231
232 auto sLoginServiceHandle = Trinity::make_unique_ptr_with_deleter<&Battlenet::LoginRESTService::StopNetwork>(&sLoginService);
233
234 // Start the listening port (acceptor) for auth connections
235 int32 bnport = sConfigMgr->GetIntDefault("BattlenetPort", 1119);
236 if (bnport <= 0 || bnport > 0xFFFF)
237 {
238 TC_LOG_ERROR("server.bnetserver", "Specified battle.net port ({}) out of allowed range (1-65535)", bnport);
239 return 1;
240 }
241
242 // Get the list of realms for the server
243 sRealmList->Initialize(*ioContext, sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 10));
244
245 auto sRealmListHandle = Trinity::make_unique_ptr_with_deleter<&RealmList::Close>(sRealmList);
246
247 std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
248
249 if (!sSessionMgr.StartNetwork(*ioContext, bindIp, bnport, 1))
250 {
251 TC_LOG_ERROR("server.bnetserver", "Failed to initialize network");
252 return 1;
253 }
254
255 auto sSessionMgrHandle = Trinity::make_unique_ptr_with_deleter<&Battlenet::SessionManager::StopNetwork>(&sSessionMgr);
256
257 // Set signal handlers
258 boost::asio::basic_signal_set<Trinity::Asio::IoContext::Executor> signals(*ioContext, SIGINT, SIGTERM);
259#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
260 signals.add(SIGBREAK);
261#endif
262 signals.async_wait(SignalHandler);
263
264 // Set process priority according to configuration settings
265 SetProcessPriority("server.bnetserver", sConfigMgr->GetIntDefault(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetBoolDefault(CONFIG_HIGH_PRIORITY, false));
266
267 // Enabled a timed callback for handling the database keep alive ping
268 int32 dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30);
269 std::shared_ptr<Trinity::Asio::DeadlineTimer> dbPingTimer = std::make_shared<Trinity::Asio::DeadlineTimer>(*ioContext);
270 dbPingTimer->expires_after(std::chrono::minutes(dbPingInterval));
271 dbPingTimer->async_wait([timerRef = std::weak_ptr(dbPingTimer), dbPingInterval](boost::system::error_code const& error) mutable
272 {
273 KeepDatabaseAliveHandler(std::move(timerRef), dbPingInterval, error);
274 });
275
276 int32 banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60);
277 std::shared_ptr<Trinity::Asio::DeadlineTimer> banExpiryCheckTimer = std::make_shared<Trinity::Asio::DeadlineTimer>(*ioContext);
278 banExpiryCheckTimer->expires_after(std::chrono::seconds(banExpiryCheckInterval));
279 banExpiryCheckTimer->async_wait([timerRef = std::weak_ptr(banExpiryCheckTimer), banExpiryCheckInterval](boost::system::error_code const& error) mutable
280 {
281 BanExpiryHandler(std::move(timerRef), banExpiryCheckInterval, error);
282 });
283
284#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
285 std::shared_ptr<Trinity::Asio::DeadlineTimer> serviceStatusWatchTimer;
286 if (m_ServiceStatus != -1)
287 {
288 serviceStatusWatchTimer = std::make_shared<Trinity::Asio::DeadlineTimer>(*ioContext);
289 serviceStatusWatchTimer->expires_after(1s);
290 serviceStatusWatchTimer->async_wait([timerRef = std::weak_ptr(serviceStatusWatchTimer), ioContextRef = std::weak_ptr(ioContext)](boost::system::error_code const& error) mutable
291 {
292 ServiceStatusWatcher(std::move(timerRef), std::move(ioContextRef), error);
293 });
294 }
295#endif
296
297 while (!Stopped)
298 std::this_thread::sleep_for(50ms);
299
300 TC_LOG_INFO("server.bnetserver", "Halting process...");
301
302 return 0;
303}
304
307{
309
310 // Load databases
311 DatabaseLoader loader("server.bnetserver", DatabaseLoader::DATABASE_NONE);
312 loader
313 .AddDatabase(LoginDatabase, "Login");
314
315 if (!loader.Load())
316 return false;
317
318 TC_LOG_INFO("server.bnetserver", "Started auth database connection pool.");
319 sLog->SetRealmId(0); // Enables DB appenders when realm is set.
320 return true;
321}
322
324void StopDB()
325{
326 LoginDatabase.Close();
328}
329
330void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
331{
332 if (!error)
333 Stopped = true;
334}
335
336void KeepDatabaseAliveHandler(std::weak_ptr<Trinity::Asio::DeadlineTimer> dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const& error)
337{
338 if (!error)
339 {
340 if (std::shared_ptr<Trinity::Asio::DeadlineTimer> dbPingTimer = dbPingTimerRef.lock())
341 {
342 TC_LOG_INFO("server.bnetserver", "Ping MySQL to keep connection alive");
343 LoginDatabase.KeepAlive();
344
345 dbPingTimer->expires_after(std::chrono::minutes(dbPingInterval));
346 dbPingTimer->async_wait([timerRef = std::move(dbPingTimerRef), dbPingInterval](boost::system::error_code const& error) mutable
347 {
348 KeepDatabaseAliveHandler(std::move(timerRef), dbPingInterval, error);
349 });
350 }
351 }
352}
353
354void BanExpiryHandler(std::weak_ptr<Trinity::Asio::DeadlineTimer> banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const& error)
355{
356 if (!error)
357 {
358 if (std::shared_ptr<Trinity::Asio::DeadlineTimer> banExpiryCheckTimer = banExpiryCheckTimerRef.lock())
359 {
360 LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS));
361 LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS));
363
364 banExpiryCheckTimer->expires_after(std::chrono::seconds(banExpiryCheckInterval));
365 banExpiryCheckTimer->async_wait([timerRef = std::move(banExpiryCheckTimerRef), banExpiryCheckInterval](boost::system::error_code const& error) mutable
366 {
367 BanExpiryHandler(std::move(timerRef), banExpiryCheckInterval, error);
368 });
369 }
370 }
371}
372
373#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
374void ServiceStatusWatcher(std::weak_ptr<Trinity::Asio::DeadlineTimer> serviceStatusWatchTimerRef, std::weak_ptr<Trinity::Asio::IoContext> ioContextRef, boost::system::error_code const& error)
375{
376 if (!error)
377 {
378 if (std::shared_ptr<Trinity::Asio::IoContext> ioContext = ioContextRef.lock())
379 {
380 if (m_ServiceStatus == 0)
381 {
382 Stopped = true;
383 }
384 else if (std::shared_ptr<Trinity::Asio::DeadlineTimer> serviceStatusWatchTimer = serviceStatusWatchTimerRef.lock())
385 {
386 serviceStatusWatchTimer->expires_after(1s);
387 serviceStatusWatchTimer->async_wait([timerRef = std::move(serviceStatusWatchTimerRef), ioContextRef = std::move(ioContextRef)](boost::system::error_code const& error) mutable
388 {
389 ServiceStatusWatcher(std::move(timerRef), std::move(ioContextRef), error);
390 });
391 }
392 }
393 }
394}
395#endif
396
397variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, fs::path& configDir, [[maybe_unused]] std::string& winServiceAction)
398{
399 options_description all("Allowed options");
400 all.add_options()
401 ("help,h", "print usage message")
402 ("version,v", "print version build info")
403 ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_BNET_CONFIG)),
404 "use <arg> as configuration file")
405 ("config-dir,cd", value<fs::path>(&configDir)->default_value(fs::absolute(_TRINITY_BNET_CONFIG_DIR)),
406 "use <arg> as directory with additional config files")
407 ("update-databases-only,u", "updates databases only")
408 ;
409#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
410 options_description win("Windows platform specific options");
411 win.add_options()
412 ("service,s", value<std::string>(&winServiceAction)->default_value(""), "Windows service options: [install | uninstall]")
413 ;
414
415 all.add(win);
416#endif
417 variables_map variablesMap;
418 try
419 {
420 store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap);
421 notify(variablesMap);
422 }
423 catch (std::exception& e)
424 {
425 std::cerr << e.what() << "\n";
426 }
427
428 if (variablesMap.count("help"))
429 {
430 std::cout << all << "\n";
431 }
432 else if (variablesMap.count("version"))
433 {
434 std::cout << GitRevision::GetFullVersion() << "\n";
435 }
436
437 return variablesMap;
438}
439
440#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
442// must be at end of file because of init_seg pragma
444#endif
#define sConfigMgr
Definition Config.h:64
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
#define sIPLocation
Definition IPLocation.h:56
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define sLog
Definition Log.h:156
@ LOGIN_DEL_EXPIRED_IP_BANS
@ LOGIN_UPD_EXPIRED_ACCOUNT_BANS
@ LOGIN_DEL_BNET_EXPIRED_ACCOUNT_BANNED
#define sLoginService
void SetProcessPriority(std::string const &logChannel, uint32 affinity, bool highPriority)
#define CONFIG_HIGH_PRIORITY
#define CONFIG_PROCESSOR_AFFINITY
#define sRealmList
Definition RealmList.h:93
#define sSecretMgr
Definition SecretMgr.h:87
@ SECRET_OWNER_BNETSERVER
Definition SecretMgr.h:39
#define sSessionMgr
uint32 CreatePIDFile(std::string const &filename)
create PID file
Definition Util.cpp:277
std::atomic< bool > Stopped
Definition Main.cpp:91
bool StartDB()
Initialize connection to the database.
Definition Main.cpp:306
void ServiceStatusWatcher(std::weak_ptr< Trinity::Asio::DeadlineTimer > serviceStatusWatchTimerRef, std::weak_ptr< Trinity::Asio::IoContext > ioContextRef, boost::system::error_code const &error)
Definition Main.cpp:374
void BanExpiryHandler(std::weak_ptr< Trinity::Asio::DeadlineTimer > banExpiryCheckTimerRef, int32 banExpiryCheckInterval, boost::system::error_code const &error)
Definition Main.cpp:354
int main(int argc, char **argv)
Definition Main.cpp:93
variables_map GetConsoleArguments(int argc, char **argv, fs::path &configFile, fs::path &configDir, std::string &winServiceAction)
Definition Main.cpp:397
void KeepDatabaseAliveHandler(std::weak_ptr< Trinity::Asio::DeadlineTimer > dbPingTimerRef, int32 dbPingInterval, boost::system::error_code const &error)
Definition Main.cpp:336
#define _TRINITY_BNET_CONFIG_DIR
Definition Main.cpp:64
void StopDB()
Close the connection to the database.
Definition Main.cpp:324
int m_ServiceStatus
Definition Main.cpp:79
#define _TRINITY_BNET_CONFIG
Definition Main.cpp:61
TCHAR serviceLongName[]
Definition Main.cpp:71
void SignalHandler(boost::system::error_code const &error, int signalNumber)
Definition Main.cpp:330
TCHAR serviceName[]
Definition Main.cpp:70
TCHAR serviceDescription[]
Definition Main.cpp:72
INIT_CRASH_HANDLER()
static bool Initialize()
DatabaseLoader & AddDatabase(DatabaseWorkerPool< T > &pool, std::string const &name)
TC_COMMON_API char const * GetFullVersion()
TC_DATABASE_API void Library_Init()
TC_DATABASE_API void Library_End()
TC_COMMON_API void threadsSetup(boost::filesystem::path const &providerModulePath)
Needs to be called before threads using openssl are spawned.
TC_COMMON_API void threadsCleanup()
Needs to be called after threads using openssl are despawned.
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
Definition Banner.cpp:22
TC_COMMON_API void Init()
Definition Locales.cpp:28
TC_NETWORK_API void ScanLocalNetworks()
TC_COMMON_API void Init(_TCHAR *serviceLongName, _TCHAR *serviceName, _TCHAR *serviceDescription, int(*entryPoint)(int argc, char **argv), int *status)
TC_COMMON_API int32 Install()
TC_COMMON_API int32 Uninstall()
TC_COMMON_API int32 Run()
TC_COMMON_API void VerifyOsVersion()
Definition Util.cpp:35
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
Definition Memory.h:133
void AbortHandler(int sigval) noexcept
Definition Errors.cpp:160