Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
Main.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
13#pragma comment (lib, "Crypt32")
14#include <openssl/crypto.h>
15#include <openssl/opensslv.h>
16#ifdef _WIN32
17#include <winsock2.h>
18#endif
19#include <mysql.h>
20#include <csignal>
21#include <filesystem>
22#include <memory>
23
24#include "Common.h"
29#include "Log.h"
30#include "OpenSSLProviders.h"
31#include "PacketLogServer.h"
32#include "Platform/TimeUtils.h"
33#include "RealmAcceptor.h"
34#include "RealmList.h"
35#include "SystemConfig.h"
36#include "Util.h"
37
38#ifdef __linux__
39#include <sched.h>
40#include <sys/resource.h>
41#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0
42#endif
43
44#ifndef _SKYFIRE_REALM_CONFIG
45# define _SKYFIRE_REALM_CONFIG "authserver.conf"
46#endif
47
48bool StartDB(const char* host, const char* port, const char* user, const char* pass,
49 const char* database, bool noUseConfigDatabaseInfo);
50void StopDB();
51
52bool stopEvent = false; // Setting it to true stops the server
53
54LoginDatabaseWorkerPool LoginDatabase; // Accessor to the authserver database
55
56#ifndef _SKYFIRE_AUTH_DATABASE_HOST
57# define _SKYFIRE_AUTH_DATABASE_HOST ""
58#endif
59
60#ifndef _SKYFIRE_AUTH_DATABASE_PORT
61# define _SKYFIRE_AUTH_DATABASE_PORT ""
62#endif
63
64#ifndef _SKYFIRE_AUTH_DATABASE_USER
65# define _SKYFIRE_AUTH_DATABASE_USER ""
66#endif
67
68#ifndef _SKYFIRE_AUTH_DATABASE_PASS
69# define _SKYFIRE_AUTH_DATABASE_PASS ""
70#endif
71
72#ifndef _SKYFIRE_AUTH_DATABASE
73# define _SKYFIRE_AUTH_DATABASE ""
74#endif
75
76namespace
77{
78 Skyfire::Database::SetupOptions LoadAuthDatabaseSetupOptions()
79 {
81 sConfigMgr->GetBoolDefault("LoginDatabase.AutoSetup", false),
82 sConfigMgr->GetBoolDefault("LoginDatabase.AutoCreate", false),
83 sConfigMgr->GetBoolDefault("LoginDatabase.AutoBaseline", false),
84 sConfigMgr->GetStringDefault("LoginDatabase.SqlPath", ""));
85
87 sConfigMgr->GetBoolDefault("LoginDatabase.AllowUpdateHashMismatch", true);
88
89 return options;
90 }
91
92 Skyfire::Database::SetupRuntimeContext GetAuthSetupRuntimeContext()
93 {
94 return
95 {
96 "server.authserver",
97 "LoginDatabase",
98 "auth",
99 "an auth",
100 "Auth",
101 "Failed while executing auth setup SQL"
102 };
103 }
104
105 bool RunAuthDatabaseSetup(MySQLConnectionInfo const& connectionInfo, Skyfire::Database::SetupOptions const& options)
106 {
107 if (options.SqlPath.empty())
108 {
109 if (options.AutoSetup)
110 {
111 SF_LOG_ERROR("server.authserver", "LoginDatabase.AutoSetup requires LoginDatabase.SqlPath.");
112 return false;
113 }
114
115 SF_LOG_INFO("server.authserver",
116 "LoginDatabase.SqlPath is not configured; auth database update check skipped.");
117 return true;
118 }
119
120 if (connectionInfo._database.empty())
121 {
122 SF_LOG_ERROR("server.authserver", "LoginDatabase setup and update checks require an auth database name.");
123 return false;
124 }
125
126 Skyfire::Database::SetupRuntimeContext context = GetAuthSetupRuntimeContext();
127 if (!Skyfire::Database::EnsureDatabaseExists(connectionInfo, options, context))
128 return false;
129
130 MYSQL* setupConnectionRaw = NULL;
131 if (!Skyfire::Database::ConnectToMySQLServer(connectionInfo, connectionInfo._database.c_str(),
132 setupConnectionRaw, context))
133 return false;
134
135 std::unique_ptr<MYSQL, decltype(&mysql_close)> setupConnection(setupConnectionRaw, mysql_close);
136
137 std::filesystem::path baseSqlPath = Skyfire::Database::GetDatabaseBaseSqlPath(options);
138 bool baseSqlExists = std::filesystem::exists(baseSqlPath);
139 std::vector<Skyfire::Database::SqlUpdateFile> updates = Skyfire::Database::DiscoverSqlUpdates(options);
140
142 if (!Skyfire::Database::LoadDatabaseSetupState(setupConnection.get(), options, state, context))
143 return false;
144
146 Skyfire::Database::BuildAuthDatabaseSetupPlan(options, state, baseSqlExists, updates);
147 if (!plan.IsValid())
148 {
149 SF_LOG_ERROR("server.authserver", "%s", plan.Error.c_str());
150 return false;
151 }
152 Skyfire::Database::LogSetupPlan(plan, updates.size(), false, context);
153
154 if (plan.ShouldInstallBase)
155 {
156 std::string baseSql;
157 SF_LOG_INFO("server.authserver", "Installing auth database base SQL from %s.",
158 baseSqlPath.string().c_str());
159 if (!Skyfire::Database::ExecuteSqlFile(setupConnection.get(), baseSqlPath, baseSql, context))
160 return false;
161 }
162
163 if (!Skyfire::Database::EnsureSetupTrackingTables(setupConnection.get(), context))
164 return false;
165
166 if (!Skyfire::Database::BaselineSetupUpdates(setupConnection.get(), options, plan, context))
167 return false;
168
169 if (!Skyfire::Database::ApplyPendingSetupUpdates(setupConnection.get(), options, plan, context))
170 return false;
171
172 SF_LOG_INFO("server.authserver",
173 "Auth database setup/update complete. Base installed: %s, updates applied: %u, updates baselined: %u.",
174 plan.ShouldInstallBase ? "yes" : "no", uint32(plan.PendingUpdates.size()),
175 uint32(plan.BaselineUpdates.size()));
176 return true;
177 }
178}
179
181{
182 switch (sigNum)
183 {
184 case SIGINT:
185 case SIGTERM:
186 stopEvent = true;
187 break;
188 }
189}
190
192void usage(const char* prog)
193{
194 printf("Usage:\n");
195 printf(" %s [<options>]\n", prog);
196 printf(" -c config_file use config_file as configuration file\n");
197 printf(" --no_use_config_database_info dont use database login info from config file\n");
198 printf(" --db_host sets the database host, requires: --no_use_config_database_info\n");
199 printf(" --db_port sets the database port, requires: --no_use_config_database_info\n");
200 printf(" --db_user sets the database user, requires: --no_use_config_database_info\n");
201 printf(" --db_password sets the database password, requires: "
202 "--no_use_config_database_info\n");
203 printf(" --db_auth sets the auth database, requires: --no_use_config_database_info\n");
204}
205
207extern int main(int argc, char** argv)
208{
209 bool noUseConfigDatabaseInfo = 0;
210 const char* db_Host = _SKYFIRE_AUTH_DATABASE_HOST;
211 const char* db_Port = _SKYFIRE_AUTH_DATABASE_PORT;
212 const char* db_User = _SKYFIRE_AUTH_DATABASE_USER;
213 const char* db_Password = _SKYFIRE_AUTH_DATABASE_PASS;
214 const char* authDB = _SKYFIRE_AUTH_DATABASE;
215
216 // Command line parsing to get the configuration file name
217 char const* configFile = _SKYFIRE_REALM_CONFIG;
218 int count = 1;
219 while (count < argc)
220 {
221 if (strcmp(argv[count], "--help") == 0)
222 {
223 usage(argv[0]);
224 return 1;
225 }
226 if (strcmp(argv[count], "--no_use_config_database_info") == 0)
227 {
228 noUseConfigDatabaseInfo = argv[count];
229 }
230
231 if (noUseConfigDatabaseInfo == 1)
232 {
233 if (strcmp(argv[count], "--db_host") == 0)
234 {
235 if (++count >= argc)
236 {
237 printf("Runtime-Error: --db_host option requires an input argument\n");
238 usage(argv[0]);
239 return 1;
240 }
241 else
242 {
243
244 db_Host = argv[count];
245 }
246 }
247
248 if (strcmp(argv[count], "--db_port") == 0)
249 {
250 if (++count >= argc)
251 {
252 printf("Runtime-Error: --db_port option requires an input argument\n");
253 usage(argv[0]);
254 return 1;
255 }
256 else
257 db_Port = argv[count];
258 }
259
260 if (strcmp(argv[count], "--db_user") == 0)
261 {
262 if (++count >= argc)
263 {
264 printf("Runtime-Error: --db_user option requires an input argument\n");
265 usage(argv[0]);
266 return 1;
267 }
268 else
269 db_User = argv[count];
270 }
271
272 if (strcmp(argv[count], "--db_password") == 0)
273 {
274 if (++count >= argc)
275 {
276 printf("Runtime-Error: --db_password option requires an input argument\n");
277 usage(argv[0]);
278 return 1;
279 }
280 else
281 db_Password = argv[count];
282 }
283
284 if (strcmp(argv[count], "--db_auth") == 0)
285 {
286 if (++count >= argc)
287 {
288 printf("Runtime-Error: --db_auth option requires an input argument\n");
289 usage(argv[0]);
290 return 1;
291 }
292 else
293 authDB = argv[count];
294 }
295 }
296
297 if (strcmp(argv[count], "-c") == 0)
298 {
299 if (++count >= argc)
300 {
301 printf("Runtime-Error: -c option requires an input argument\n");
302 usage(argv[0]);
303 return 1;
304 }
305 else
306 configFile = argv[count];
307 }
308 ++count;
309 }
310
311 if (!sConfigMgr->LoadInitial(configFile))
312 {
313 printf("Invalid or missing configuration file : %s\n", configFile);
314 printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n");
315 return 1;
316 }
317
318 SF_LOG_INFO("server.authserver", "authserver-daemon. revision: % s", SKYFIRE_VER_PRODUCTVERSION_STR);
319 SF_LOG_INFO("server.authserver", "<Ctrl-C> to stop.\n");
320
321 SF_LOG_INFO("server.authserver", " ______ __ __ __ __ ______ __ ______ ______ ");
322 SF_LOG_INFO("server.authserver", " /\\ ___\\/\\ \\/ / /\\ \\_\\ \\/\\ ___/\\ \\/\\ == \\/\\ ___\\ ");
323 SF_LOG_INFO("server.authserver", " \\ \\___ \\ \\ _'-\\ \\____ \\ \\ __\\ \\ \\ \\ __<\\ \\ __\\ ");
324 SF_LOG_INFO("server.authserver", " \\/\\_____\\ \\_\\ \\_\\/\\_____\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_____\\ ");
325 SF_LOG_INFO("server.authserver", " \\/_____/\\/_/\\/_/\\/_____/\\/_/ \\/_/\\/_/ /_/\\/_____/ ");
326 SF_LOG_INFO("server.authserver", " %s Open-sourced Game Emulation", SKYFIRE_VER_LEGALCOPYRIGHT_STR);
327 SF_LOG_INFO("server.authserver", " <http://www.projectskyfire.org/> \n");
328
329 SF_LOG_INFO("server.authserver", "Using configuration file %s.", configFile);
330
332 uint32 confVersion = sConfigMgr->GetIntDefault("ConfVersion", 0);
333 if (confVersion < SKYFIREAUTH_CONFIG_VERSION)
334 {
335 SF_LOG_INFO("server.authserver",
336 "*****************************************************************************");
337 SF_LOG_INFO("server.authserver",
338 " WARNING: Your authserver.conf version indicates your conf file is out of date!");
339 SF_LOG_INFO("server.authserver",
340 " Please check for updates, as your current default values may cause");
341 SF_LOG_INFO("server.authserver", " strange behavior.");
342 SF_LOG_INFO("server.authserver",
343 "*****************************************************************************");
344 }
345
346 SF_LOG_WARN("server.authserver", "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
347
348 if (!Skyfire::LoadOpenSSLProviders("server.authserver"))
349 return 1;
350
351 sPacketLogServer->Initialize("", "PacketLogs");
352
353 // authserver PID file creation
354 std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
355 if (!pidFile.empty())
356 {
357 if (uint32 pid = CreatePIDFile(pidFile))
358 SF_LOG_INFO("server.authserver", "Daemon PID: %u\n", pid);
359 else
360 {
361 SF_LOG_ERROR("server.authserver", "Cannot create PID file %s.\n", pidFile.c_str());
362 return 1;
363 }
364 }
365
366 // Initialize the database connection
367 if (!StartDB(db_Host, db_Port, db_User, db_Password, authDB, noUseConfigDatabaseInfo))
368 return 1;
369
370 // Get the list of realms for the server
371 sRealmList->Initialize(sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20));
372 if (sRealmList->size() == 0)
373 {
374 SF_LOG_ERROR("server.authserver", "No valid realms specified.");
375 return 1;
376 }
377
378 // Launch the listening network socket
379 RealmAcceptor acceptor;
380
381 int32 rmport = sConfigMgr->GetIntDefault("RealmServerPort", 3724);
382 if (rmport < 0 || rmport > 0xFFFF)
383 {
384 SF_LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)");
385 return 1;
386 }
387
388 std::string bind_ip = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
389
390 if (!acceptor.Open(uint16(rmport), bind_ip))
391 {
392 SF_LOG_ERROR("server.authserver", "Auth server can not bind to %s:%d", bind_ip.c_str(), rmport);
393 return 1;
394 }
395
396 // Register authservers's signal handlers
397 std::signal(SIGINT, AuthServerSignalHandler);
398 std::signal(SIGTERM, AuthServerSignalHandler);
399
400#if defined(_WIN32) || defined(__linux__)
401
403 uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0);
404 bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false);
405
406#ifdef _WIN32 // Windows
407 HANDLE hProcess = GetCurrentProcess();
408 if (affinity > 0)
409 {
410 ULONG_PTR appAff;
411 ULONG_PTR sysAff;
412
413 if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
414 {
415 ULONG_PTR currentAffinity = affinity & appAff; // remove non accessible processors
416
417 if (!currentAffinity)
418 SF_LOG_ERROR("server.authserver",
419 "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. "
420 "Accessible processors bitmask (hex): %x", affinity, appAff);
421 else if (SetProcessAffinityMask(hProcess, currentAffinity))
422 SF_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %x", currentAffinity);
423 else
424 SF_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x", currentAffinity);
425 }
426 }
427
428 if (highPriority)
429 {
430 if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
431 SF_LOG_INFO("server.authserver", "authserver process priority class set to HIGH");
432 else
433 SF_LOG_ERROR("server.authserver", "Can't set authserver process priority class.");
434 }
435#else // Linux
436
437 if (affinity > 0)
438 {
439 cpu_set_t mask;
440 CPU_ZERO(&mask);
441
442 for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i)
443 if (affinity & (1 << i))
444 CPU_SET(i, &mask);
445
446 if (sched_setaffinity(0, sizeof(mask), &mask))
447 SF_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x, error: %s",
448 affinity, strerror(errno));
449 else
450 {
451 CPU_ZERO(&mask);
452 sched_getaffinity(0, sizeof(mask), &mask);
453 SF_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask));
454 }
455 }
456
457 if (highPriority)
458 {
459 if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY))
460 SF_LOG_ERROR("server.authserver", "Can't set authserver process priority class, error: %s",
461 strerror(errno));
462 else
463 SF_LOG_INFO("server.authserver", "authserver process priority class set to %i",
464 getpriority(PRIO_PROCESS, 0));
465 }
466
467#endif
468#endif
469
470 // maximum counter for next ping
471 uint32 numLoops = (sConfigMgr->GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
472 uint32 loopCounter = 0;
473
474 // Wait for termination signal
475 while (!stopEvent)
476 {
477 acceptor.Update();
479
480 if ((++loopCounter) == numLoops)
481 {
482 loopCounter = 0;
483 SF_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive");
484 LoginDatabase.KeepAlive();
485 }
486 }
487
488 acceptor.Close();
489
490 // Close the Database Pool and library
491 StopDB();
492
493 SF_LOG_INFO("server.authserver", "Halting process...");
494 return 0;
495}
496
498bool StartDB(const char* host, const char* port, const char* user, const char* pass,
499 const char* database, bool noUseConfigDatabaseInfo)
500{
502 std::string dbstring;
503 if (noUseConfigDatabaseInfo == false)
504 {
505 dbstring = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
506
507 if (dbstring.empty())
508 {
509 SF_LOG_ERROR("server.authserver", "Database not specified");
510 return false;
511 }
512 }
513
514 Skyfire::Database::SetupOptions setupOptions = LoadAuthDatabaseSetupOptions();
515 MySQLConnectionInfo setupConnectionInfo = noUseConfigDatabaseInfo == false
516 ? MySQLConnectionInfo(dbstring)
517 : MySQLConnectionInfo(host, port, user, pass, database);
518
519 if (!RunAuthDatabaseSetup(setupConnectionInfo, setupOptions))
520 return false;
521
522 int32 worker_threads = sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1);
523 if (worker_threads < 1 || worker_threads > 32)
524 {
525 SF_LOG_ERROR("server.authserver", "Improper value specified for LoginDatabase.WorkerThreads, defaulting to 1.");
526 worker_threads = 1;
527 }
528
529 int32 synch_threads = sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1);
530 if (synch_threads < 1 || synch_threads > 32)
531 {
532 SF_LOG_ERROR("server.authserver", "Improper value specified for LoginDatabase.SynchThreads, defaulting to 1.");
533 synch_threads = 1;
534 }
535
536 // NOTE: While authserver is singlethreaded you should keep synch_threads == 1.
537 // Increasing it is just silly since only 1 will be used ever.
538 if (noUseConfigDatabaseInfo == false)
539 {
540 if (!LoginDatabase.Open(dbstring, uint8(worker_threads), uint8(synch_threads)))
541 {
542 SF_LOG_ERROR("server.authserver", "Cannot connect to database");
543 return false;
544 }
545 }
546 else
547 {
548 if (!LoginDatabase.Open(host, port, user, pass, database, uint8(worker_threads), uint8(synch_threads)))
549 {
550 SF_LOG_ERROR("server.authserver", "Cannot connect to database");
551 return false;
552 }
553 }
554
555 SF_LOG_INFO("server.authserver", "Started auth database connection pool.");
556 sLog->SetRealmId(0); // Enables DB appenders when realm is set.
557 return true;
558}
559
561void StopDB()
562{
563 LoginDatabase.Close();
565}
@ MINUTE
Definition Common.h:119
#define sConfigMgr
Definition Config.h:64
std::int32_t int32
Definition Define.h:73
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint16_t uint16
Definition Define.h:78
#define SF_LOG_WARN(filterType__,...)
Definition Log.h:140
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
#define sLog
Definition Log.h:106
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabaseWorkerPool
#define sPacketLogServer
#define sRealmList
Definition RealmList.h:75
#define SKYFIREAUTH_CONFIG_VERSION
uint32 CreatePIDFile(const std::string &filename)
create PID file
Definition Util.cpp:253
static void Library_Init()
static void Library_End()
bool Open(uint16 port, std::string const &bindIp)
std::vector< SqlUpdateFile > DiscoverSqlUpdates(SetupOptions const &options)
bool EnsureDatabaseExists(MySQLConnectionInfo const &connectionInfo, SetupOptions const &options, SetupRuntimeContext const &context)
bool LoadDatabaseSetupState(MYSQL *setupConnection, SetupOptions const &options, SetupState &state, SetupRuntimeContext const &context)
bool ConnectToMySQLServer(MySQLConnectionInfo const &connectionInfo, char const *databaseName, MYSQL *&handle, SetupRuntimeContext const &context)
bool ExecuteSqlFile(MYSQL *setupConnection, std::filesystem::path const &path, std::string &contents, SetupRuntimeContext const &context)
bool BaselineSetupUpdates(MYSQL *setupConnection, SetupOptions const &options, SetupPlan const &plan, SetupRuntimeContext const &context)
std::filesystem::path GetDatabaseBaseSqlPath(SetupOptions const &options)
void LogSetupPlan(SetupPlan const &plan, std::size_t discoveredUpdateCount, bool appliesRequiredSql, SetupRuntimeContext const &context)
SetupPlan BuildAuthDatabaseSetupPlan(SetupOptions const &options, SetupState const &state, bool baseSqlExists, std::vector< SqlUpdateFile > const &updates)
bool EnsureSetupTrackingTables(MYSQL *setupConnection, SetupRuntimeContext const &context)
bool ApplyPendingSetupUpdates(MYSQL *setupConnection, SetupOptions const &options, SetupPlan const &plan, SetupRuntimeContext const &context)
SetupOptions MakeAuthDatabaseSetupOptions(bool autoSetup, bool autoCreate, std::string sqlPath)
void SleepForMilliseconds(uint32 milliseconds)
Definition TimeUtils.h:42
bool LoadOpenSSLProviders(char const *logFilter)
#define _SKYFIRE_AUTH_DATABASE_USER
Definition Main.cpp:65
void usage(const char *prog)
Print out the usage string for this program on the console.
Definition Main.cpp:192
int main(int argc, char **argv)
Launch the auth server.
Definition Main.cpp:207
#define _SKYFIRE_AUTH_DATABASE_PASS
Definition Main.cpp:69
#define _SKYFIRE_REALM_CONFIG
Definition Main.cpp:45
#define _SKYFIRE_AUTH_DATABASE
Definition Main.cpp:73
bool stopEvent
Definition Main.cpp:52
bool StartDB(const char *host, const char *port, const char *user, const char *pass, const char *database, bool noUseConfigDatabaseInfo)
Initialize connection to the database.
Definition Main.cpp:498
void StopDB()
Close the connection to the database.
Definition Main.cpp:561
void AuthServerSignalHandler(int sigNum)
Definition Main.cpp:180
LoginDatabaseWorkerPool LoginDatabase
Definition Main.cpp:54
#define _SKYFIRE_AUTH_DATABASE_PORT
Definition Main.cpp:61
#define _SKYFIRE_AUTH_DATABASE_HOST
Definition Main.cpp:57
std::vector< SqlUpdateFile > PendingUpdates
std::vector< SqlUpdateFile > BaselineUpdates