PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/core.c

https://gitlab.com/evol/hercules
C | 482 lines | 337 code | 49 blank | 96 comment | 43 complexity | 77a465f7cb4e07b31082d2087fb367b3 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0
  1. /**
  2. * This file is part of Hercules.
  3. * http://herc.ws - http://github.com/HerculesWS/Hercules
  4. *
  5. * Copyright (C) 2012-2015 Hercules Dev Team
  6. * Copyright (C) Athena Dev Teams
  7. *
  8. * Hercules is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #define HERCULES_CORE
  22. #include "config/core.h"
  23. #include "core.h"
  24. #include "common/cbasetypes.h"
  25. #include "common/console.h"
  26. #include "common/db.h"
  27. #include "common/memmgr.h"
  28. #include "common/mmo.h"
  29. #include "common/random.h"
  30. #include "common/showmsg.h"
  31. #include "common/strlib.h"
  32. #include "common/sysinfo.h"
  33. #include "common/nullpo.h"
  34. #ifndef MINICORE
  35. # include "common/HPM.h"
  36. # include "common/conf.h"
  37. # include "common/ers.h"
  38. # include "common/socket.h"
  39. # include "common/sql.h"
  40. # include "common/thread.h"
  41. # include "common/timer.h"
  42. # include "common/utils.h"
  43. #endif
  44. #ifndef _WIN32
  45. # include <unistd.h>
  46. #else
  47. # include "common/winapi.h" // Console close event handling
  48. #endif
  49. #include <signal.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. /// Called when a terminate signal is received.
  53. void (*shutdown_callback)(void) = NULL;
  54. struct core_interface core_s;
  55. struct core_interface *core = &core_s;
  56. #ifndef MINICORE // minimalist Core
  57. // Added by Gabuzomeu
  58. //
  59. // This is an implementation of signal() using sigaction() for portability.
  60. // (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
  61. // Programming in the UNIX Environment_.
  62. //
  63. #ifdef WIN32 // windows don't have SIGPIPE
  64. #define SIGPIPE SIGINT
  65. #endif
  66. #ifndef POSIX
  67. #define compat_signal(signo, func) signal((signo), (func))
  68. #else
  69. sigfunc *compat_signal(int signo, sigfunc *func) {
  70. struct sigaction sact, oact;
  71. sact.sa_handler = func;
  72. sigemptyset(&sact.sa_mask);
  73. sact.sa_flags = 0;
  74. #ifdef SA_INTERRUPT
  75. sact.sa_flags |= SA_INTERRUPT; /* SunOS */
  76. #endif
  77. if (sigaction(signo, &sact, &oact) < 0)
  78. return (SIG_ERR);
  79. return (oact.sa_handler);
  80. }
  81. #endif
  82. /*======================================
  83. * CORE : Console events for Windows
  84. *--------------------------------------*/
  85. #ifdef _WIN32
  86. static BOOL WINAPI console_handler(DWORD c_event) {
  87. switch(c_event) {
  88. case CTRL_CLOSE_EVENT:
  89. case CTRL_LOGOFF_EVENT:
  90. case CTRL_SHUTDOWN_EVENT:
  91. if( shutdown_callback != NULL )
  92. shutdown_callback();
  93. else
  94. core->runflag = CORE_ST_STOP;// auto-shutdown
  95. break;
  96. default:
  97. return FALSE;
  98. }
  99. return TRUE;
  100. }
  101. static void cevents_init(void) {
  102. if (SetConsoleCtrlHandler(console_handler,TRUE)==FALSE)
  103. ShowWarning ("Unable to install the console handler!\n");
  104. }
  105. #endif
  106. /*======================================
  107. * CORE : Signal Sub Function
  108. *--------------------------------------*/
  109. static void sig_proc(int sn) {
  110. static int is_called = 0;
  111. switch (sn) {
  112. case SIGINT:
  113. case SIGTERM:
  114. if (++is_called > 3)
  115. exit(EXIT_SUCCESS);
  116. if( shutdown_callback != NULL )
  117. shutdown_callback();
  118. else
  119. core->runflag = CORE_ST_STOP;// auto-shutdown
  120. break;
  121. case SIGSEGV:
  122. case SIGFPE:
  123. do_abort();
  124. // Pass the signal to the system's default handler
  125. compat_signal(sn, SIG_DFL);
  126. raise(sn);
  127. break;
  128. #ifndef _WIN32
  129. case SIGXFSZ:
  130. // ignore and allow it to set errno to EFBIG
  131. ShowWarning ("Max file size reached!\n");
  132. //run_flag = 0; // should we quit?
  133. break;
  134. case SIGPIPE:
  135. //ShowInfo ("Broken pipe found... closing socket\n"); // set to eof in socket.c
  136. break; // does nothing here
  137. #endif
  138. }
  139. }
  140. void signals_init (void) {
  141. compat_signal(SIGTERM, sig_proc);
  142. compat_signal(SIGINT, sig_proc);
  143. #ifndef _DEBUG // need unhandled exceptions to debug on Windows
  144. compat_signal(SIGSEGV, sig_proc);
  145. compat_signal(SIGFPE, sig_proc);
  146. #endif
  147. #ifndef _WIN32
  148. compat_signal(SIGILL, SIG_DFL);
  149. compat_signal(SIGXFSZ, sig_proc);
  150. compat_signal(SIGPIPE, sig_proc);
  151. compat_signal(SIGBUS, SIG_DFL);
  152. compat_signal(SIGTRAP, SIG_DFL);
  153. #endif
  154. }
  155. #endif
  156. /**
  157. * Warns the user if executed as superuser (root)
  158. */
  159. void usercheck(void) {
  160. if (sysinfo->is_superuser()) {
  161. ShowWarning("You are running Hercules with root privileges, it is not necessary.\n");
  162. }
  163. }
  164. void core_defaults(void) {
  165. nullpo_defaults();
  166. #ifndef MINICORE
  167. hpm_defaults();
  168. HCache_defaults();
  169. #endif
  170. sysinfo_defaults();
  171. console_defaults();
  172. strlib_defaults();
  173. malloc_defaults();
  174. showmsg_defaults();
  175. cmdline_defaults();
  176. #ifndef MINICORE
  177. libconfig_defaults();
  178. sql_defaults();
  179. timer_defaults();
  180. db_defaults();
  181. socket_defaults();
  182. #endif
  183. }
  184. /**
  185. * Returns the source (core or plugin name) for the given command-line argument
  186. */
  187. const char *cmdline_arg_source(struct CmdlineArgData *arg) {
  188. #ifdef MINICORE
  189. return "core";
  190. #else // !MINICORE
  191. return HPM->pid2name(arg->pluginID);
  192. #endif // MINICORE
  193. }
  194. /**
  195. * Defines a command line argument.
  196. *
  197. * @param pluginID the source plugin ID (use HPM_PID_CORE if loading from the core).
  198. * @param name the command line argument name, including the leading '--'.
  199. * @param shortname an optional short form (single-character, it will be prefixed with '-'). Use '\0' to skip.
  200. * @param func the triggered function.
  201. * @param help the help string to be displayed by '--help', if any.
  202. * @param options options associated to the command-line argument. @see enum cmdline_options.
  203. * @return the success status.
  204. */
  205. bool cmdline_arg_add(unsigned int pluginID, const char *name, char shortname, CmdlineExecFunc func, const char *help, unsigned int options) {
  206. struct CmdlineArgData *data = NULL;
  207. VECTOR_ENSURE(cmdline->args_data, 1, 1);
  208. VECTOR_PUSHZEROED(cmdline->args_data);
  209. data = &VECTOR_LAST(cmdline->args_data);
  210. data->pluginID = pluginID;
  211. data->name = aStrdup(name);
  212. data->shortname = shortname;
  213. data->func = func;
  214. data->help = aStrdup(help);
  215. data->options = options;
  216. return true;
  217. }
  218. /**
  219. * Help screen to be displayed by '--help'.
  220. */
  221. static CMDLINEARG(help)
  222. {
  223. int i;
  224. ShowInfo("Usage: %s [options]\n", SERVER_NAME);
  225. ShowInfo("\n");
  226. ShowInfo("Options:\n");
  227. for (i = 0; i < VECTOR_LENGTH(cmdline->args_data); i++) {
  228. struct CmdlineArgData *data = &VECTOR_INDEX(cmdline->args_data, i);
  229. char altname[16], paramnames[256];
  230. if (data->shortname) {
  231. snprintf(altname, sizeof(altname), " [-%c]", data->shortname);
  232. } else {
  233. *altname = '\0';
  234. }
  235. snprintf(paramnames, sizeof(paramnames), "%s%s%s", data->name, altname, (data->options&CMDLINE_OPT_PARAM) ? " <name>" : "");
  236. ShowInfo(" %-30s %s [%s]\n", paramnames, data->help ? data->help : "<no description provided>", cmdline->arg_source(data));
  237. }
  238. return false;
  239. }
  240. /**
  241. * Info screen to be displayed by '--version'.
  242. */
  243. static CMDLINEARG(version)
  244. {
  245. ShowInfo(CL_GREEN"Website/Forum:"CL_RESET"\thttp://herc.ws/\n");
  246. ShowInfo(CL_GREEN"IRC Channel:"CL_RESET"\tirc://irc.rizon.net/#Hercules\n");
  247. ShowInfo("Open "CL_WHITE"readme.txt"CL_RESET" for more information.\n");
  248. return false;
  249. }
  250. /**
  251. * Checks if there is a value available for the current argument
  252. *
  253. * @param name the current argument's name.
  254. * @param current_arg the current argument's position.
  255. * @param argc the program's argc.
  256. * @return true if a value for the current argument is available on the command line.
  257. */
  258. bool cmdline_arg_next_value(const char *name, int current_arg, int argc)
  259. {
  260. if (current_arg >= argc-1) {
  261. ShowError("Missing value for option '%s'.\n", name);
  262. return false;
  263. }
  264. return true;
  265. }
  266. /**
  267. * Executes the command line arguments handlers.
  268. *
  269. * @param argc the program's argc
  270. * @param argv the program's argv
  271. * @param options Execution options. Allowed values:
  272. * - CMDLINE_OPT_SILENT: Scans the argv for a command line argument that
  273. * requires the server's silent mode, and triggers it. Invalid command line
  274. * arguments don't cause it to abort. No command handlers are executed.
  275. * - CMDLINE_OPT_PREINIT: Scans the argv for command line arguments with the
  276. * CMDLINE_OPT_PREINIT flag set and executes their handlers. Invalid command
  277. * line arguments don't cause it to abort. Handler's failure causes the
  278. * program to abort.
  279. * - CMDLINE_OPT_NORMAL: Scans the argv for normal command line arguments,
  280. * skipping the pre-init ones, and executes their handlers. Invalid command
  281. * line arguments or handler's failure cause the program to abort.
  282. * @return the amount of command line handlers successfully executed.
  283. */
  284. int cmdline_exec(int argc, char **argv, unsigned int options)
  285. {
  286. int count = 0, i;
  287. for (i = 1; i < argc; i++) {
  288. int j;
  289. struct CmdlineArgData *data = NULL;
  290. const char *arg = argv[i];
  291. if (arg[0] != '-') { // All arguments must begin with '-'
  292. ShowError("Invalid option '%s'.\n", argv[i]);
  293. exit(EXIT_FAILURE);
  294. }
  295. if (arg[1] != '-' && strlen(arg) == 2) {
  296. ARR_FIND(0, VECTOR_LENGTH(cmdline->args_data), j, VECTOR_INDEX(cmdline->args_data, j).shortname == arg[1]);
  297. } else {
  298. ARR_FIND(0, VECTOR_LENGTH(cmdline->args_data), j, strcmpi(VECTOR_INDEX(cmdline->args_data, j).name, arg) == 0);
  299. }
  300. if (j == VECTOR_LENGTH(cmdline->args_data)) {
  301. if (options&(CMDLINE_OPT_SILENT|CMDLINE_OPT_PREINIT))
  302. continue;
  303. ShowError("Unknown option '%s'.\n", arg);
  304. exit(EXIT_FAILURE);
  305. }
  306. data = &VECTOR_INDEX(cmdline->args_data, j);
  307. if (data->options&CMDLINE_OPT_PARAM) {
  308. if (!cmdline->arg_next_value(arg, i, argc))
  309. exit(EXIT_FAILURE);
  310. i++;
  311. }
  312. if (options&CMDLINE_OPT_SILENT) {
  313. if (data->options&CMDLINE_OPT_SILENT) {
  314. showmsg->silent = 0x7; // silence information and status messages
  315. break;
  316. }
  317. } else if ((data->options&CMDLINE_OPT_PREINIT) == (options&CMDLINE_OPT_PREINIT)) {
  318. const char *param = NULL;
  319. if (data->options&CMDLINE_OPT_PARAM) {
  320. param = argv[i]; // Already incremented above
  321. }
  322. if (!data->func(arg, param))
  323. exit(EXIT_SUCCESS);
  324. count++;
  325. }
  326. }
  327. return count;
  328. }
  329. /**
  330. * Defines the global command-line arguments.
  331. */
  332. void cmdline_init(void)
  333. {
  334. #ifdef MINICORE
  335. // Minicore has no HPM. This value isn't used, but the arg_add function requires it, so we're (re)defining it here
  336. #define HPM_PID_CORE ((unsigned int)-1)
  337. #endif
  338. CMDLINEARG_DEF(help, 'h', "Displays this help screen", CMDLINE_OPT_NORMAL);
  339. CMDLINEARG_DEF(version, 'v', "Displays the server's version.", CMDLINE_OPT_NORMAL);
  340. #ifndef MINICORE
  341. CMDLINEARG_DEF2(load-plugin, loadplugin, "Loads an additional plugin (can be repeated).", CMDLINE_OPT_PARAM|CMDLINE_OPT_PREINIT);
  342. #endif // !MINICORE
  343. cmdline_args_init_local();
  344. }
  345. void cmdline_final(void)
  346. {
  347. while (VECTOR_LENGTH(cmdline->args_data) > 0) {
  348. struct CmdlineArgData *data = &VECTOR_POP(cmdline->args_data);
  349. aFree(data->name);
  350. aFree(data->help);
  351. }
  352. VECTOR_CLEAR(cmdline->args_data);
  353. }
  354. struct cmdline_interface cmdline_s;
  355. struct cmdline_interface *cmdline;
  356. void cmdline_defaults(void)
  357. {
  358. cmdline = &cmdline_s;
  359. VECTOR_INIT(cmdline->args_data);
  360. cmdline->init = cmdline_init;
  361. cmdline->final = cmdline_final;
  362. cmdline->arg_add = cmdline_arg_add;
  363. cmdline->exec = cmdline_exec;
  364. cmdline->arg_next_value = cmdline_arg_next_value;
  365. cmdline->arg_source = cmdline_arg_source;
  366. }
  367. /*======================================
  368. * CORE : MAINROUTINE
  369. *--------------------------------------*/
  370. int main (int argc, char **argv) {
  371. int retval = EXIT_SUCCESS;
  372. {// initialize program arguments
  373. char *p1 = SERVER_NAME = argv[0];
  374. char *p2 = p1;
  375. while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL) {
  376. SERVER_NAME = ++p1;
  377. p2 = p1;
  378. }
  379. core->arg_c = argc;
  380. core->arg_v = argv;
  381. core->runflag = CORE_ST_RUN;
  382. }
  383. core_defaults();
  384. iMalloc->init();// needed for Show* in display_title() [FlavioJS]
  385. showmsg->init();
  386. cmdline->init();
  387. cmdline->exec(argc, argv, CMDLINE_OPT_SILENT);
  388. iMalloc->init_messages(); // Initialization messages (after buying us some time to suppress them if needed)
  389. sysinfo->init();
  390. if (!(showmsg->silent&0x1))
  391. console->display_title();
  392. usercheck();
  393. #ifdef MINICORE // minimalist Core
  394. do_init(argc,argv);
  395. do_final();
  396. #else// not MINICORE
  397. set_server_type();
  398. Sql_Init();
  399. rathread_init();
  400. DB->init();
  401. signals_init();
  402. #ifdef _WIN32
  403. cevents_init();
  404. #endif
  405. timer->init();
  406. /* timer first */
  407. rnd_init();
  408. srand((unsigned int)timer->gettick());
  409. console->init();
  410. HCache->init();
  411. HPM->init();
  412. sockt->init();
  413. do_init(argc,argv);
  414. // Main runtime cycle
  415. while (core->runflag != CORE_ST_STOP) {
  416. int next = timer->perform(timer->gettick_nocache());
  417. sockt->perform(next);
  418. }
  419. console->final();
  420. retval = do_final();
  421. HPM->final();
  422. timer->final();
  423. sockt->final();
  424. DB->final();
  425. rathread_final();
  426. ers_final();
  427. #endif
  428. cmdline->final();
  429. //sysinfo->final(); Called by iMalloc->final()
  430. iMalloc->final();
  431. showmsg->final(); // Should be after iMalloc->final()
  432. return retval;
  433. }