Updated Copyright year to 2013
[getmangos.git] / src / realmd / Main.cpp
blobb861a389e7216f6f44a65294b9e96d6c1e73deb0
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /// \addtogroup realmd Realm Daemon
20 /// @{
21 /// \file
23 #include "Common.h"
24 #include "Database/DatabaseEnv.h"
25 #include "RealmList.h"
27 #include "Config/Config.h"
28 #include "Log.h"
29 #include "AuthSocket.h"
30 #include "SystemConfig.h"
31 #include "revision.h"
32 #include "revision_nr.h"
33 #include "revision_sql.h"
34 #include "Util.h"
35 #include <openssl/opensslv.h>
36 #include <openssl/crypto.h>
38 #include <ace/Get_Opt.h>
39 #include <ace/Dev_Poll_Reactor.h>
40 #include <ace/TP_Reactor.h>
41 #include <ace/ACE.h>
42 #include <ace/Acceptor.h>
43 #include <ace/SOCK_Acceptor.h>
45 #ifdef WIN32
46 #include "ServiceWin32.h"
47 char serviceName[] = "realmd";
48 char serviceLongName[] = "MaNGOS realmd service";
49 char serviceDescription[] = "Massive Network Game Object Server";
51 * -1 - not in service mode
52 * 0 - stopped
53 * 1 - running
54 * 2 - paused
56 int m_ServiceStatus = -1;
57 #else
58 #include "PosixDaemon.h"
59 #endif
61 bool StartDB();
62 void UnhookSignals();
63 void HookSignals();
65 bool stopEvent = false; ///< Setting it to true stops the server
67 DatabaseType LoginDatabase; ///< Accessor to the realm server database
69 /// Print out the usage string for this program on the console.
70 void usage(const char* prog)
72 sLog.outString("Usage: \n %s [<options>]\n"
73 " -v, --version print version and exist\n\r"
74 " -c config_file use config_file as configuration file\n\r"
75 #ifdef WIN32
76 " Running as service functions:\n\r"
77 " -s run run as service\n\r"
78 " -s install install service\n\r"
79 " -s uninstall uninstall service\n\r"
80 #else
81 " Running as daemon functions:\n\r"
82 " -s run run as daemon\n\r"
83 " -s stop stop daemon\n\r"
84 #endif
85 , prog);
88 /// Launch the realm server
89 extern int main(int argc, char** argv)
91 ///- Command line parsing
92 char const* cfg_file = _REALMD_CONFIG;
94 char const* options = ":c:s:";
96 ACE_Get_Opt cmd_opts(argc, argv, options);
97 cmd_opts.long_option("version", 'v');
99 char serviceDaemonMode = '\0';
101 int option;
102 while ((option = cmd_opts()) != EOF)
104 switch (option)
106 case 'c':
107 cfg_file = cmd_opts.opt_arg();
108 break;
109 case 'v':
110 printf("%s\n", _FULLVERSION(REVISION_DATE, REVISION_TIME, REVISION_NR, REVISION_ID));
111 return 0;
113 case 's':
115 const char* mode = cmd_opts.opt_arg();
117 if (!strcmp(mode, "run"))
118 serviceDaemonMode = 'r';
119 #ifdef WIN32
120 else if (!strcmp(mode, "install"))
121 serviceDaemonMode = 'i';
122 else if (!strcmp(mode, "uninstall"))
123 serviceDaemonMode = 'u';
124 #else
125 else if (!strcmp(mode, "stop"))
126 serviceDaemonMode = 's';
127 #endif
128 else
130 sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
131 usage(argv[0]);
132 Log::WaitBeforeContinueIfNeed();
133 return 1;
135 break;
137 case ':':
138 sLog.outError("Runtime-Error: -%c option requires an input argument", cmd_opts.opt_opt());
139 usage(argv[0]);
140 Log::WaitBeforeContinueIfNeed();
141 return 1;
142 default:
143 sLog.outError("Runtime-Error: bad format of commandline arguments");
144 usage(argv[0]);
145 Log::WaitBeforeContinueIfNeed();
146 return 1;
150 #ifdef WIN32 // windows service command need execute before config read
151 switch (serviceDaemonMode)
153 case 'i':
154 if (WinServiceInstall())
155 sLog.outString("Installing service");
156 return 1;
157 case 'u':
158 if (WinServiceUninstall())
159 sLog.outString("Uninstalling service");
160 return 1;
161 case 'r':
162 WinServiceRun();
163 break;
165 #endif
167 if (!sConfig.SetSource(cfg_file))
169 sLog.outError("Could not find configuration file %s.", cfg_file);
170 Log::WaitBeforeContinueIfNeed();
171 return 1;
174 #ifndef WIN32 // posix daemon commands need apply after config read
175 switch (serviceDaemonMode)
177 case 'r':
178 startDaemon();
179 break;
180 case 's':
181 stopDaemon();
182 break;
184 #endif
186 sLog.Initialize();
188 sLog.outString("%s [realm-daemon]", _FULLVERSION(REVISION_DATE, REVISION_TIME, REVISION_NR, REVISION_ID));
189 sLog.outString("<Ctrl-C> to stop.\n");
190 sLog.outString("Using configuration file %s.", cfg_file);
192 ///- Check the version of the configuration file
193 uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
194 if (confVersion < _REALMDCONFVERSION)
196 sLog.outError("*****************************************************************************");
197 sLog.outError(" WARNING: Your realmd.conf version indicates your conf file is out of date!");
198 sLog.outError(" Please check for updates, as your current default values may cause");
199 sLog.outError(" strange behavior.");
200 sLog.outError("*****************************************************************************");
201 Log::WaitBeforeContinueIfNeed();
204 DETAIL_LOG("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
205 if (SSLeay() < 0x009080bfL)
207 DETAIL_LOG("WARNING: Outdated version of OpenSSL lib. Logins to server may not work!");
208 DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]");
211 DETAIL_LOG("Using ACE: %s", ACE_VERSION);
213 #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
214 ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
215 #else
216 ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
217 #endif
219 sLog.outBasic("Max allowed open files is %d", ACE::max_handles());
221 /// realmd PID file creation
222 std::string pidfile = sConfig.GetStringDefault("PidFile", "");
223 if (!pidfile.empty())
225 uint32 pid = CreatePIDFile(pidfile);
226 if (!pid)
228 sLog.outError("Cannot create PID file %s.\n", pidfile.c_str());
229 Log::WaitBeforeContinueIfNeed();
230 return 1;
233 sLog.outString("Daemon PID: %u\n", pid);
236 ///- Initialize the database connection
237 if (!StartDB())
239 Log::WaitBeforeContinueIfNeed();
240 return 1;
243 ///- Get the list of realms for the server
244 sRealmList.Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20));
245 if (sRealmList.size() == 0)
247 sLog.outError("No valid realms specified.");
248 Log::WaitBeforeContinueIfNeed();
249 return 1;
252 // cleanup query
253 // set expired bans to inactive
254 LoginDatabase.BeginTransaction();
255 LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
256 LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
257 LoginDatabase.CommitTransaction();
259 ///- Launch the listening network socket
260 ACE_Acceptor<AuthSocket, ACE_SOCK_Acceptor> acceptor;
262 uint16 rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT);
263 std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
265 ACE_INET_Addr bind_addr(rmport, bind_ip.c_str());
267 if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
269 sLog.outError("MaNGOS realmd can not bind to %s:%d", bind_ip.c_str(), rmport);
270 Log::WaitBeforeContinueIfNeed();
271 return 1;
274 ///- Catch termination signals
275 HookSignals();
277 ///- Handle affinity for multiple processors and process priority on Windows
278 #ifdef WIN32
280 HANDLE hProcess = GetCurrentProcess();
282 uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
283 if (Aff > 0)
285 ULONG_PTR appAff;
286 ULONG_PTR sysAff;
288 if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
290 ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
292 if (!curAff)
294 sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x", Aff, appAff);
296 else
298 if (SetProcessAffinityMask(hProcess, curAff))
299 sLog.outString("Using processors (bitmask, hex): %x", curAff);
300 else
301 sLog.outError("Can't set used processors (hex): %x", curAff);
304 sLog.outString();
307 bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
309 if (Prio)
311 if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
312 sLog.outString("realmd process priority class set to HIGH");
313 else
314 sLog.outError("Can't set realmd process priority class.");
315 sLog.outString();
318 #endif
320 // server has started up successfully => enable async DB requests
321 LoginDatabase.AllowAsyncTransactions();
323 // maximum counter for next ping
324 uint32 numLoops = (sConfig.GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
325 uint32 loopCounter = 0;
327 #ifndef WIN32
328 detachDaemon();
329 #endif
330 ///- Wait for termination signal
331 while (!stopEvent)
333 // dont move this outside the loop, the reactor will modify it
334 ACE_Time_Value interval(0, 100000);
336 if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
337 break;
339 if ((++loopCounter) == numLoops)
341 loopCounter = 0;
342 DETAIL_LOG("Ping MySQL to keep connection alive");
343 LoginDatabase.Ping();
345 #ifdef WIN32
346 if (m_ServiceStatus == 0) stopEvent = true;
347 while (m_ServiceStatus == 2) Sleep(1000);
348 #endif
351 ///- Wait for the delay thread to exit
352 LoginDatabase.HaltDelayThread();
354 ///- Remove signal handling before leaving
355 UnhookSignals();
357 sLog.outString("Halting process...");
358 return 0;
361 /// Handle termination signals
362 /** Put the global variable stopEvent to 'true' if a termination signal is caught **/
363 void OnSignal(int s)
365 switch (s)
367 case SIGINT:
368 case SIGTERM:
369 stopEvent = true;
370 break;
371 #ifdef _WIN32
372 case SIGBREAK:
373 stopEvent = true;
374 break;
375 #endif
378 signal(s, OnSignal);
381 /// Initialize connection to the database
382 bool StartDB()
384 std::string dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
385 if (dbstring.empty())
387 sLog.outError("Database not specified");
388 return false;
391 sLog.outString("Login Database total connections: %i", 1 + 1);
393 if (!LoginDatabase.Initialize(dbstring.c_str()))
395 sLog.outError("Cannot connect to database");
396 return false;
399 if (!LoginDatabase.CheckRequiredField("realmd_db_version", REVISION_DB_REALMD))
401 ///- Wait for already started DB delay threads to end
402 LoginDatabase.HaltDelayThread();
403 return false;
406 return true;
409 /// Define hook 'OnSignal' for all termination signals
410 void HookSignals()
412 signal(SIGINT, OnSignal);
413 signal(SIGTERM, OnSignal);
414 #ifdef _WIN32
415 signal(SIGBREAK, OnSignal);
416 #endif
419 /// Unhook the signals before leaving
420 void UnhookSignals()
422 signal(SIGINT, 0);
423 signal(SIGTERM, 0);
424 #ifdef _WIN32
425 signal(SIGBREAK, 0);
426 #endif
429 /// @}