Crossfire Server, Branches 1.12  R18729
metaserver.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_metaserver_c =
3  * "$Id: metaserver.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2002,2007 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
35 #include <global.h>
36 
37 #ifndef WIN32 /* ---win32 exclude unix header files */
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netdb.h>
42 #include <arpa/inet.h>
43 
44 #endif /* end win32 */
45 
46 #include <pthread.h>
47 #include <metaserver2.h>
48 #include <version.h>
49 
50 #ifdef HAVE_CURL_CURL_H
51 #include <curl/curl.h>
52 #include <curl/types.h>
53 #include <curl/easy.h>
54 #endif
55 
56 static int metafd = -1;
57 
58 static struct sockaddr_in sock;
59 
68 void metaserver_init(void) {
69 #ifdef WIN32 /* ***win32 metaserver_init(): init win32 socket */
70  struct hostent *hostbn;
71  int temp = 1;
72 #endif
73 
74  if (!settings.meta_on) {
75  metafd = -1;
76  return;
77  }
78 
79  if (isdigit(settings.meta_server[0]))
80  sock.sin_addr.s_addr = inet_addr(settings.meta_server);
81  else {
82  struct hostent *hostbn = gethostbyname(settings.meta_server);
83 
84  if (hostbn == NULL) {
85  LOG(llevDebug, "metaserver_init: Unable to resolve hostname %s\n", settings.meta_server);
86  return;
87  }
88  memcpy(&sock.sin_addr, hostbn->h_addr, hostbn->h_length);
89  }
90 #ifdef WIN32 /* ***win32 metaserver_init(): init win32 socket */
91  ioctlsocket(metafd, FIONBIO , &temp);
92 #else
93  fcntl(metafd, F_SETFL, O_NONBLOCK);
94 #endif
95  if ((metafd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
96  LOG(llevDebug, "metaserver_init: Unable to create socket, err %d\n", errno);
97  return;
98  }
99  sock.sin_family = AF_INET;
100  sock.sin_port = htons(settings.meta_port);
101 
102  /* No hostname specified, so lets try to figure one out */
103  if (settings.meta_host[0] == 0) {
104  char hostname[MAX_BUF], domain[MAX_BUF];
105 
106  if (gethostname(hostname, MAX_BUF-1)) {
107  LOG(llevDebug, "metaserver_init: gethostname failed - will not report hostname\n");
108  return;
109  }
110 
111 #ifdef WIN32 /* ***win32 metaserver_init(): gethostbyname! */
112  hostbn = gethostbyname(hostname);
113  if (hostbn != (struct hostent *)NULL) /* quick hack */
114  memcpy(domain, hostbn->h_addr, hostbn->h_length);
115 
116  if (hostbn == (struct hostent *)NULL) {
117 #else
118  if (getdomainname(domain, MAX_BUF-1)) {
119 #endif /* win32 */
120  LOG(llevDebug, "metaserver_init: getdomainname failed - will not report hostname\n");
121  return;
122  }
123  /* Potential overrun here but unlikely to occur */
124  sprintf(settings.meta_host, "%s.%s", hostname, domain);
125  }
126 }
127 
134 void metaserver_update(void) {
135  char data[MAX_BUF], num_players = 0;
136  player *pl;
137 
138  /* We could use socket_info.nconns, but that is not quite as accurate,
139  * as connections in the progress of being established, are listening
140  * but don't have a player, etc. The checks below are basically the
141  * same as for the who commands with the addition that WIZ, AFK, and BOT
142  * players are not counted.
143  */
144  for (pl = first_player; pl != NULL; pl = pl->next) {
145  if (pl->ob->map == NULL)
146  continue;
147  if (pl->hidden)
148  continue;
149  if (QUERY_FLAG(pl->ob, FLAG_WIZ))
150  continue;
151  if (QUERY_FLAG(pl->ob, FLAG_AFK))
152  continue;
153  if (pl->state != ST_PLAYING && pl->state != ST_GET_PARTY_PASSWORD)
154  continue;
155  if (pl->socket.is_bot)
156  continue;
157  num_players++;
158  }
159 
160  /* Only do this if we have a valid connection */
161  if (metafd != -1) {
162  snprintf(data, sizeof(data), "%s|%d|%s|%s|%d|%d|%ld", settings.meta_host, num_players,
163  FULL_VERSION,
165  (long)time(NULL)-cst_tot.time_start);
166  if (sendto(metafd, data, strlen(data), 0, (struct sockaddr *)&sock, sizeof(sock)) < 0) {
167  LOG(llevDebug, "metaserver_update: sendto failed, err = %d\n", errno);
168  }
169  }
170 
171  /* Everything inside the pthread lock/unlock is related
172  * to metaserver2 synchronization.
173  */
174  pthread_mutex_lock(&ms2_info_mutex);
175  metaserver2_updateinfo.num_players = num_players;
178  metaserver2_updateinfo.uptime = (long)time(NULL)-cst_tot.time_start;
179  pthread_mutex_unlock(&ms2_info_mutex);
180 
181 }
182 
196 typedef struct _MetaServer2 {
197  char *hostname;
199 } MetaServer2;
200 
202 
211 typedef struct _LocalMeta2Info {
212  int notification; /* if true, do updates to metaservers */
213  char *hostname; /* Hostname of this server */
214  int portnumber; /* Portnumber of this server */
215  char *html_comment; /* html comment to send to metaservers */
216  char *text_comment; /* text comment to send to metaservers */
217  char *archbase; /* Different sources for arches, maps */
218  char *mapbase; /* and server */
219  char *codebase;
220  char *flags; /* Short flags to send to metaserver */
222 
224 
225 /* These two are globals, but we declare them here. */
226 pthread_mutex_t ms2_info_mutex;
227 
229 
236 static void free_metaserver2(MetaServer2 *ms) {
237  free(ms->hostname);
238  free(ms);
239 }
240 
255 int metaserver2_init(void) {
256  static int has_init = 0;
257  FILE *fp;
258  char buf[MAX_BUF], *cp;
259  MetaServer2 *ms2, *msnext;
260  int comp;
261  pthread_t thread_id;
262 
263 #ifdef HAVE_CURL_CURL_H
264  if (!has_init) {
265  memset(&local_info, 0, sizeof(LocalMeta2Info));
266  memset(&metaserver2_updateinfo, 0, sizeof(MetaServer2_UpdateInfo));
267 
268  local_info.portnumber = settings.csport;
269  metaserver2 = NULL;
270  pthread_mutex_init(&ms2_info_mutex, NULL);
271  curl_global_init(CURL_GLOBAL_ALL);
272  } else {
273  local_info.notification = 0;
274  if (local_info.hostname)
275  FREE_AND_CLEAR(local_info.hostname);
276  if (local_info.html_comment)
277  FREE_AND_CLEAR(local_info.html_comment);
278  if (local_info.text_comment)
279  FREE_AND_CLEAR(local_info.text_comment);
280  if (local_info.archbase)
281  FREE_AND_CLEAR(local_info.archbase);
282  if (local_info.mapbase)
283  FREE_AND_CLEAR(local_info.mapbase);
284  if (local_info.codebase)
285  FREE_AND_CLEAR(local_info.codebase);
286  if (local_info.flags)
287  FREE_AND_CLEAR(local_info.flags);
288  for (ms2 = metaserver2; ms2; ms2 = msnext) {
289  msnext = ms2->next;
290  free_metaserver2(ms2);
291  }
292  metaserver2 = NULL;
293  }
294 #endif
295 
296  /* Now load up the values from the file */
297  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
298 
299  if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
300  LOG(llevError, "Warning: No metaserver2 file found\n");
301  return 0;
302  }
303  while (fgets(buf, MAX_BUF-1, fp) != NULL) {
304  if (buf[0] == '#')
305  continue;
306  /* eliminate newline */
307  if ((cp = strrchr(buf, '\n')) != NULL)
308  *cp = '\0';
309 
310  /* Skip over empty lines */
311  if (buf[0] == 0)
312  continue;
313 
314  /* Find variable pairs */
315 
316  if ((cp = strpbrk(buf, " \t")) != NULL) {
317  while (isspace(*cp))
318  *cp++ = 0;
319  } else {
320  /* This makes it so we don't have to do NULL checks against
321  * cp everyplace
322  */
323  cp = "";
324  }
325 
326  if (!strcasecmp(buf, "metaserver2_notification")) {
327  if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
328  local_info.notification = TRUE;
329  } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
330  local_info.notification = FALSE;
331  } else {
332  LOG(llevError, "metaserver2: Unknown value for metaserver2_notification: %s\n", cp);
333  }
334  } else if (!strcasecmp(buf, "metaserver2_server")) {
335  if (*cp != 0) {
336  ms2 = calloc(1, sizeof(MetaServer2));
337  ms2->hostname = strdup(cp);
338  ms2->next = metaserver2;
339  metaserver2 = ms2;
340  } else {
341  LOG(llevError, "metaserver2: metaserver2_server must have a value.\n");
342  }
343  } else if (!strcasecmp(buf, "localhostname")) {
344  if (*cp != 0) {
345  local_info.hostname = strdup_local(cp);
346  } else {
347  LOG(llevError, "metaserver2: localhostname must have a value.\n");
348  }
349  } else if (!strcasecmp(buf, "portnumber")) {
350  if (*cp != 0) {
351  local_info.portnumber = atoi(cp);
352  } else {
353  LOG(llevError, "metaserver2: portnumber must have a value.\n");
354  }
355  /* For the following values, it is easier to make sure
356  * the pointers are set to something, even if it is a blank
357  * string, so don't care if there is data in the string or not.
358  */
359  } else if (!strcasecmp(buf, "html_comment")) {
360  local_info.html_comment = strdup(cp);
361  } else if (!strcasecmp(buf, "text_comment")) {
362  local_info.text_comment = strdup(cp);
363  } else if (!strcasecmp(buf, "archbase")) {
364  local_info.archbase = strdup(cp);
365  } else if (!strcasecmp(buf, "mapbase")) {
366  local_info.mapbase = strdup(cp);
367  } else if (!strcasecmp(buf, "codebase")) {
368  local_info.codebase = strdup(cp);
369  } else if (!strcasecmp(buf, "flags")) {
370  local_info.flags = strdup(cp);
371  } else {
372  LOG(llevError, "Unknown value in metaserver2 file: %s\n", buf);
373  }
374  }
375  close_and_delete(fp, comp);
376 
377  /* If no hostname is set, can't do updates */
378  if (!local_info.hostname)
379  local_info.notification = 0;
380 
381 #ifndef HAVE_CURL_CURL_H
382  if (local_info.notification) {
383  LOG(llevError, "metaserver2 file is set to do notification, but libcurl is not found.\n");
384  LOG(llevError, "Either fix your compilation, or turn of metaserver2 notification in \n");
385  LOG(llevError, "the %s/metaserver2 file.\n", settings.confdir);
386  LOG(llevError, "Exiting program.\n");
387  exit(1);
388  }
389 #endif
390 
391  if (local_info.notification) {
392  /* As noted above, it is much easier for the rest of the code
393  * to not have to check for null pointers. So we do that
394  * here, and anything that is null, we just allocate
395  * an empty string.
396  */
397  if (!local_info.html_comment)
398  local_info.html_comment = strdup("");
399  if (!local_info.text_comment)
400  local_info.text_comment = strdup("");
401  if (!local_info.archbase)
402  local_info.archbase = strdup("");
403  if (!local_info.mapbase)
404  local_info.mapbase = strdup("");
405  if (!local_info.codebase)
406  local_info.codebase = strdup("");
407  if (!local_info.flags)
408  local_info.flags = strdup("");
409 
410  comp = pthread_create(&thread_id, NULL, metaserver2_thread, NULL);
411  if (comp) {
412  LOG(llevError, "metaserver2_init: return code from pthread_create() is %d\n", comp);
413 
414  /* Effectively true - we're not going to update the metaserver */
415  local_info.notification = 0;
416  }
417  }
418  return local_info.notification;
419 }
420 
427 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
428  size_t realsize = size*nmemb;
429 
430  LOG(llevDebug, "metaserver2_writer- Start of text:\n%s\n", (const char*)ptr);
431  LOG(llevDebug, "metaserver2_writer- End of text:\n");
432 
433  return realsize;
434 }
435 
436 
442 static void metaserver2_updates(void) {
443 #ifdef HAVE_CURL_CURL_H
444  MetaServer2 *ms2;
445  struct curl_httppost *formpost = NULL;
446  struct curl_httppost *lastptr = NULL;
447  char buf[MAX_BUF];
448 
449  /* First, fill in the form - note that everything has to be a string,
450  * so we convert as needed with snprintf.
451  * The order of fields here really isn't important.
452  * The string after CURLFORM_COPYNAME is the name of the POST variable
453  * as the
454  */
455  curl_formadd(&formpost, &lastptr,
456  CURLFORM_COPYNAME, "hostname",
457  CURLFORM_COPYCONTENTS, local_info.hostname,
458  CURLFORM_END);
459 
460  snprintf(buf, MAX_BUF-1, "%d", local_info.portnumber);
461  curl_formadd(&formpost, &lastptr,
462  CURLFORM_COPYNAME, "port",
463  CURLFORM_COPYCONTENTS, buf,
464  CURLFORM_END);
465 
466  curl_formadd(&formpost, &lastptr,
467  CURLFORM_COPYNAME, "html_comment",
468  CURLFORM_COPYCONTENTS, local_info.html_comment,
469  CURLFORM_END);
470 
471  curl_formadd(&formpost, &lastptr,
472  CURLFORM_COPYNAME, "text_comment",
473  CURLFORM_COPYCONTENTS, local_info.text_comment,
474  CURLFORM_END);
475 
476  curl_formadd(&formpost, &lastptr,
477  CURLFORM_COPYNAME, "archbase",
478  CURLFORM_COPYCONTENTS, local_info.archbase,
479  CURLFORM_END);
480 
481  curl_formadd(&formpost, &lastptr,
482  CURLFORM_COPYNAME, "mapbase",
483  CURLFORM_COPYCONTENTS, local_info.mapbase,
484  CURLFORM_END);
485 
486  curl_formadd(&formpost, &lastptr,
487  CURLFORM_COPYNAME, "codebase",
488  CURLFORM_COPYCONTENTS, local_info.codebase,
489  CURLFORM_END);
490 
491  curl_formadd(&formpost, &lastptr,
492  CURLFORM_COPYNAME, "flags",
493  CURLFORM_COPYCONTENTS, local_info.flags,
494  CURLFORM_END);
495 
496  pthread_mutex_lock(&ms2_info_mutex);
497 
498  snprintf(buf, MAX_BUF-1, "%d", metaserver2_updateinfo.num_players);
499  curl_formadd(&formpost, &lastptr,
500  CURLFORM_COPYNAME, "num_players",
501  CURLFORM_COPYCONTENTS, buf,
502  CURLFORM_END);
503 
504  snprintf(buf, MAX_BUF-1, "%d", metaserver2_updateinfo.in_bytes);
505  curl_formadd(&formpost, &lastptr,
506  CURLFORM_COPYNAME, "in_bytes",
507  CURLFORM_COPYCONTENTS, buf,
508  CURLFORM_END);
509 
510  snprintf(buf, MAX_BUF-1, "%d", metaserver2_updateinfo.out_bytes);
511  curl_formadd(&formpost, &lastptr,
512  CURLFORM_COPYNAME, "out_bytes",
513  CURLFORM_COPYCONTENTS, buf,
514  CURLFORM_END);
515 
516  snprintf(buf, MAX_BUF-1, "%ld", metaserver2_updateinfo.uptime);
517  curl_formadd(&formpost, &lastptr,
518  CURLFORM_COPYNAME, "uptime",
519  CURLFORM_COPYCONTENTS, buf,
520  CURLFORM_END);
521 
522  pthread_mutex_unlock(&ms2_info_mutex);
523 
524  /* Following few fields are global variables,
525  * but are really defines, so won't ever change.
526  */
527  curl_formadd(&formpost, &lastptr,
528  CURLFORM_COPYNAME, "version",
529  CURLFORM_COPYCONTENTS, FULL_VERSION,
530  CURLFORM_END);
531 
532  snprintf(buf, MAX_BUF-1, "%d", VERSION_SC);
533  curl_formadd(&formpost, &lastptr,
534  CURLFORM_COPYNAME, "sc_version",
535  CURLFORM_COPYCONTENTS, buf,
536  CURLFORM_END);
537 
538  snprintf(buf, MAX_BUF-1, "%d", VERSION_CS);
539  curl_formadd(&formpost, &lastptr,
540  CURLFORM_COPYNAME, "cs_version",
541  CURLFORM_COPYCONTENTS, buf,
542  CURLFORM_END);
543 
544  for (ms2 = metaserver2; ms2; ms2 = ms2->next) {
545  CURL *curl;
546  CURLcode res;
547 
548  curl = curl_easy_init();
549  if (curl) {
550  /* what URL that receives this POST */
551  curl_easy_setopt(curl, CURLOPT_URL, ms2->hostname);
552  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
553 
554  /* Almost always, we will get HTTP data returned
555  * to us - instead of it going to stderr,
556  * we want to take care of it ourselves.
557  */
558  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
559  res = curl_easy_perform(curl);
560 
561  if (res)
562  fprintf(stderr, "easy_perform got error %d\n", res);
563 
564  /* always cleanup */
565  curl_easy_cleanup(curl);
566  }
567  }
568  /* then cleanup the formpost chain */
569  curl_formfree(formpost);
570 #endif
571 }
572 
586 void *metaserver2_thread(void *junk) {
587  while (1) {
589  sleep(60);
590  }
591 }
static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
Definition: metaserver.c:427
char * html_comment
Definition: metaserver.c:215
Definition: player.h:146
#define ST_GET_PARTY_PASSWORD
Definition: define.h:894
struct _MetaServer2 * next
Definition: metaserver.c:198
#define FALSE
Definition: exp.c:42
char meta_host[MAX_BUF]
Definition: global.h:376
unsigned int meta_on
Definition: global.h:374
int obytes
Definition: newclient.h:582
socket_struct socket
Definition: player.h:148
static int metafd
Definition: metaserver.c:56
struct _MetaServer2 MetaServer2
void close_and_delete(FILE *fp, int compressed)
Definition: porting.c:748
void metaserver_init(void)
Definition: metaserver.c:68
#define FLAG_AFK
Definition: define.h:673
char * hostname
Definition: metaserver.c:197
char meta_server[MAX_BUF]
Definition: global.h:375
#define ST_PLAYING
Definition: define.h:886
char meta_comment[MAX_BUF]
Definition: global.h:378
void metaserver_update(void)
Definition: metaserver.c:134
#define VERSION_SC
Definition: newserver.h:181
char * text_comment
Definition: metaserver.c:216
void * metaserver2_thread(void *junk)
Definition: metaserver.c:586
uint32 hidden
Definition: player.h:186
struct mapdef * map
Definition: object.h:155
int ibytes
Definition: newclient.h:581
uint8 state
Definition: player.h:172
uint32 is_bot
Definition: newserver.h:137
int metaserver2_init(void)
Definition: metaserver.c:255
#define TRUE
Definition: exp.c:41
struct _LocalMeta2Info LocalMeta2Info
MetaServer2_UpdateInfo metaserver2_updateinfo
Definition: metaserver.c:228
#define VERSION_CS
Definition: newserver.h:180
static void free_metaserver2(MetaServer2 *ms)
Definition: metaserver.c:236
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
pthread_mutex_t ms2_info_mutex
Definition: metaserver.c:226
static LocalMeta2Info local_info
Definition: metaserver.c:223
char * strdup_local(const char *str)
Definition: porting.c:310
#define FLAG_WIZ
Definition: define.h:527
#define MAX_BUF
Definition: define.h:81
static void metaserver2_updates(void)
Definition: metaserver.c:442
const char * confdir
Definition: global.h:333
static MetaServer2 * metaserver2
Definition: metaserver.c:201
object * ob
Definition: player.h:207
uint16 meta_port
Definition: global.h:377
static struct sockaddr_in sock
Definition: metaserver.c:58
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define sleep(x)
Definition: win32.h:167
CS_Stats cst_tot
struct Settings settings
Definition: init.c:48
EXTERN player * first_player
Definition: global.h:190
uint16 csport
Definition: global.h:326
time_t time_start
Definition: newclient.h:584
struct pl * next
Definition: player.h:147
int strcasecmp(const char *s1, const char *s2)
Definition: porting.c:434
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
#define FULL_VERSION
Definition: version.h:6
#define FREE_AND_CLEAR(xyz)
Definition: global.h:282
FILE * open_and_uncompress(const char *name, int flag, int *compressed)
Definition: porting.c:724