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 
78 void metaserver_update(void) {
79 #ifdef HAVE_LIBCURL
80  /* Everything inside the pthread lock/unlock is related
81  * to metaserver2 synchronization.
82  */
83  pthread_mutex_lock(&ms2_info_mutex);
88  pthread_mutex_unlock(&ms2_info_mutex);
89 #endif
90 }
91 
92 /*
93  * Start of metaserver2 logic
94  * Note: All static structures in this file should be treated as strictly
95  * private. The metaserver2 update logic runs in its own thread,
96  * so if something needs to modify these structures, proper locking
97  * is needed.
98  */
99 
104 typedef struct _MetaServer2 {
105  char *hostname;
106  struct _MetaServer2 *next;
107 } MetaServer2;
108 
111 
120 typedef struct _LocalMeta2Info {
122  char *hostname;
124  char *html_comment;
125  char *text_comment;
126  char *archbase;
127  char *mapbase;
128  char *codebase;
129  char *flags;
131 
134 
137 
145 static void free_metaserver2(MetaServer2 *ms) {
146  free(ms->hostname);
147  free(ms);
148 }
149 
164 int metaserver2_init(void) {
165  static int has_init = 0;
166  FILE *fp;
167  char buf[MAX_BUF], *cp, dummy[1];
168  MetaServer2 *ms2, *msnext;
169  pthread_t thread_id;
170 
171  dummy[0] = '\0';
172 
173 #ifdef HAVE_LIBCURL
174  if (!has_init) {
175  memset(&local_info, 0, sizeof(LocalMeta2Info));
176  memset(&metaserver2_updateinfo, 0, sizeof(MetaServer2_UpdateInfo));
177 
179  metaserver2 = NULL;
180  pthread_mutex_init(&ms2_info_mutex, NULL);
181  curl_global_init(CURL_GLOBAL_ALL);
182  } else {
184  if (local_info.hostname)
190  if (local_info.archbase)
192  if (local_info.mapbase)
194  if (local_info.codebase)
196  if (local_info.flags)
198  for (ms2 = metaserver2; ms2; ms2 = msnext) {
199  msnext = ms2->next;
200  free_metaserver2(ms2);
201  }
202  metaserver2 = NULL;
203  }
204 #endif
205 
206  /* Now load up the values from the file */
207  snprintf(buf, sizeof(buf), "%s/metaserver2", settings.confdir);
208 
209  if ((fp = fopen(buf, "r")) == NULL) {
210  LOG(llevError, "Warning: No metaserver2 file found\n");
211  return 0;
212  }
213  while (fgets(buf, sizeof(buf), fp) != NULL) {
214  if (buf[0] == '#')
215  continue;
216  /* eliminate newline */
217  if ((cp = strrchr(buf, '\n')) != NULL)
218  *cp = '\0';
219 
220  /* Skip over empty lines */
221  if (buf[0] == 0)
222  continue;
223 
224  /* Find variable pairs */
225 
226  if ((cp = strpbrk(buf, " \t")) != NULL) {
227  while (isspace(*cp))
228  *cp++ = 0;
229  } else {
230  /* This makes it so we don't have to do NULL checks against
231  * cp everyplace
232  */
233  cp = dummy;
234  }
235 
236  if (!strcasecmp(buf, "metaserver2_notification")) {
237  if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
239  } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
241  } else {
242  LOG(llevError, "metaserver2: Unknown value for metaserver2_notification: %s\n", cp);
243  }
244  } else if (!strcasecmp(buf, "metaserver2_server")) {
245  if (*cp != 0) {
246  ms2 = calloc(1, sizeof(MetaServer2));
247  ms2->hostname = strdup(cp);
248  ms2->next = metaserver2;
249  metaserver2 = ms2;
250  } else {
251  LOG(llevError, "metaserver2: metaserver2_server must have a value.\n");
252  }
253  } else if (!strcasecmp(buf, "localhostname")) {
254  if (*cp != 0) {
256  } else {
257  LOG(llevError, "metaserver2: localhostname must have a value.\n");
258  }
259  } else if (!strcasecmp(buf, "portnumber")) {
260  if (*cp != 0) {
261  local_info.portnumber = atoi(cp);
262  } else {
263  LOG(llevError, "metaserver2: portnumber must have a value.\n");
264  }
265  /* For the following values, it is easier to make sure
266  * the pointers are set to something, even if it is a blank
267  * string, so don't care if there is data in the string or not.
268  */
269  } else if (!strcasecmp(buf, "html_comment")) {
270  local_info.html_comment = strdup(cp);
271  } else if (!strcasecmp(buf, "text_comment")) {
272  local_info.text_comment = strdup(cp);
273  } else if (!strcasecmp(buf, "archbase")) {
274  local_info.archbase = strdup(cp);
275  } else if (!strcasecmp(buf, "mapbase")) {
276  local_info.mapbase = strdup(cp);
277  } else if (!strcasecmp(buf, "codebase")) {
278  local_info.codebase = strdup(cp);
279  } else if (!strcasecmp(buf, "flags")) {
280  local_info.flags = strdup(cp);
281  } else {
282  LOG(llevError, "Unknown value in metaserver2 file: %s\n", buf);
283  }
284  }
285  fclose(fp);
286 
287  /* If no hostname is set, can't do updates */
288  if (!local_info.hostname)
290 
291 #ifndef HAVE_LIBCURL
292  if (local_info.notification) {
293  LOG(llevError, "metaserver2 file is set to do notification, but libcurl is not found.\n");
294  LOG(llevError, "Either fix your compilation, or turn off metaserver2 notification in \n");
295  LOG(llevError, "the %s/metaserver2 file.\n", settings.confdir);
296  LOG(llevError, "Exiting program.\n");
297  exit(1);
298  }
299 #endif
300 
301  if (local_info.notification) {
302  int ret;
303 
304  /* As noted above, it is much easier for the rest of the code
305  * to not have to check for null pointers. So we do that
306  * here, and anything that is null, we just allocate
307  * an empty string.
308  */
310  local_info.html_comment = strdup("");
312  local_info.text_comment = strdup("");
313  if (!local_info.archbase)
314  local_info.archbase = strdup("");
315  if (!local_info.mapbase)
316  local_info.mapbase = strdup("");
317  if (!local_info.codebase)
318  local_info.codebase = strdup("");
319  if (!local_info.flags)
320  local_info.flags = strdup("");
321 
322  ret = pthread_create(&thread_id, NULL, metaserver2_thread, NULL);
323  if (ret) {
324  LOG(llevError, "metaserver2_init: return code from pthread_create() is %d\n", ret);
325 
326  /* Effectively true - we're not going to update the metaserver */
328  }
329  }
330  return local_info.notification;
331 }
332 
344 static size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) {
345  (void)data;
346  size_t realsize = size*nmemb;
347  LOG(llevError, "Message from metaserver:\n%s\n", (const char*)ptr);
348  return realsize;
349 }
350 
351 #ifdef HAVE_LIBCURL
352 static void metaserver2_build_form(struct curl_httppost **formpost) {
353  struct curl_httppost *lastptr = NULL;
354  char buf[MAX_BUF];
355 
356  /* First, fill in the form - note that everything has to be a string,
357  * so we convert as needed with snprintf.
358  * The order of fields here really isn't important.
359  * The string after CURLFORM_COPYNAME is the name of the POST variable
360  * as the
361  */
362  curl_formadd(formpost, &lastptr,
363  CURLFORM_COPYNAME, "hostname",
364  CURLFORM_COPYCONTENTS, local_info.hostname,
365  CURLFORM_END);
366 
367  snprintf(buf, sizeof(buf), "%d", local_info.portnumber);
368  curl_formadd(formpost, &lastptr,
369  CURLFORM_COPYNAME, "port",
370  CURLFORM_COPYCONTENTS, buf,
371  CURLFORM_END);
372 
373  curl_formadd(formpost, &lastptr,
374  CURLFORM_COPYNAME, "html_comment",
375  CURLFORM_COPYCONTENTS, local_info.html_comment,
376  CURLFORM_END);
377 
378  curl_formadd(formpost, &lastptr,
379  CURLFORM_COPYNAME, "text_comment",
380  CURLFORM_COPYCONTENTS, local_info.text_comment,
381  CURLFORM_END);
382 
383  curl_formadd(formpost, &lastptr,
384  CURLFORM_COPYNAME, "archbase",
385  CURLFORM_COPYCONTENTS, local_info.archbase,
386  CURLFORM_END);
387 
388  curl_formadd(formpost, &lastptr,
389  CURLFORM_COPYNAME, "mapbase",
390  CURLFORM_COPYCONTENTS, local_info.mapbase,
391  CURLFORM_END);
392 
393  curl_formadd(formpost, &lastptr,
394  CURLFORM_COPYNAME, "codebase",
395  CURLFORM_COPYCONTENTS, local_info.codebase,
396  CURLFORM_END);
397 
398  curl_formadd(formpost, &lastptr,
399  CURLFORM_COPYNAME, "flags",
400  CURLFORM_COPYCONTENTS, local_info.flags,
401  CURLFORM_END);
402 
403  pthread_mutex_lock(&ms2_info_mutex);
404 
405  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.num_players);
406  curl_formadd(formpost, &lastptr,
407  CURLFORM_COPYNAME, "num_players",
408  CURLFORM_COPYCONTENTS, buf,
409  CURLFORM_END);
410 
411  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.in_bytes);
412  curl_formadd(formpost, &lastptr,
413  CURLFORM_COPYNAME, "in_bytes",
414  CURLFORM_COPYCONTENTS, buf,
415  CURLFORM_END);
416 
417  snprintf(buf, sizeof(buf), "%d", metaserver2_updateinfo.out_bytes);
418  curl_formadd(formpost, &lastptr,
419  CURLFORM_COPYNAME, "out_bytes",
420  CURLFORM_COPYCONTENTS, buf,
421  CURLFORM_END);
422 
423  snprintf(buf, sizeof(buf), "%ld", (long)metaserver2_updateinfo.uptime);
424  curl_formadd(formpost, &lastptr,
425  CURLFORM_COPYNAME, "uptime",
426  CURLFORM_COPYCONTENTS, buf,
427  CURLFORM_END);
428 
429  pthread_mutex_unlock(&ms2_info_mutex);
430 
431  /* Following few fields are global variables,
432  * but are really defines, so won't ever change.
433  */
434  curl_formadd(formpost, &lastptr,
435  CURLFORM_COPYNAME, "version",
436  CURLFORM_COPYCONTENTS, FULL_VERSION,
437  CURLFORM_END);
438 
439  snprintf(buf, sizeof(buf), "%d", VERSION_SC);
440  curl_formadd(formpost, &lastptr,
441  CURLFORM_COPYNAME, "sc_version",
442  CURLFORM_COPYCONTENTS, buf,
443  CURLFORM_END);
444 
445  snprintf(buf, sizeof(buf), "%d", VERSION_CS);
446  curl_formadd(formpost, &lastptr,
447  CURLFORM_COPYNAME, "cs_version",
448  CURLFORM_COPYCONTENTS, buf,
449  CURLFORM_END);
450 }
451 #endif
452 
458 static void metaserver2_updates(void) {
459 #ifdef HAVE_LIBCURL
460  struct curl_httppost *formpost = NULL;
461  metaserver2_build_form(&formpost);
462 
463  for (MetaServer2 *ms2 = metaserver2; ms2; ms2 = ms2->next) {
464  CURL *curl = curl_easy_init();
465  if (curl) {
466  /* what URL that receives this POST */
467  curl_easy_setopt(curl, CURLOPT_URL, ms2->hostname);
468  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
469 
470  /* Almost always, we will get HTTP data returned
471  * to us - instead of it going to stderr,
472  * we want to take care of it ourselves.
473  */
474  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
475 
476  char errbuf[CURL_ERROR_SIZE];
477  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
478 
479  CURLcode res = curl_easy_perform(curl);
480  if (res) {
481  LOG(llevError, "metaserver update failed: %s\n", errbuf);
482  }
483 
484  /* always cleanup */
485  curl_easy_cleanup(curl);
486  } else {
487  LOG(llevError, "metaserver: could not initialize curl\n");
488  }
489  }
490  /* then cleanup the formpost chain */
491  curl_formfree(formpost);
492 #endif
493 }
494 
509 void *metaserver2_thread(void *junk) {
510  (void)junk;
511  while (1) {
513  sleep(60);
514  }
515  return NULL;
516 }
global.h
CS_Stats::ibytes
int ibytes
Definition: newclient.h:695
llevError
@ llevError
Definition: logger.h:11
_LocalMeta2Info::codebase
char * codebase
Definition: metaserver.c:128
strdup_local
#define strdup_local
Definition: compat.h:29
_MetaServer2
Definition: metaserver.c:104
obj::map
struct mapdef * map
Definition: object.h:298
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
pl::socket
socket_struct socket
Definition: player.h:94
pl
Definition: player.h:92
metaserver2_init
int metaserver2_init(void)
Definition: metaserver.c:164
pl::ob
object * ob
Definition: player.h:162
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:344
_MetaServer2_UpdateInfo::num_players
int num_players
Definition: metaserver2.h:32
Settings::csport
uint16_t csport
Definition: global.h:239
metaserver2_updateinfo
MetaServer2_UpdateInfo metaserver2_updateinfo
Definition: metaserver.c:136
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:93
free_metaserver2
static void free_metaserver2(MetaServer2 *ms)
Definition: metaserver.c:145
local_info
static LocalMeta2Info local_info
Definition: metaserver.c:133
pl::state
uint8_t state
Definition: player.h:118
_LocalMeta2Info::text_comment
char * text_comment
Definition: metaserver.c:125
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:121
navar-midane_time.data
data
Definition: navar-midane_time.py:11
metaserver2_updates
static void metaserver2_updates(void)
Definition: metaserver.c:458
MetaServer2
struct _MetaServer2 MetaServer2
Settings::confdir
const char * confdir
Definition: global.h:243
VERSION_SC
#define VERSION_SC
Definition: newserver.h:150
metaserver2_thread
void * metaserver2_thread(void *junk)
Definition: metaserver.c:509
_LocalMeta2Info::mapbase
char * mapbase
Definition: metaserver.c:127
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:105
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:191
_LocalMeta2Info::hostname
char * hostname
Definition: metaserver.c:122
metaserver_update
void metaserver_update(void)
Definition: metaserver.c:78
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
_MetaServer2::next
struct _MetaServer2 * next
Definition: metaserver.c:106
_LocalMeta2Info::portnumber
int portnumber
Definition: metaserver.c:123
_LocalMeta2Info
Definition: metaserver.c:120
metaserver2.h
buf
StringBuffer * buf
Definition: readable.c:1606
pl::hidden
uint32_t hidden
Definition: player.h:134
strcasecmp
int strcasecmp(const char *s1, const char *s2)
metaserver2
static MetaServer2 * metaserver2
Definition: metaserver.c:110
_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:124
LocalMeta2Info
struct _LocalMeta2Info LocalMeta2Info
_LocalMeta2Info::archbase
char * archbase
Definition: metaserver.c:126
TRUE
#define TRUE
Definition: compat.h:11
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:129