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