Crossfire Server, Trunk
metaserver.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
20 #include "global.h"
21 
22 #include <ctype.h>
23 #include <pthread.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #ifndef WIN32 /* ---win32 exclude unix header files */
28 #include <sys/types.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <arpa/inet.h>
32 
33 #endif /* end win32 */
34 
35 #include "metaserver2.h"
36 #include "version.h"
37 
38 #ifdef HAVE_LIBCURL
39 #include <curl/curl.h>
40 #include <curl/easy.h>
41 #endif
42 
44 static pthread_mutex_t ms2_info_mutex;
45 
47  /* We could use socket_info.nconns, but that is not quite as accurate,
48  * as connections in the progress of being established, are listening
49  * but don't have a player, etc. The checks below are basically the
50  * same as for the who commands with the addition that WIZ, AFK, and BOT
51  * players are not counted.
52  */
53  char num_players = 0;
54  for (player *pl = first_player; pl != NULL; pl = pl->next) {
55  if (pl->ob->map == NULL)
56  continue;
57  if (pl->hidden)
58  continue;
59  if (QUERY_FLAG(pl->ob, FLAG_WIZ))
60  continue;
61  if (QUERY_FLAG(pl->ob, FLAG_AFK))
62  continue;
64  continue;
65  if (pl->socket.is_bot)
66  continue;
67  num_players++;
68  }
69  return num_players;
70 }
71 
73  int players = 0;
74  player *pl;
75  for (pl = first_player, players = 0; pl != NULL; pl = pl->next, players++);
76  return players;
77 }
78 
85 void metaserver_update(void) {
86 #ifdef HAVE_LIBCURL
87  /* Everything inside the pthread lock/unlock is related
88  * to metaserver2 synchronization.
89  */
90  pthread_mutex_lock(&ms2_info_mutex);
95  pthread_mutex_unlock(&ms2_info_mutex);
96 #endif
97 }
98 
99 /*
100  * Start of metaserver2 logic
101  * Note: All static structures in this file should be treated as strictly
102  * private. The metaserver2 update logic runs in its own thread,
103  * so if something needs to modify these structures, proper locking
104  * is needed.
105  */
106 
111 typedef struct _MetaServer2 {
112  char *hostname;
113  struct _MetaServer2 *next;
114 } MetaServer2;
115 
118 
127 typedef struct _LocalMeta2Info {
129  char *hostname;
131  char *html_comment;
132  char *text_comment;
133  char *archbase;
134  char *mapbase;
135  char *codebase;
136  char *flags;
138 
141 
144 
152 static void free_metaserver2(MetaServer2 *ms) {
153  free(ms->hostname);
154  free(ms);
155 }
156 
171 int metaserver2_init(void) {
172  static int has_init = 0;
173  FILE *fp;
174  char buf[MAX_BUF], *cp, dummy[1];
175  MetaServer2 *ms2, *msnext;
176  pthread_t thread_id;
177 
178  dummy[0] = '\0';
179 
180 #ifdef HAVE_LIBCURL
181  if (!has_init) {
182  memset(&local_info, 0, sizeof(LocalMeta2Info));
183  memset(&metaserver2_updateinfo, 0, sizeof(MetaServer2_UpdateInfo));
184 
186  metaserver2 = NULL;
187  pthread_mutex_init(&ms2_info_mutex, NULL);
188  curl_global_init(CURL_GLOBAL_ALL);
189  } else {
191  if (local_info.hostname)
197  if (local_info.archbase)
199  if (local_info.mapbase)
201  if (local_info.codebase)
203  if (local_info.flags)
205  for (ms2 = metaserver2; ms2; ms2 = msnext) {
206  msnext = ms2->next;
207  free_metaserver2(ms2);
208  }
209  metaserver2 = NULL;
210  }
211 #endif
212 
213  /* Now load up the values from the file */
214  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
215 
216  if ((fp = fopen(buf, "r")) == NULL) {
217  LOG(llevError, "Warning: No metaserver2 file found\n");
218  return 0;
219  }
220  while (fgets(buf, sizeof(buf), fp) != NULL) {
221  if (buf[0] == '#')
222  continue;
223  /* eliminate newline */
224  if ((cp = strrchr(buf, '\n')) != NULL)
225  *cp = '\0';
226 
227  /* Skip over empty lines */
228  if (buf[0] == 0)
229  continue;
230 
231  /* Find variable pairs */
232 
233  if ((cp = strpbrk(buf, " \t")) != NULL) {
234  while (isspace(*cp))
235  *cp++ = 0;
236  } else {
237  /* This makes it so we don't have to do NULL checks against
238  * cp everyplace
239  */
240  cp = dummy;
241  }
242 
243  if (!strcasecmp(buf, "metaserver2_notification")) {
244  if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
246  } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
248  } else {
249  LOG(llevError, "metaserver2: Unknown value for metaserver2_notification: %s\n", cp);
250  }
251  } else if (!strcasecmp(buf, "metaserver2_server")) {
252  if (*cp != 0) {
253  ms2 = calloc(1, sizeof(MetaServer2));
254  ms2->hostname = strdup(cp);
255  ms2->next = metaserver2;
256  metaserver2 = ms2;
257  } else {
258  LOG(llevError, "metaserver2: metaserver2_server must have a value.\n");
259  }
260  } else if (!strcasecmp(buf, "localhostname")) {
261  if (*cp != 0) {
263  } else {
264  LOG(llevError, "metaserver2: localhostname must have a value.\n");
265  }
266  } else if (!strcasecmp(buf, "portnumber")) {
267  if (*cp != 0) {
268  local_info.portnumber = atoi(cp);
269  } else {
270  LOG(llevError, "metaserver2: portnumber must have a value.\n");
271  }
272  /* For the following values, it is easier to make sure
273  * the pointers are set to something, even if it is a blank
274  * string, so don't care if there is data in the string or not.
275  */
276  } else if (!strcasecmp(buf, "html_comment")) {
277  local_info.html_comment = strdup(cp);
278  } else if (!strcasecmp(buf, "text_comment")) {
279  local_info.text_comment = strdup(cp);
280  } else if (!strcasecmp(buf, "archbase")) {
281  local_info.archbase = strdup(cp);
282  } else if (!strcasecmp(buf, "mapbase")) {
283  local_info.mapbase = strdup(cp);
284  } else if (!strcasecmp(buf, "codebase")) {
285  local_info.codebase = strdup(cp);
286  } else if (!strcasecmp(buf, "flags")) {
287  local_info.flags = strdup(cp);
288  } else {
289  LOG(llevError, "Unknown value in metaserver2 file: %s\n", buf);
290  }
291  }
292  fclose(fp);
293 
294  /* If no hostname is set, can't do updates */
295  if (!local_info.hostname)
297 
298 #ifndef HAVE_LIBCURL
299  if (local_info.notification) {
300  LOG(llevError, "metaserver2 file is set to do notification, but libcurl is not found.\n");
301  LOG(llevError, "Either fix your compilation, or turn off metaserver2 notification in \n");
302  LOG(llevError, "the %s/metaserver2 file.\n", settings.confdir);
303  LOG(llevError, "Exiting program.\n");
304  exit(1);
305  }
306 #endif
307 
308  if (local_info.notification) {
309  int ret;
310 
311  /* As noted above, it is much easier for the rest of the code
312  * to not have to check for null pointers. So we do that
313  * here, and anything that is null, we just allocate
314  * an empty string.
315  */
317  local_info.html_comment = strdup("");
319  local_info.text_comment = strdup("");
320  if (!local_info.archbase)
321  local_info.archbase = strdup("");
322  if (!local_info.mapbase)
323  local_info.mapbase = strdup("");
324  if (!local_info.codebase)
325  local_info.codebase = strdup("");
326  if (!local_info.flags)
327  local_info.flags = strdup("");
328 
329  ret = pthread_create(&thread_id, NULL, metaserver2_thread, NULL);
330  if (ret) {
331  LOG(llevError, "metaserver2_init: return code from pthread_create() is %d\n", ret);
332 
333  /* Effectively true - we're not going to update the metaserver */
335  }
336  }
337  return local_info.notification;
338 }
339 
351 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
352  (void)data;
353  size_t realsize = size*nmemb;
354  LOG(llevError, "Message from metaserver:\n%s\n", (const char*)ptr);
355  return realsize;
356 }
357 
358 #ifdef HAVE_LIBCURL
359 static void metaserver2_build_form(struct curl_httppost **formpost) {
360  struct curl_httppost *lastptr = NULL;
361  char buf[MAX_BUF];
362 
363  /* First, fill in the form - note that everything has to be a string,
364  * so we convert as needed with snprintf.
365  * The order of fields here really isn't important.
366  * The string after CURLFORM_COPYNAME is the name of the POST variable
367  * as the
368  */
369  curl_formadd(formpost, &lastptr,
370  CURLFORM_COPYNAME, "hostname",
371  CURLFORM_COPYCONTENTS, local_info.hostname,
372  CURLFORM_END);
373 
374  snprintf(buf, sizeof(buf), "%d", local_info.portnumber);
375  curl_formadd(formpost, &lastptr,
376  CURLFORM_COPYNAME, "port",
377  CURLFORM_COPYCONTENTS, buf,
378  CURLFORM_END);
379 
380  curl_formadd(formpost, &lastptr,
381  CURLFORM_COPYNAME, "html_comment",
382  CURLFORM_COPYCONTENTS, local_info.html_comment,
383  CURLFORM_END);
384 
385  curl_formadd(formpost, &lastptr,
386  CURLFORM_COPYNAME, "text_comment",
387  CURLFORM_COPYCONTENTS, local_info.text_comment,
388  CURLFORM_END);
389 
390  curl_formadd(formpost, &lastptr,
391  CURLFORM_COPYNAME, "archbase",
392  CURLFORM_COPYCONTENTS, local_info.archbase,
393  CURLFORM_END);
394 
395  curl_formadd(formpost, &lastptr,
396  CURLFORM_COPYNAME, "mapbase",
397  CURLFORM_COPYCONTENTS, local_info.mapbase,
398  CURLFORM_END);
399 
400  curl_formadd(formpost, &lastptr,
401  CURLFORM_COPYNAME, "codebase",
402  CURLFORM_COPYCONTENTS, local_info.codebase,
403  CURLFORM_END);
404 
405  curl_formadd(formpost, &lastptr,
406  CURLFORM_COPYNAME, "flags",
407  CURLFORM_COPYCONTENTS, local_info.flags,
408  CURLFORM_END);
409 
410  pthread_mutex_lock(&ms2_info_mutex);
411 
412  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.num_players);
413  curl_formadd(formpost, &lastptr,
414  CURLFORM_COPYNAME, "num_players",
415  CURLFORM_COPYCONTENTS, buf,
416  CURLFORM_END);
417 
418  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.in_bytes);
419  curl_formadd(formpost, &lastptr,
420  CURLFORM_COPYNAME, "in_bytes",
421  CURLFORM_COPYCONTENTS, buf,
422  CURLFORM_END);
423 
424  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.out_bytes);
425  curl_formadd(formpost, &lastptr,
426  CURLFORM_COPYNAME, "out_bytes",
427  CURLFORM_COPYCONTENTS, buf,
428  CURLFORM_END);
429 
430  snprintf(buf, sizeof(buf), "%ld", (long)metaserver2_updateinfo.uptime);
431  curl_formadd(formpost, &lastptr,
432  CURLFORM_COPYNAME, "uptime",
433  CURLFORM_COPYCONTENTS, buf,
434  CURLFORM_END);
435 
436  pthread_mutex_unlock(&ms2_info_mutex);
437 
438  /* Following few fields are global variables,
439  * but are really defines, so won't ever change.
440  */
441  curl_formadd(formpost, &lastptr,
442  CURLFORM_COPYNAME, "version",
443  CURLFORM_COPYCONTENTS, FULL_VERSION,
444  CURLFORM_END);
445 
446  snprintf(buf, sizeof(buf), "%d", VERSION_SC);
447  curl_formadd(formpost, &lastptr,
448  CURLFORM_COPYNAME, "sc_version",
449  CURLFORM_COPYCONTENTS, buf,
450  CURLFORM_END);
451 
452  snprintf(buf, sizeof(buf), "%d", VERSION_CS);
453  curl_formadd(formpost, &lastptr,
454  CURLFORM_COPYNAME, "cs_version",
455  CURLFORM_COPYCONTENTS, buf,
456  CURLFORM_END);
457 }
458 #endif
459 
465 static void metaserver2_updates(void) {
466 #ifdef HAVE_LIBCURL
467  struct curl_httppost *formpost = NULL;
468  metaserver2_build_form(&formpost);
469 
470  for (MetaServer2 *ms2 = metaserver2; ms2; ms2 = ms2->next) {
471  CURL *curl = curl_easy_init();
472  if (curl) {
473  /* what URL that receives this POST */
474  curl_easy_setopt(curl, CURLOPT_URL, ms2->hostname);
475  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
476 
477  /* Almost always, we will get HTTP data returned
478  * to us - instead of it going to stderr,
479  * we want to take care of it ourselves.
480  */
481  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
482 
483  char errbuf[CURL_ERROR_SIZE];
484  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
485 
486  CURLcode res = curl_easy_perform(curl);
487  if (res) {
488  LOG(llevError, "metaserver update failed: %s\n", errbuf);
489  }
490 
491  /* always cleanup */
492  curl_easy_cleanup(curl);
493  } else {
494  LOG(llevError, "metaserver: could not initialize curl\n");
495  }
496  }
497  /* then cleanup the formpost chain */
498  curl_formfree(formpost);
499 #endif
500 }
501 
516 void *metaserver2_thread(void *junk) {
517  (void)junk;
518  while (1) {
520  sleep(60);
521  }
522  return NULL;
523 }
global.h
CS_Stats::ibytes
int ibytes
Definition: newclient.h:695
llevError
@ llevError
Definition: logger.h:11
_LocalMeta2Info::codebase
char * codebase
Definition: metaserver.c:135
strdup_local
#define strdup_local
Definition: compat.h:29
_MetaServer2
Definition: metaserver.c:111
obj::map
struct mapdef * map
Definition: object.h:300
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
FALSE
#define FALSE
Definition: compat.h:14
_MetaServer2_UpdateInfo::uptime
time_t uptime
Definition: metaserver2.h:35
_MetaServer2_UpdateInfo::out_bytes
int out_bytes
Definition: metaserver2.h:34
FULL_VERSION
#define FULL_VERSION
Definition: version.h:4
count_all_players
int count_all_players()
Definition: metaserver.c:72
pl::socket
socket_struct socket
Definition: player.h:107
pl
Definition: player.h:105
metaserver2_init
int metaserver2_init(void)
Definition: metaserver.c:171
pl::ob
object * ob
Definition: player.h:176
socket_struct::is_bot
uint32_t is_bot
Definition: newserver.h:107
metaserver2_writer
static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
Definition: metaserver.c:351
guildbuy.players
list players
Definition: guildbuy.py:17
_MetaServer2_UpdateInfo::num_players
int num_players
Definition: metaserver2.h:32
Settings::csport
uint16_t csport
Definition: global.h:238
metaserver2_updateinfo
MetaServer2_UpdateInfo metaserver2_updateinfo
Definition: metaserver.c:143
version.h
settings
struct Settings settings
Definition: init.c:39
CS_Stats::time_start
time_t time_start
Definition: newclient.h:698
pl::next
struct pl * next
Definition: player.h:106
free_metaserver2
static void free_metaserver2(MetaServer2 *ms)
Definition: metaserver.c:152
local_info
static LocalMeta2Info local_info
Definition: metaserver.c:140
pl::state
uint8_t state
Definition: player.h:131
_LocalMeta2Info::text_comment
char * text_comment
Definition: metaserver.c:132
sleep
Definition: sleep.py:1
FLAG_AFK
#define FLAG_AFK
Definition: define.h:368
_MetaServer2_UpdateInfo::in_bytes
int in_bytes
Definition: metaserver2.h:33
first_player
EXTERN player * first_player
Definition: global.h:115
_LocalMeta2Info::notification
int notification
Definition: metaserver.c:128
navar-midane_time.data
data
Definition: navar-midane_time.py:11
metaserver2_updates
static void metaserver2_updates(void)
Definition: metaserver.c:465
MetaServer2
struct _MetaServer2 MetaServer2
Settings::confdir
const char * confdir
Definition: global.h:242
VERSION_SC
#define VERSION_SC
Definition: newserver.h:150
metaserver2_thread
void * metaserver2_thread(void *junk)
Definition: metaserver.c:516
_LocalMeta2Info::mapbase
char * mapbase
Definition: metaserver.c:134
CS_Stats::obytes
int obytes
Definition: newclient.h:696
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
MAX_BUF
#define MAX_BUF
Definition: define.h:35
cst_tot
CS_Stats cst_tot
_MetaServer2::hostname
char * hostname
Definition: metaserver.c:112
ST_PLAYING
#define ST_PLAYING
Definition: define.h:541
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
FREE_AND_CLEAR
#define FREE_AND_CLEAR(xyz)
Definition: global.h:190
_LocalMeta2Info::hostname
char * hostname
Definition: metaserver.c:129
metaserver_update
void metaserver_update(void)
Definition: metaserver.c:85
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
_MetaServer2::next
struct _MetaServer2 * next
Definition: metaserver.c:113
_LocalMeta2Info::portnumber
int portnumber
Definition: metaserver.c:130
_LocalMeta2Info
Definition: metaserver.c:127
metaserver2.h
buf
StringBuffer * buf
Definition: readable.c:1610
pl::hidden
uint32_t hidden
Definition: player.h:147
strcasecmp
int strcasecmp(const char *s1, const char *s2)
metaserver2
static MetaServer2 * metaserver2
Definition: metaserver.c:117
_MetaServer2_UpdateInfo
Definition: metaserver2.h:31
ST_GET_PARTY_PASSWORD
#define ST_GET_PARTY_PASSWORD
Definition: define.h:549
VERSION_CS
#define VERSION_CS
Definition: newserver.h:149
_LocalMeta2Info::html_comment
char * html_comment
Definition: metaserver.c:131
LocalMeta2Info
struct _LocalMeta2Info LocalMeta2Info
_LocalMeta2Info::archbase
char * archbase
Definition: metaserver.c:133
TRUE
#define TRUE
Definition: compat.h:11
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
count_players
int count_players()
Definition: metaserver.c:46
ms2_info_mutex
static pthread_mutex_t ms2_info_mutex
Definition: metaserver.c:44
altar_valkyrie.res
int res
Definition: altar_valkyrie.py:74
_LocalMeta2Info::flags
char * flags
Definition: metaserver.c:136