[10134] MaNGOS 0.16 release.
[getmangos.git] / src / realmd / Main.cpp
blobd863d808d373bc986dd39bd7de13639ca1baa3dc
1 /*
2 * Copyright (C) 2005-2010 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 #endif
59 bool StartDB();
60 void UnhookSignals();
61 void HookSignals();
63 bool stopEvent = false; ///< Setting it to true stops the server
65 DatabaseType LoginDatabase; ///< Accessor to the realm server database
67 /// Print out the usage string for this program on the console.
68 void usage(const char *prog)
70 sLog.outString("Usage: \n %s [<options>]\n"
71 " -v, --version print version and exist\n\r"
72 " -c config_file use config_file as configuration file\n\r"
73 #ifdef WIN32
74 " Running as service functions:\n\r"
75 " -s run run as service\n\r"
76 " -s install install service\n\r"
77 " -s uninstall uninstall service\n\r"
78 #endif
79 ,prog);
82 /// Launch the realm server
83 extern int main(int argc, char **argv)
85 ///- Command line parsing
86 char const* cfg_file = _REALMD_CONFIG;
88 #ifdef WIN32
89 char const *options = ":c:s:";
90 #else
91 char const *options = ":c:";
92 #endif
94 ACE_Get_Opt cmd_opts(argc, argv, options);
95 cmd_opts.long_option("version", 'v');
97 int option;
98 while ((option = cmd_opts()) != EOF)
100 switch (option)
102 case 'c':
103 cfg_file = cmd_opts.opt_arg();
104 break;
105 case 'v':
106 printf("%s\n", _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_NR,REVISION_ID));
107 return 0;
108 #ifdef WIN32
109 case 's':
111 const char *mode = cmd_opts.opt_arg();
113 if (!strcmp(mode, "install"))
115 if (WinServiceInstall())
116 sLog.outString("Installing service");
117 return 1;
119 else if (!strcmp(mode, "uninstall"))
121 if (WinServiceUninstall())
122 sLog.outString("Uninstalling service");
123 return 1;
125 else if (!strcmp(mode, "run"))
126 WinServiceRun();
127 else
129 sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
130 usage(argv[0]);
131 Log::WaitBeforeContinueIfNeed();
132 return 1;
134 break;
136 #endif
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 if (!sConfig.SetSource(cfg_file))
152 sLog.outError("Could not find configuration file %s.", cfg_file);
153 Log::WaitBeforeContinueIfNeed();
154 return 1;
156 sLog.Initialize();
158 sLog.outString( "%s [realm-daemon]", _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_NR,REVISION_ID) );
159 sLog.outString( "<Ctrl-C> to stop.\n" );
160 sLog.outString("Using configuration file %s.", cfg_file);
162 ///- Check the version of the configuration file
163 uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
164 if (confVersion < _REALMDCONFVERSION)
166 sLog.outError("*****************************************************************************");
167 sLog.outError(" WARNING: Your realmd.conf version indicates your conf file is out of date!");
168 sLog.outError(" Please check for updates, as your current default values may cause");
169 sLog.outError(" strange behavior.");
170 sLog.outError("*****************************************************************************");
171 Log::WaitBeforeContinueIfNeed();
174 DETAIL_LOG("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
175 if (SSLeay() < 0x009080bfL )
177 DETAIL_LOG("WARNING: Outdated version of OpenSSL lib. Logins to server may not work!");
178 DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]");
181 DETAIL_LOG("Using ACE: %s", ACE_VERSION);
183 #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
184 ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
185 #else
186 ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
187 #endif
189 sLog.outBasic("Max allowed open files is %d", ACE::max_handles());
191 /// realmd PID file creation
192 std::string pidfile = sConfig.GetStringDefault("PidFile", "");
193 if(!pidfile.empty())
195 uint32 pid = CreatePIDFile(pidfile);
196 if( !pid )
198 sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() );
199 Log::WaitBeforeContinueIfNeed();
200 return 1;
203 sLog.outString( "Daemon PID: %u\n", pid );
206 ///- Initialize the database connection
207 if(!StartDB())
209 Log::WaitBeforeContinueIfNeed();
210 return 1;
213 ///- Get the list of realms for the server
214 sRealmList.Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20));
215 if (sRealmList.size() == 0)
217 sLog.outError("No valid realms specified.");
218 Log::WaitBeforeContinueIfNeed();
219 return 1;
222 // cleanup query
223 // set expired bans to inactive
224 LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
225 LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
227 ///- Launch the listening network socket
228 ACE_Acceptor<AuthSocket, ACE_SOCK_Acceptor> acceptor;
230 uint16 rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT);
231 std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
233 ACE_INET_Addr bind_addr(rmport, bind_ip.c_str());
235 if(acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
237 sLog.outError("MaNGOS realmd can not bind to %s:%d", bind_ip.c_str(), rmport);
238 Log::WaitBeforeContinueIfNeed();
239 return 1;
242 ///- Catch termination signals
243 HookSignals();
245 ///- Handle affinity for multiple processors and process priority on Windows
246 #ifdef WIN32
248 HANDLE hProcess = GetCurrentProcess();
250 uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
251 if(Aff > 0)
253 ULONG_PTR appAff;
254 ULONG_PTR sysAff;
256 if(GetProcessAffinityMask(hProcess,&appAff,&sysAff))
258 ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
260 if(!curAff )
262 sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x",Aff,appAff);
264 else
266 if(SetProcessAffinityMask(hProcess,curAff))
267 sLog.outString("Using processors (bitmask, hex): %x", curAff);
268 else
269 sLog.outError("Can't set used processors (hex): %x", curAff);
272 sLog.outString();
275 bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
277 if(Prio)
279 if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS))
280 sLog.outString("realmd process priority class set to HIGH");
281 else
282 sLog.outError("ERROR: Can't set realmd process priority class.");
283 sLog.outString();
286 #endif
288 // maximum counter for next ping
289 uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / 100000));
290 uint32 loopCounter = 0;
292 ///- Wait for termination signal
293 while (!stopEvent)
295 // dont move this outside the loop, the reactor will modify it
296 ACE_Time_Value interval(0, 100000);
298 if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
299 break;
301 if( (++loopCounter) == numLoops )
303 loopCounter = 0;
304 DETAIL_LOG("Ping MySQL to keep connection alive");
305 delete LoginDatabase.Query("SELECT 1 FROM realmlist LIMIT 1");
307 #ifdef WIN32
308 if (m_ServiceStatus == 0) stopEvent = true;
309 while (m_ServiceStatus == 2) Sleep(1000);
310 #endif
313 ///- Wait for the delay thread to exit
314 LoginDatabase.HaltDelayThread();
316 ///- Remove signal handling before leaving
317 UnhookSignals();
319 sLog.outString( "Halting process..." );
320 return 0;
323 /// Handle termination signals
324 /** Put the global variable stopEvent to 'true' if a termination signal is caught **/
325 void OnSignal(int s)
327 switch (s)
329 case SIGINT:
330 case SIGTERM:
331 stopEvent = true;
332 break;
333 #ifdef _WIN32
334 case SIGBREAK:
335 stopEvent = true;
336 break;
337 #endif
340 signal(s, OnSignal);
343 /// Initialize connection to the database
344 bool StartDB()
346 std::string dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
347 if(dbstring.empty())
349 sLog.outError("Database not specified");
350 return false;
353 sLog.outString("Database: %s", dbstring.c_str() );
354 if(!LoginDatabase.Initialize(dbstring.c_str()))
356 sLog.outError("Cannot connect to database");
357 return false;
360 if(!LoginDatabase.CheckRequiredField("realmd_db_version",REVISION_DB_REALMD))
362 ///- Wait for already started DB delay threads to end
363 LoginDatabase.HaltDelayThread();
364 return false;
367 return true;
370 /// Define hook 'OnSignal' for all termination signals
371 void HookSignals()
373 signal(SIGINT, OnSignal);
374 signal(SIGTERM, OnSignal);
375 #ifdef _WIN32
376 signal(SIGBREAK, OnSignal);
377 #endif
380 /// Unhook the signals before leaving
381 void UnhookSignals()
383 signal(SIGINT, 0);
384 signal(SIGTERM, 0);
385 #ifdef _WIN32
386 signal(SIGBREAK, 0);
387 #endif
390 /// @}