masterserver.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. /* masterserver.c: A generic masterserver for various games. */
  2. /* Copyright (C) 2003 Andre' Schulz
  3. * This file is part of masterserver.
  4. *
  5. * masterserver is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * masterserver is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with masterserver; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. * The author can be contacted at andre@malchen.de
  20. */
  21. /*
  22. * vim:sw=4:ts=4
  23. */
  24. #include <pthread.h>
  25. #include <stdio.h>
  26. #include <stdarg.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. #include <signal.h>
  31. #include <errno.h>
  32. #include <dirent.h> // opendir()
  33. #include <dlfcn.h> // for dlopen() etc.
  34. #include <fcntl.h>
  35. #include <grp.h> // for changing user
  36. #include <pwd.h> // for changing user
  37. #if defined(SOLARIS)
  38. #include <sys/time.h>
  39. #include <limits.h> // PATH_MAX
  40. #elif defined(__FreeBSD__)
  41. #include <limits.h>
  42. #endif
  43. #include <sys/stat.h>
  44. #include <sys/types.h>
  45. #include <sys/select.h> // for select()
  46. #include <sys/socket.h> // for socket() etc.
  47. #include <netinet/in.h>
  48. #include <arpa/inet.h>
  49. #include <net/if.h> // IFNAMSIZ
  50. #include "masterserver.h" // masterserver stuff
  51. #define MAX_PKT_LEN 1024 // max packet length
  52. #undef LOG_SUBNAME
  53. #define LOG_SUBNAME "main" // logging subcategory description
  54. // linked list for keeping track of plugins
  55. // beware! it's a pointer array! :)
  56. struct masterserver_plugin *plugins = NULL;
  57. // function prototypes
  58. void change_user_and_group_to(char *, char *); // self explanatory
  59. extern void delete_server(struct masterserver_plugin *, int); // generic function for removing servers from server list
  60. void exit_printhelp(void); // print help and exit
  61. void exit_printversion(void); // print version and exit
  62. int load_plugins(char *, void ***); // load plugins from the given directory
  63. void plugin_thread(void *); // main thread calling plugin routines
  64. void plugin_heartbeat_thread(void *); // remove dead servers from server list
  65. extern void register_plugin(struct masterserver_plugin *); // plugins call this functions to register themselves
  66. void sigint_handler(int); // SIGINT handler
  67. void
  68. exit_printhelp(void)
  69. {
  70. fprintf(stdout,
  71. "Usage: masterserver [options]\n"
  72. "Options:\n"
  73. " -D\t\tgo into daemon mode\n"
  74. " -d\t\tdebug mode\n"
  75. " -g groupname\tgroup under which masterserver shall run\n"
  76. " \t\t(only together with -u)\n"
  77. " -h\t\toutput this help text\n"
  78. " -i interface\tbind the masterserver to specific interfaces\n"
  79. " \t\t(use more than once for multiple interfaces)\n"
  80. " -l filename\tlog stdout to a file\n"
  81. /*" -L\tset log level\n"
  82. " \t0 = INFO\n"
  83. " \t1 = WARNING\n"
  84. " \t2 = ERROR\n"*/
  85. " -p path\tset location of plugins\n"
  86. " -u username\tusername under which masterserver shall run\n"
  87. " -V\t\tdisplay version information and exit\n"
  88. "Report bugs to <chickenman@exhale.de>.\n");
  89. }
  90. void
  91. exit_printversion(void)
  92. {
  93. fprintf(stdout,
  94. "Copyright (C) 2003,2004,2005 André Schulz and Ingo Rohlfs\n"
  95. "masterserver comes with NO WARRANTY,\n"
  96. "to the extent permitted by law.\n"
  97. "You may redistribute copies of masterserver\n"
  98. "under the terms of the GNU General Public License.\n"
  99. "For more information about these matters,\n"
  100. "see the files named COPYING.\n\n");
  101. exit(EXIT_SUCCESS);
  102. }
  103. void
  104. change_user_and_group_to(char *user, char *group)
  105. {
  106. int retval = 0;
  107. struct passwd *passwd_temp;
  108. struct group *group_temp;
  109. DEBUG("getting uid of user \"%s\"\n", user);
  110. // check if user exists and get user infos
  111. passwd_temp = getpwnam(user);
  112. if (passwd_temp == NULL) {
  113. ERRORV("getpwnam() (errno: %d - %s)\n", errno, strerror(errno));
  114. exit(EXIT_FAILURE);
  115. }
  116. if (group == NULL)
  117. group_temp = getgrgid(passwd_temp->pw_gid);
  118. else
  119. group_temp = getgrnam(group);
  120. if (group_temp == NULL) {
  121. ERRORV("getgrgid() (errno: %d - %s)\n", errno, strerror(errno));
  122. exit(EXIT_FAILURE);
  123. }
  124. DEBUG("setting gid to %d (\"%s\")\n",
  125. group_temp->gr_gid, group_temp->gr_name);
  126. // change uid/gid to drop privileges
  127. retval = setgid(group_temp->gr_gid);
  128. if (retval == -1) {
  129. ERRORV("setgid() (errno: %d - %s)\n", errno, strerror(errno));
  130. exit(EXIT_FAILURE);
  131. }
  132. DEBUG("setting uid to %d (\"%s\")\n",
  133. passwd_temp->pw_uid, passwd_temp->pw_name);
  134. retval = setuid(passwd_temp->pw_uid);
  135. if (retval == -1) {
  136. ERRORV("setuid() (errno: %d - %s)\n", errno, strerror(errno));
  137. exit(EXIT_FAILURE);
  138. }
  139. INFO("uid/gid change successful\n");
  140. }
  141. int
  142. load_plugins(char *masterserver_plugin_dir, void ***handle)
  143. {
  144. int retval = 0;
  145. int num_plugins = 0;
  146. DIR *plugin_dir; // for opening the plugin dir
  147. struct dirent *plugin_dir_entry;
  148. char path[PATH_MAX]; // path to plugin dir
  149. // open plugin directory
  150. DEBUG("opening %s\n", masterserver_plugin_dir);
  151. plugin_dir = opendir(masterserver_plugin_dir);
  152. if (plugin_dir == NULL) {
  153. ERRORV("opendir(%s) (errno: %d - %s)\n", masterserver_plugin_dir, errno, strerror(errno));
  154. return -1;
  155. }
  156. // load all plugins in masterserver_plugin_dir
  157. while ((plugin_dir_entry = readdir(plugin_dir))) {
  158. // omit ., .. and files non-.so suffix
  159. if ((strcmp(plugin_dir_entry->d_name, ".") == 0)
  160. || (strcmp(plugin_dir_entry->d_name, "..") == 0)
  161. || (strcmp(plugin_dir_entry->d_name+strlen(plugin_dir_entry->d_name)-3, ".so") != 0))
  162. continue;
  163. snprintf(path,
  164. strlen(masterserver_plugin_dir)+plugin_dir_entry->d_reclen+2,
  165. "%s/%s", masterserver_plugin_dir, plugin_dir_entry->d_name);
  166. DEBUG("path: \"%s\"\n", path);
  167. // allocate memory for the new handle
  168. *handle = realloc(*handle, (num_plugins+1)*sizeof(void*));
  169. if (*handle == NULL) {
  170. ERRORV("realloc() failed trying to get %d bytes!\n",
  171. (num_plugins+1)*sizeof(void *));
  172. return -1;
  173. }
  174. (*handle)[num_plugins] = dlopen(path, RTLD_NOW);
  175. if ((*handle)[num_plugins] == NULL)
  176. {
  177. ERRORV("dlopen (%s)\n", dlerror());
  178. return -1;
  179. }
  180. DEBUG("dlopen() successful (0x%x)\n", (*handle)[num_plugins]);
  181. INFO("%s loaded\n", plugin_dir_entry->d_name);
  182. num_plugins++;
  183. }
  184. retval = closedir(plugin_dir);
  185. if (retval == -1)
  186. {
  187. ERRORV("closedir(%s) (errno: %d - %s)\n", plugin_dir, errno, strerror(errno));
  188. return -1;
  189. }
  190. DEBUG("closedir succeeded\n");
  191. return num_plugins;
  192. }
  193. extern void
  194. register_plugin(struct masterserver_plugin *me)
  195. {
  196. struct masterserver_plugin **i;
  197. if (strcmp(me->cversion, masterserver_version) != 0) {
  198. WARNING("plugin %s was compiled for masterserver version %s (this is %s)\n", me->name, me->cversion, masterserver_version);
  199. WARNING("plugin %s disabled\n", me->name);
  200. me->enabled = 0;
  201. } else {
  202. me->enabled = 1; // plugin is enabled
  203. }
  204. // append to linked list
  205. for (i = &plugins; *i; i = &(*i)->next);
  206. me->next = NULL;
  207. *i = me;
  208. // initialize plugin structure
  209. // me->mutex = PTHREAD_MUTEX_INITIALIZER;
  210. pthread_mutex_init(&me->mutex, NULL);
  211. me->num_servers = 0;
  212. me->list = calloc(1, sizeof(serverlist_t)); // initialize server list
  213. if (me->list == NULL) {
  214. ERRORV("calloc() failed to get %d bytes!\n", sizeof(serverlist_t));
  215. exit(EXIT_FAILURE);
  216. }
  217. me->num_sockets = 0;
  218. me->socket_d = NULL;
  219. me->server = calloc(me->num_ports, sizeof(struct sockaddr_in));
  220. if (me->server == NULL) {
  221. ERRORV("calloc() failed trying to get %d bytes!\n", me->num_ports*sizeof(struct sockaddr_in));
  222. exit(EXIT_FAILURE);
  223. }
  224. me->msg_out = NULL;
  225. me->msg_out_length = NULL;
  226. me->info(); // display plugin info
  227. }
  228. int
  229. main(int argc, char *argv[])
  230. {
  231. // cmdline options
  232. int option_logfile = 0;
  233. int option_bind_to_interface = 0;
  234. int option_daemon = 0;
  235. int option_plugin_dir = 0;
  236. int option_change_user_and_group = 0;
  237. char *user = NULL;
  238. char *group = NULL;
  239. int i, k, l, num_plugins;
  240. void **handle = NULL; // for dlopen() calls
  241. int retval; // return value of syscalls
  242. unsigned int num_plugins_enabled, num_listen_interfaces = 0;
  243. char *logfile; // pointer to argv argument
  244. char *masterserver_plugin_dir; // pointer to argv argument
  245. char **listen_interface = NULL; // ptr array for storing interface/device names
  246. struct masterserver_plugin **j; // temporary variable
  247. // temporary variables
  248. int setsockopt_temp = 1;
  249. pid_t temp_pid;
  250. // seed the rng; needed for challenge creation in q3 plugin
  251. srand(time(NULL));
  252. log_init(NULL, "masterserver");
  253. INFO("masterserver v%s\n", masterserver_version);
  254. // TODO: read config
  255. // cmdline parser
  256. while (1) {
  257. //retval = getopt(argc, argv, "?dDhi:l:L:p:V");
  258. retval = getopt(argc, argv, "?dDg:hi:l:p:u:V");
  259. if (retval == -1) break;
  260. switch (retval) {
  261. // debug
  262. case 'd':
  263. debug = 1;
  264. break;
  265. // daemon mode
  266. case 'D':
  267. option_daemon = 1;
  268. break;
  269. // run masterserver under a certain group
  270. case 'g':
  271. group = argv[optind-1];
  272. break;
  273. // bind to interface
  274. case 'i':
  275. if (getuid() != 0) {
  276. ERRORV("you have to be root to bind to specific interfaces\n");
  277. exit(EXIT_FAILURE);
  278. }
  279. if (strlen(argv[optind-1]) > IFNAMSIZ) {
  280. ERRORV("interface/device name is longer than IFNAMSIZ = %d"
  281. " chars\n", IFNAMSIZ);
  282. exit(EXIT_FAILURE);
  283. }
  284. num_listen_interfaces++;
  285. listen_interface = realloc(listen_interface, num_listen_interfaces*sizeof(char *));
  286. listen_interface[num_listen_interfaces-1] = argv[optind-1];
  287. option_bind_to_interface = 1;
  288. break;
  289. // log messages to a file
  290. case 'l':
  291. option_logfile = 1;
  292. logfile = argv[optind-1];
  293. break;
  294. /*case 'L':
  295. _log_level = atoi(argv[optind-1]);
  296. if ((_log_level < 0) || (_log_level > 2)) {
  297. ERROR("log level must be 0 <= x <= 2\n");
  298. return -1;
  299. }
  300. break;*/
  301. // plugin path
  302. case 'p':
  303. masterserver_plugin_dir = argv[optind-1];
  304. option_plugin_dir = 1;
  305. break;
  306. // run masterserver as a certain user
  307. case 'u':
  308. if (getuid() != 0) {
  309. ERRORV("you have to be root to change user/group\n");
  310. exit(EXIT_FAILURE);
  311. }
  312. user = argv[optind-1];
  313. option_change_user_and_group = 1;
  314. break;
  315. // version information
  316. case 'V':
  317. exit_printversion();
  318. // help
  319. case 'h':
  320. case '?':
  321. default:
  322. exit_printhelp();
  323. return EXIT_FAILURE;
  324. } // switch(retval)
  325. } // while(1)
  326. if ((group != NULL) && !option_change_user_and_group) {
  327. ERROR("-g can only be used together with -u\n");
  328. return EXIT_FAILURE;
  329. }
  330. // XXX: this is a hack to get multi port working
  331. if ((num_listen_interfaces == 0) && !option_bind_to_interface) num_listen_interfaces = 1;
  332. // check -l cmdline argument
  333. if (option_logfile) {
  334. INFO("masterserver: logging stdout to %s\n", logfile);
  335. // initialize log file
  336. retval = log_init(logfile, "masterserver");
  337. if (retval == -1) {
  338. ERROR("log_init()\n");
  339. return EXIT_FAILURE;
  340. }
  341. // log stdout to log file
  342. /*if (freopen(logfile, "a", stdout) != stdout) {
  343. ERRORV("freopen() (errno: %d - %s)\n", errno, strerror(errno));
  344. return -1;
  345. }*/
  346. // change buffering to per line so we actually see something in the logfile
  347. setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
  348. }
  349. // check -D cmdline argument
  350. if (option_daemon) {
  351. INFO("masterserver: becoming a daemon ... bye, bye\n");
  352. if ( (temp_pid = fork()) < 0) {
  353. ERRORV("fork() (errno: %d - %s)\n", errno, strerror(errno));
  354. return -1;
  355. } else if (temp_pid != 0) {
  356. exit(EXIT_SUCCESS);
  357. }
  358. retval = setsid();
  359. if (retval == -1) {
  360. ERRORV("setsid() (errno: %d - %s)\n", errno, strerror(errno));
  361. exit(EXIT_FAILURE);
  362. }
  363. retval = chdir("/");
  364. if (retval == -1) {
  365. ERRORV("chdir() (errno: %d - %s)\n", errno, strerror(errno));
  366. exit(EXIT_FAILURE);
  367. }
  368. umask(0);
  369. if (option_logfile == 0) {
  370. if (freopen("/dev/null", "a", stdout) != stdout) {
  371. ERRORV("freopen() (errno: %d - %s)\n", errno, strerror(errno));
  372. return EXIT_FAILURE;
  373. }
  374. }
  375. }
  376. // check if user specified an alternative plugin dir
  377. // if he did well we already set it above
  378. // else we set the default here
  379. if (!option_plugin_dir)
  380. masterserver_plugin_dir = MASTERSERVER_LIB_DIR;
  381. // register signal handler
  382. signal(SIGINT, &sigint_handler);
  383. // load all libs in plugin_dir
  384. num_plugins = load_plugins(masterserver_plugin_dir, &handle);
  385. if (num_plugins <= 0) {
  386. ERRORV("no plugins found in \"%s\"\n", masterserver_plugin_dir);
  387. return EXIT_FAILURE;
  388. }
  389. // print out a summary
  390. INFO("%d plugins loaded\n", num_plugins);
  391. // create sockets and bind them
  392. // had to be done because threads inherit the original user
  393. // and we don't want the threads to be root
  394. // TODO: sanity checks (e.g. duplicate ports)
  395. // TODO: check for plugin protocol
  396. j = &plugins;
  397. DEBUG("going to listen on %d interfaces ...\n", num_listen_interfaces);
  398. for (i = 0; i < num_plugins; i++) {
  399. if (*j == NULL) break;
  400. if ((*j)->enabled == 0) {
  401. WARNING("plugin nr %d %s disabled\n", i, (*j)->name);
  402. continue;
  403. }
  404. // create socket(s) for plugin
  405. for (k = 0; k < (*j)->num_ports; k++) {
  406. // fill sockaddr_in structure
  407. (*j)->server[k].sin_family = AF_INET;
  408. (*j)->server[k].sin_port = htons((*j)->port[k].num); // port number from plugin
  409. (*j)->server[k].sin_addr.s_addr = htonl(INADDR_ANY);
  410. for (l = 0; l < num_listen_interfaces; l++, (*j)->num_sockets++) {
  411. (*j)->socket_d = realloc((*j)->socket_d, ((*j)->num_sockets+1)*sizeof(int));
  412. if ((*j)->socket_d == NULL) {
  413. ERRORV("realloc() failed trying to get %d bytes\n",
  414. (*j)->num_sockets+1*sizeof(int));
  415. return EXIT_FAILURE;
  416. }
  417. (*j)->socket_d[(*j)->num_sockets] = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  418. DEBUG("plugin #%d %s | socket_d[%d] is %d\n", i, (*j)->name,
  419. (*j)->num_sockets, (*j)->socket_d[(*j)->num_sockets]);
  420. // receive broadcast packets
  421. retval = setsockopt((*j)->socket_d[(*j)->num_sockets], SOL_SOCKET,
  422. SO_BROADCAST, &setsockopt_temp, sizeof(setsockopt_temp));
  423. if (retval == -1) {
  424. ERRORV("setsockopt() (errno: %d - %s)\n", errno,
  425. strerror(errno));
  426. return EXIT_FAILURE;
  427. }
  428. // bind socket to the interfaces specified in -i
  429. if (option_bind_to_interface) {
  430. #ifdef __linux__
  431. DEBUG("setsockopt(..., \"%s\", %d+1);\n",
  432. listen_interface[l], strlen(listen_interface[l]));
  433. retval = setsockopt((*j)->socket_d[(*j)->num_sockets],
  434. SOL_SOCKET, SO_BINDTODEVICE, listen_interface[l],
  435. strlen(listen_interface[l])+1);
  436. if (retval == -1) {
  437. ERRORV("setsockopt() (errno: %d - %s)\n", errno,
  438. strerror(errno));
  439. return EXIT_FAILURE;
  440. }
  441. DEBUG("%s socket #%d successfully bound to %s\n",
  442. (*j)->name, (*j)->num_sockets, listen_interface[l]);
  443. INFO("listening on %s UDP port %d\n", listen_interface[l],
  444. (*j)->port[k].num);
  445. #endif
  446. } else INFO("listening on UDP port %d\n", (*j)->port[k].num);
  447. // bind socket to structure
  448. retval = bind((*j)->socket_d[(*j)->num_sockets],
  449. (struct sockaddr *) &(*j)->server[k],
  450. sizeof(struct sockaddr_in));
  451. if (retval == -1) {
  452. ERRORV("bind() (errno: %d - %s)\n", errno, strerror(errno));
  453. return EXIT_FAILURE;
  454. }
  455. }
  456. }
  457. j = &(*j)->next;
  458. }
  459. DEBUG("sockets successfully created and bound\n");
  460. if (option_bind_to_interface
  461. || option_change_user_and_group)
  462. {
  463. change_user_and_group_to(user, group);
  464. }
  465. // main part
  466. DEBUG("creating plugin threads...\n");
  467. j = &plugins;
  468. for (i = 0; i < num_plugins; i++) {
  469. if (*j == NULL) break;
  470. if ((*j)->enabled == 0) continue;
  471. // create plugin thread
  472. retval = pthread_create(&(*j)->thread_nr, NULL, (void *) plugin_thread, (void *) *j);
  473. if (retval != 0) {
  474. switch(retval) {
  475. case EAGAIN:
  476. ERROR("pthread_create returned an error; not enough system"
  477. " resources to create a process for the new thread\n");
  478. ERRORV("or more than max posix threads are already active\n");
  479. return EXIT_FAILURE;
  480. }
  481. }
  482. INFO("created %s plugin thread\n", (*j)->name);
  483. j = &(*j)->next; // point j to next plugin in linked list
  484. }
  485. // create heartbeat threads
  486. DEBUG("creating heartbeat threads...\n");
  487. j = &plugins;
  488. for (i = 0; i < num_plugins; i++) {
  489. if (*j == NULL) break;
  490. if ((*j)->enabled == 0) continue;
  491. retval = pthread_create(&(*j)->heartbeat_thread_nr, NULL,
  492. (void *) plugin_heartbeat_thread, (void *) *j);
  493. if (retval != 0) {
  494. switch(retval) {
  495. case EAGAIN:
  496. ERROR("pthread_create returned an error; not enough system"
  497. " resources to create a process for the new thread\n");
  498. ERRORV("or more than max posix threads are already active\n");
  499. return EXIT_FAILURE;
  500. }
  501. }
  502. INFO("created heartbeat thread for %s\n", (*j)->name);
  503. j = &(*j)->next;
  504. }
  505. // admin interface
  506. // TODO
  507. // cleanup and exit
  508. // (not really; this is just to stop the parent from eating cpu time)
  509. // XXX: paranoid cleanup ?
  510. // check if pointers are != NULL
  511. // destroy mutexes
  512. // free private data
  513. INFO("joining plugin threads for graceful cleanup/shutdown... \n");
  514. for (j = &plugins; *j; j = &(*j)->next) {
  515. DEBUG("joining %s thread (#%ld)\n", (*j)->name, (*j)->thread_nr);
  516. retval = pthread_join((*j)->thread_nr, NULL);
  517. if (retval != 0) {
  518. ERROR("pthread_join()\n");
  519. return EXIT_FAILURE;
  520. }
  521. DEBUG("joining %s heartbeat thread (#%ld)\n", (*j)->name, (*j)->heartbeat_thread_nr);
  522. retval = pthread_join((*j)->heartbeat_thread_nr, NULL);
  523. if (retval != 0) {
  524. ERROR("pthread_join()\n");
  525. return EXIT_FAILURE;
  526. }
  527. DEBUG("thread #%ld exited; calling plugin cleanup() function\n", (*j)->heartbeat_thread_nr);
  528. // free private data
  529. // to really free all private data we have to call a cleanup function
  530. // of the plugin
  531. if ((*j)->cleanup != NULL) (*j)->cleanup();
  532. // free server list
  533. free((*j)->list);
  534. for (i = 0; i < (*j)->num_sockets; i++) close((*j)->socket_d[i]);
  535. for (i = 0; i < (*j)->num_msgs; i++) free((*j)->msg_out[i]);
  536. free((*j)->msg_out);
  537. free((*j)->msg_out_length);
  538. DEBUG("%s clean up successful\n", (*j)->name);
  539. }
  540. DEBUG("closing dynamic libs ...\n");
  541. INFO("unload plugins\n");
  542. while (num_plugins-- > 0) {
  543. DEBUG("closing dynamic lib %d (0x%x)\n", num_plugins, handle[num_plugins]);
  544. dlclose(handle[num_plugins]);
  545. }
  546. DEBUG("dynamic libs successfully closed\n");
  547. log_close();
  548. return EXIT_SUCCESS;
  549. }
  550. void
  551. plugin_thread(void *arg)
  552. {
  553. int retval; // temp var for return values
  554. int i, j, packetlen;
  555. char msg_in[MAX_PKT_LEN]; // buffer for incoming packet
  556. struct masterserver_plugin *me = (struct masterserver_plugin *) arg;
  557. unsigned int client_len = sizeof(me->client);
  558. int n = 0; // for select()
  559. fd_set rfds;
  560. DEBUG("%s_thread: hello world\n", me->name);
  561. // initialize msg_in buffer
  562. memset(msg_in, 0, MAX_PKT_LEN);
  563. // main loop
  564. while (!master_shutdown) {
  565. FD_ZERO(&rfds);
  566. for (i = 0; i < me->num_sockets; i++) {
  567. if (me->socket_d[i] > n) n = me->socket_d[i];
  568. FD_SET(me->socket_d[i], &rfds);
  569. }
  570. retval = select(n+1, &rfds, NULL, NULL, NULL);
  571. if (retval == -1) {
  572. if (errno == EINTR) continue;
  573. ERRORV("%s_thread: select() (errno: %d - %s)\n", me->name, errno,
  574. strerror(errno));
  575. pthread_exit((void *) -1);
  576. }
  577. for (i = 0; i < me->num_sockets; i++) {
  578. if (FD_ISSET(me->socket_d[i], &rfds)) {
  579. packetlen = recvfrom(me->socket_d[i], &msg_in, MAX_PKT_LEN-1, 0,
  580. (struct sockaddr *) &me->client, &client_len);
  581. if (packetlen == -1) {
  582. ERRORV("%s_thread: recvfrom() (errno: %d - %s)\n", me->name,
  583. errno, strerror(errno));
  584. ERRORV("%s_thread: socket_d is %d\n", me->name,
  585. me->socket_d[i]);
  586. ERRORV("%s_thread: MAX_PKT_LEN is %d\n",
  587. me->name, MAX_PKT_LEN);
  588. pthread_exit((void *) -1);
  589. }
  590. DEBUG("%d bytes received\n", packetlen);
  591. DEBUG("locking mutex\n");
  592. retval = pthread_mutex_lock(&me->mutex);
  593. if (retval != 0) {
  594. ERRORV("%s_thread: pthread_mutex_lock() (retval: %d)\n",
  595. me->name, retval);
  596. pthread_exit((void *) -1);
  597. }
  598. DEBUG("mutex succesfully locked\n");
  599. retval = me->process(msg_in, packetlen);
  600. if (retval == -2) {
  601. ERRORV("%s_thread: plugin reported: out of memory\n", me->name);
  602. // TODO: cleanup?
  603. pthread_exit((void *) -1);
  604. } else if (retval == -1) {
  605. //WARNING("%s_thread: plugin reported: invalid packet received\n", me->name);
  606. } else if (retval == 0) {
  607. //INFO("%s_thread: plugin reported: server successfully added\n", me->name);
  608. } else if (retval == 1) {
  609. DEBUG("sending %d packets to %s:%u\n",
  610. me->num_msgs, inet_ntoa(me->client.sin_addr),
  611. ntohs(me->client.sin_port));
  612. for (j = 0; j < me->num_msgs; j++) {
  613. retval = sendto(me->socket_d[i], me->msg_out[j],
  614. me->msg_out_length[j], 0,
  615. (struct sockaddr *) &me->client, client_len);
  616. if (retval == -1) {
  617. ERRORV("sendto() (errno: %d - %s)\n",
  618. errno, strerror(errno));
  619. } else DEBUG("%d bytes sent\n", retval);
  620. }
  621. } else if (retval == 2) {
  622. // INFO("%s_thread: plugin reported: server deleted\n", me->name);
  623. }
  624. // clean up
  625. memset(msg_in, 0, MAX_PKT_LEN);
  626. if (me->num_msgs > 0) {
  627. DEBUG("freeing outgoing packets\n");
  628. for (j = 0; j < me->num_msgs; j++)
  629. free(me->msg_out[j]);
  630. me->num_msgs = 0;
  631. free(me->msg_out);
  632. free(me->msg_out_length);
  633. me->msg_out = NULL;
  634. me->msg_out_length = NULL;
  635. }
  636. DEBUG("unlocking mutex\n");
  637. retval = pthread_mutex_unlock(&me->mutex);
  638. if (retval != 0) {
  639. ERROR("pthread_mutex_unlock()\n");
  640. pthread_exit((void *) -1);
  641. }
  642. } // if(FD_ISSET())
  643. } // for(i)
  644. } // while()
  645. }
  646. void
  647. plugin_heartbeat_thread(void *arg)
  648. {
  649. struct masterserver_plugin *me = (struct masterserver_plugin *) arg;
  650. int i = 0;
  651. int heartbeat_diff = 0;
  652. int retval; // temp var for return values
  653. DEBUG("%s_heartbeat_thread: hello world\n", me->name);
  654. // main loop
  655. while (!master_shutdown) {
  656. DEBUG("sleeping %d seconds ...\n", me->heartbeat_timeout);
  657. sleep(me->heartbeat_timeout);
  658. DEBUG("waking up\n");
  659. DEBUG("locking plugin mutex\n");
  660. retval = pthread_mutex_lock(&me->mutex);
  661. if (retval != 0) {
  662. ERROR("pthread_mutex_lock()\n");
  663. pthread_exit((void *) -1);
  664. }
  665. for (i = 0; i < me->num_servers; i++) {
  666. heartbeat_diff = time(NULL) - me->list[i].lastheartbeat;
  667. if (heartbeat_diff > 300) {
  668. INFO("%s_heartbeat_thread: server %s:%d died (heartbeat_diff %d)\n",
  669. me->name, inet_ntoa(me->list[i].ip), ntohs(me->list[i].port), heartbeat_diff);
  670. delete_server(me, i);
  671. i--;
  672. } else {
  673. DEBUG("server %s:%d is alive (heartbeat_diff %d)\n",
  674. inet_ntoa(me->list[i].ip), ntohs(me->list[i].port),
  675. heartbeat_diff);
  676. }
  677. }
  678. DEBUG("unlocking mutex\n");
  679. retval = pthread_mutex_unlock(&me->mutex);
  680. if (retval != 0) {
  681. ERROR("pthread_mutex_unlock\n");
  682. pthread_exit((void *) -1);
  683. }
  684. } // while()
  685. }
  686. extern void
  687. delete_server(struct masterserver_plugin *me, int server_num)
  688. {
  689. if (me->free_privdata != NULL) me->free_privdata(me->list[server_num].private_data);
  690. me->num_servers--;
  691. me->list[server_num] = me->list[me->num_servers];
  692. DEBUG("reallocating server list (old size: %d -> new size: %d)\n",
  693. (me->num_servers+2)*sizeof(serverlist_t),
  694. (me->num_servers+1)*sizeof(serverlist_t));
  695. me->list = (serverlist_t *) realloc(me->list, (me->num_servers+1)*sizeof(serverlist_t));
  696. if (me->list == NULL) {
  697. ERROR("(__)\n");
  698. ERROR(" °°\\\\\\~\n");
  699. ERROR(" !!!!\n");
  700. pthread_exit((void *) -1);
  701. }
  702. DEBUG("reallocation successful\n");
  703. }
  704. void
  705. sigint_handler(int signum)
  706. {
  707. DEBUG("caught SIGINT!\n");
  708. master_shutdown = 1;
  709. }