OregonCore  revision fb2a440-git
Your Favourite TBC server
Main.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore 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 "Common.h"
27 #include "Database/DatabaseEnv.h"
28 #include "RealmList.h"
29 
30 #include "Config/Config.h"
31 #include "Log.h"
32 #include "AuthSocket.h"
33 #include "SystemConfig.h"
34 #include "Utilities/Util.h"
35 
36 #include <ace/Get_Opt.h>
37 #include <ace/Dev_Poll_Reactor.h>
38 #include <ace/TP_Reactor.h>
39 #include <ace/ACE.h>
40 #include <ace/Acceptor.h>
41 #include <ace/SOCK_Acceptor.h>
42 
43 // Format is YYYYMMDDRR where RR is the change in the conf file
44 // for that day.
45 #ifndef _REALMDCONFVERSION
46 # define _REALMDCONFVERSION 2010101001
47 #endif
48 
49 #ifndef _OREGON_REALM_CONFIG
50 # define _OREGON_REALM_CONFIG "oregonrealm.conf"
51 #endif
52 
53 #ifdef _WIN32
54 #include "ServiceWin32.h"
55 char serviceName[] = "realmd";
56 char serviceLongName[] = "Oregon realm service";
57 char serviceDescription[] = "Massive Network Game Object Server";
58 /*
59  * -1 - not in service mode
60  * 0 - stopped
61  * 1 - running
62  * 2 - paused
63  */
64 int m_ServiceStatus = -1;
65 #endif
66 
67 bool StartDB();
68 void UnhookSignals();
69 void HookSignals();
70 
71 bool stopEvent = false; // Setting it to true stops the server
72 
73 DatabaseType LoginDatabase; // Accessor to the realm server database
74 
76 
77 // Print out the usage string for this program on the console.
78 void usage(const char* prog)
79 {
80  sLog.outString("Usage: \n %s [<options>]\n"
81  " -v, --version print version and exit\n\r"
82  " -c config_file use config_file as configuration file\n\r"
83  #ifdef _WIN32
84  " Running as service functions:\n\r"
85  " -s run run as service\n\r"
86  " -s install install service\n\r"
87  " -s uninstall uninstall service\n\r"
88  #endif
89  , prog);
90 }
91 
93 extern int main(int argc, char** argv)
94 {
95  // Command line parsing
96  char const* cfg_file = _OREGON_REALM_CONFIG;
97 
98  #ifdef _WIN32
99  char const* options = ":c:s:";
100  #else
101  char const* options = ":c:";
102  #endif
103 
104  ACE_Get_Opt cmd_opts(argc, argv, options);
105  cmd_opts.long_option("version", 'v');
106 
107  int option;
108  while ((option = cmd_opts()) != EOF)
109  {
110  switch (option)
111  {
112  case 'c':
113  cfg_file = cmd_opts.opt_arg();
114  break;
115  case 'v':
116  printf("%s\n", _FULLVERSION);
117  return 0;
118  #ifdef _WIN32
119  case 's':
120  {
121  const char* mode = cmd_opts.opt_arg();
122 
123  if (!strcmp(mode, "install"))
124  {
125  if (WinServiceInstall())
126  sLog.outString("Installing service");
127  return 1;
128  }
129  else if (!strcmp(mode, "uninstall"))
130  {
131  if (WinServiceUninstall())
132  sLog.outString("Uninstalling service");
133  return 1;
134  }
135  else if (!strcmp(mode, "run"))
136  WinServiceRun();
137  else
138  {
139  sLog.outError("Runtime-Error: -%c unsupported argument %s", cmd_opts.opt_opt(), mode);
140  usage(argv[0]);
141  return 1;
142  }
143  break;
144  }
145  #endif
146  case ':':
147  sLog.outError("Runtime-Error: -%c option requires an input argument", cmd_opts.opt_opt());
148  usage(argv[0]);
149  return 1;
150  default:
151  sLog.outError("Runtime-Error: bad format of commandline arguments");
152  usage(argv[0]);
153  return 1;
154  }
155  }
156 
157  if (!sConfig.SetSource(cfg_file))
158  {
159  sLog.outError("Invalid or missing configuration file : %s", cfg_file);
160  sLog.outError("Verify that the file exists and has \'[authserver]\' written in the top of the file!");
161  return 1;
162  }
163  sLog.Initialize();
164 
165  sLog.outString( "%s [realm-daemon]", _FULLVERSION);
166  sLog.outString( "<Ctrl-C> to stop.\n" );
167  sLog.outString("Using configuration file %s.", cfg_file);
168 
169  // Check the version of the configuration file
170  uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
171  if (confVersion < _REALMDCONFVERSION)
172  {
173  sLog.outError("*****************************************************************************");
174  sLog.outError(" WARNING: Your oregonrealm.conf version indicates your conf file is out of date!");
175  sLog.outError(" Please check for updates, as your current default values may cause");
176  sLog.outError(" strange behavior.");
177  sLog.outError("*****************************************************************************");
178  clock_t pause = 3000 + clock();
179 
180  while (pause > clock()) {}
181  }
182 
183  sLog.outDetail("Using ACE: %s", ACE_VERSION);
184 
185  #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
186  ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
187  #else
188  ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
189  #endif
190 
191  sLog.outBasic("Max allowed open files is %d", ACE::max_handles());
192 
193  // realmd PID file creation
194  std::string pidfile = sConfig.GetStringDefault("PidFile", "");
195  if (!pidfile.empty())
196  {
197  uint32 pid = CreatePIDFile(pidfile);
198  if (!pid)
199  {
200  sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() );
201  return 1;
202  }
203 
204  sLog.outString( "Daemon PID: %u\n", pid );
205  }
206 
207  // Initialize the database connection
208  if (!StartDB())
209  return 1;
210 
211  // Get the list of realms for the server
212  sRealmList->Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20));
213  if (sRealmList->size() == 0)
214  {
215  sLog.outError("No valid realms specified.");
216  return 1;
217  }
218 
219  // cleanup query
220  // set expired bans to inactive
221  LoginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
222  LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
223 
224  // Launch the listening network socket
225  ACE_Acceptor<AuthSocket, ACE_SOCK_Acceptor> acceptor;
226 
227  uint16 rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT);
228  std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
229 
230  ACE_INET_Addr bind_addr(rmport, bind_ip.c_str());
231 
232  if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
233  {
234  sLog.outError("realmd can not bind to %s:%d", bind_ip.c_str(), rmport);
235  return 1;
236  }
237 
238  // Catch termination signals
239  HookSignals();
240 
241  // Handle affinity for multiple processors and process priority on Windows
242  #ifdef _WIN32
243  {
244  HANDLE hProcess = GetCurrentProcess();
245 
246  uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
247  if (Aff > 0)
248  {
249  ULONG_PTR appAff;
250  ULONG_PTR sysAff;
251 
252  if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
253  {
254  ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
255 
256  if (!curAff )
257  sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x", Aff, appAff);
258  else
259  {
260  if (SetProcessAffinityMask(hProcess, curAff))
261  sLog.outString("Using processors (bitmask, hex): %x", curAff);
262  else
263  sLog.outError("Can't set used processors (hex): %x", curAff);
264  }
265  }
266  sLog.outString();
267  }
268 
269  bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
270 
271  if (Prio)
272  {
273  if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
274  sLog.outString("realmd process priority class set to HIGH");
275  else
276  sLog.outError("ERROR: Can't set realmd process priority class.");
277  sLog.outString();
278  }
279  }
280  #endif
281 
282  // maximum counter for next ping
283  uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / 100000));
284  uint32 loopCounter = 0;
285 
286  // Wait for termination signal
287  while (!stopEvent)
288  {
289  // dont move this outside the loop, the reactor will modify it
290  ACE_Time_Value interval(0, 100000);
291 
292  if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
293  break;
294 
295  if ( (++loopCounter) == numLoops )
296  {
297  loopCounter = 0;
298  sLog.outDetail("Ping MySQL to keep connection alive");
299  LoginDatabase.Query("SELECT 1 FROM realmlist LIMIT 1");
300  }
301  #ifdef _WIN32
302  if (m_ServiceStatus == 0) stopEvent = true;
303  while (m_ServiceStatus == 2) Sleep(1000);
304  #endif
305  }
306 
307  // Wait for the delay thread to exit
308  LoginDatabase.HaltDelayThread();
309 
310  // Remove signal handling before leaving
311  UnhookSignals();
312 
313  sLog.outString( "Halting process..." );
314  return 0;
315 }
316 
318 
319 void OnSignal(int s)
320 {
321  switch (s)
322  {
323  case SIGINT:
324  case SIGTERM:
325  stopEvent = true;
326  break;
327  #ifdef _WIN32
328  case SIGBREAK:
329  stopEvent = true;
330  break;
331  #endif
332  }
333 
334  signal(s, OnSignal);
335 }
336 
338 bool StartDB()
339 {
340  std::string dbstring = sConfig.GetStringDefault("LoginDatabaseInfo", "");
341  if (dbstring.empty())
342  {
343  sLog.outError("Database not specified");
344  return false;
345  }
346 
347  sLog.outString("Database: %s", dbstring.c_str() );
348  if (!LoginDatabase.Initialize(dbstring.c_str()))
349  {
350  sLog.outError("Cannot connect to database");
351  return false;
352  }
353 
354  return true;
355 }
356 
359 {
360  signal(SIGINT, OnSignal);
361  signal(SIGTERM, OnSignal);
362  #ifdef _WIN32
363  signal(SIGBREAK, OnSignal);
364  #endif
365 }
366 
369 {
370  signal(SIGINT, 0);
371  signal(SIGTERM, 0);
372  #ifdef _WIN32
373  signal(SIGBREAK, 0);
374  #endif
375 }
376 
bool StartDB()
Initialize connection to the database.
Definition: Main.cpp:338
int main(int argc, char **argv)
Definition: Main.cpp:76
#define sConfig
Definition: Config.h:52
#define DEFAULT_REALMSERVER_PORT
Definition: SystemConfig.h:48
#define _REALMDCONFVERSION
Definition: Main.cpp:46
bool Execute(const char *sql)
Definition: Database.cpp:420
#define sLog
Log class singleton.
Definition: Log.h:187
QueryResult_AutoPtr Query(const char *sql)
Definition: Database.cpp:383
#define _OREGON_REALM_CONFIG
Definition: Main.cpp:50
void usage(const char *prog)
Definition: Main.cpp:60
DatabaseType LoginDatabase
Accessor to the realm/login database.
Definition: Main.cpp:55
Definition: Common.h:179
bool Initialize(const char *infoString)
Definition: Database.cpp:75
bool stopEvent
Definition: Main.cpp:71
#define _FULLVERSION
Definition: SystemConfig.h:40
void HaltDelayThread()
Definition: Database.cpp:645
#define sRealmList
Definition: RealmList.h:92
uint32 CreatePIDFile(const std::string &filename)
Definition: Util.cpp:203
void HookSignals()
Define hook &#39;OnSignal&#39; for all termination signals.
Definition: Main.cpp:358
ACE_UINT16 uint16
Definition: Define.h:72
ACE_UINT32 uint32
Definition: Define.h:71
uint32 realmID
Id of the realm.
Definition: Main.cpp:57
void OnSignal(int s)
Handle realm servers termination signals.
Definition: Main.cpp:319
void UnhookSignals()
Unhook the signals before leaving.
Definition: Main.cpp:368