Crossfire Server, Trunk
lowlevel.cpp
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 
26 #include "global.h"
27 
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 
35 #include "output_file.h"
36 #include "shared/newclient.h"
37 #include "sproto.h"
38 #include "stats.h"
39 
40 #ifdef WIN32
41 #include <winsock2.h>
42 #endif
43 
44 /***********************************************************************
45  *
46  * SockList functions/utilities
47  *
48  **********************************************************************/
49 
56  SockList_Reset(sl);
57 }
58 
66  (void)sl;
67 }
68 
75  sl->len = 2;
76 }
77 
84  sl->len = 0;
85 }
86 
95 static void SockList_Ensure(const SockList *sl, size_t size) {
96  if (sl->len+size > sizeof(sl->buf)) {
98  }
99 }
100 
106 void SockList_AddChar(SockList *sl, unsigned char data) {
107  SockList_Ensure(sl, 1);
108  sl->buf[sl->len++] = data;
109 }
110 
116 void SockList_AddShort(SockList *sl, uint16_t data) {
117  SockList_Ensure(sl, 2);
118  sl->buf[sl->len++] = (data>>8)&0xff;
119  sl->buf[sl->len++] = data&0xff;
120 }
121 
127 void SockList_AddInt(SockList *sl, uint32_t data) {
128  SockList_Ensure(sl, 4);
129  sl->buf[sl->len++] = (data>>24)&0xff;
130  sl->buf[sl->len++] = (data>>16)&0xff;
131  sl->buf[sl->len++] = (data>>8)&0xff;
132  sl->buf[sl->len++] = data&0xff;
133 }
134 
140 void SockList_AddInt64(SockList *sl, uint64_t data) {
141  SockList_Ensure(sl, 8);
142  sl->buf[sl->len++] = (char)((data>>56)&0xff);
143  sl->buf[sl->len++] = (char)((data>>48)&0xff);
144  sl->buf[sl->len++] = (char)((data>>40)&0xff);
145  sl->buf[sl->len++] = (char)((data>>32)&0xff);
146  sl->buf[sl->len++] = (char)((data>>24)&0xff);
147  sl->buf[sl->len++] = (char)((data>>16)&0xff);
148  sl->buf[sl->len++] = (char)((data>>8)&0xff);
149  sl->buf[sl->len++] = (char)(data&0xff);
150 }
151 
157 void SockList_AddString(SockList *sl, const char *data) {
158  SockList_AddData(sl, data, strlen(data));
159 }
160 
167 void SockList_AddData(SockList *sl, const void *data, size_t len) {
168  SockList_Ensure(sl, len);
169  memcpy(sl->buf+sl->len, data, len);
170  sl->len += len;
171 }
172 
179 void SockList_AddLen8Data(SockList *sl, const void *data, size_t len) {
180  assert(len <= 255);
181  SockList_AddChar(sl, len);
182  SockList_AddData(sl, data, len);
183 }
184 
191 void SockList_AddLen16Data(SockList *sl, const void *data, size_t len) {
192  assert(len <= 65535);
193  SockList_AddShort(sl, len);
194  SockList_AddData(sl, data, len);
195 }
196 
202 void SockList_AddPrintf(SockList *sl, const char *format, ...) {
203  size_t size;
204  int n;
205  va_list arg;
206 
207  size = sizeof(sl->buf)-sl->len;
208 
209  va_start(arg, format);
210  n = vsnprintf((char *)sl->buf+sl->len, size, format, arg);
211  va_end(arg);
212 
213  if (n <= -1 || (size_t)n >= size) {
214  LOG(llevError, "Truncating message exceeding MAXSOCKBUF. The message was:\n%s\n", sl->buf+sl->len);
215  }
216  sl->len += (size_t)n;
217 }
218 
226  char *p;
227 
228  p = stringbuffer_finish(sb);
229  SockList_AddString(sl, p);
230  free(p);
231 }
232 
238  SockList_Ensure(sl, 1);
239  sl->buf[sl->len] = '\0';
240 }
241 
246 size_t SockList_Avail(const SockList *sl) {
247  return sizeof(sl->buf)-sl->len;
248 }
249 
254 int GetInt_String(const unsigned char *data) {
255  return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
256 }
257 
258 short GetShort_String(const unsigned char *data) {
259  return ((data[0]<<8)+data[1]);
260 }
261 
262 /******************************************************************************
263  *
264  * Start of read routines.
265  *
266  ******************************************************************************/
267 
275 int SockList_ReadPacket(int fd, SockList *sl, int len) {
276  int stat, toread;
277 
278  /* We already have a partial packet */
279  if (sl->len < 2) {
280 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */
281 
282  stat = recv(fd, reinterpret_cast<char *>(sl->buf+sl->len), 2-sl->len, 0);
283 
284 #else
285  do {
286  stat = read(fd, sl->buf+sl->len, 2-sl->len);
287  } while ((stat == -1) && (errno == EINTR));
288 #endif
289  if (stat < 0) {
290  /* In non blocking mode, EAGAIN is set when there is no
291  * data available.
292  */
293 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */
294  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
295  if (WSAGetLastError() == WSAECONNRESET)
296  LOG(llevDebug, "Connection closed by client\n");
297  else {
298  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
299  }
300  return -1; /* kick this user! */
301  }
302 #else
303  if (errno == ECONNRESET) {
304  LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror(errno));
305  return -1;
306  }
307  if (errno != EAGAIN && errno != EWOULDBLOCK) {
308  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
309  }
310 #endif
311  return 0; /*Error */
312  }
313  if (stat == 0)
314  return -1;
315  sl->len += stat;
316 #ifdef CS_LOGSTATS
317  cst_tot.ibytes += stat;
318  cst_lst.ibytes += stat;
319 #endif
320  if (stat < 2)
321  return 0; /* Still don't have a full packet */
322  }
323  /* Figure out how much more data we need to read. Add 2 from the
324  * end of this - size header information is not included.
325  */
326  toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len;
327  if ((toread+(int)sl->len) >= len) {
328  LOG(llevError, "SockList_ReadPacket: Want to read more bytes than will fit in buffer (%lu>=%lu).\n", (unsigned long)toread+sl->len, (unsigned long)len);
329  /* Quick hack in case for 'oldsocketmode' input. If we are
330  * closing the socket anyways, then reading this extra 100 bytes
331  * shouldn't hurt.
332  */
333 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
334  stat = recv(fd, reinterpret_cast<char *>(sl->buf+2), 100, 0);
335 #else
336  stat = read(fd, sl->buf+2, 100);
337 #endif /* end win32 */
338  (void) stat; // Don't care how much we read; avoid complier warnings
339 
340  /* return error so the socket is closed */
341  return -1;
342  }
343  do {
344 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
345  stat = recv(fd, reinterpret_cast<char *>(sl->buf+sl->len), toread, 0);
346 #else
347  do {
348  stat = read(fd, sl->buf+sl->len, toread);
349  } while ((stat < 0) && (errno == EINTR));
350 #endif
351  if (stat < 0) {
352 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */
353  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
354  if (WSAGetLastError() == WSAECONNRESET)
355  LOG(llevDebug, "Connection closed by client\n");
356  else {
357  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
358  }
359  return -1; /* kick this user! */
360  }
361 #else
362  if (errno != EAGAIN && errno != EWOULDBLOCK) {
363  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
364  }
365 #endif
366  return 0; /*Error */
367  }
368  if (stat == 0)
369  return -1;
370  sl->len += stat;
371 #ifdef CS_LOGSTATS
372  cst_tot.ibytes += stat;
373  cst_lst.ibytes += stat;
374 #endif
375  toread -= stat;
376  if (toread == 0)
377  return 1;
378  if (toread < 0) {
379  LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n");
380  return 1;
381  }
382  } while (toread > 0);
383  return 0;
384 }
385 
386 /*******************************************************************************
387  *
388  * Start of write related routines.
389  *
390  ******************************************************************************/
391 
399 static void Write_To_Socket(socket_struct* ns, const unsigned char* buf, const int len) {
400  if (ns->status == Ns_Dead || !buf) {
401  LOG(llevDebug, "Write_To_Socket called with dead socket\n");
402  return;
403  }
404 
405 #ifdef WIN32
406  const int amt = send(ns->fd, reinterpret_cast<const char *>(buf), len, 0);
407 #else
408  const int amt = send(ns->fd, buf, len, 0);
409 #endif
410  if (amt < 0) { /* We got an error */
411 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */
412  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
413  LOG(llevInfo, "New socket write failed WTS (%d).\n",
414  WSAGetLastError());
415 #else
416  if (errno != EWOULDBLOCK) {
417  LOG(llevInfo, "New socket write failed WTS: %s\n",
418  strerror(errno));
419 #endif
420  ns->status = Ns_Dead;
421  return;
422  } else { /* EWOULDBLOCK */
423  LOG(llevError,
424  "Write_To_Socket: write would block; disconnecting. Try "
425  "increasing SOCKETBUFSIZE.\n");
426  ns->status = Ns_Dead;
427  return;
428  }
429  } else if (amt != len) {
430  LOG(llevError, "Write_To_Socket: write wrote less than requested; "
431  "disconnecting. Try increasing SOCKETBUFSIZE.\n");
432  ns->status = Ns_Dead;
433  return;
434  }
435 #ifdef CS_LOGSTATS
436  cst_tot.obytes += amt;
437  cst_lst.obytes += amt;
438 #endif
439 }
440 
448  if (ns->status == Ns_Dead || sl == NULL)
449  return;
450 
451  sl->buf[0] = ((sl->len-2)>>8)&0xFF;
452  sl->buf[1] = (sl->len-2)&0xFF;
453  Write_To_Socket(ns, sl->buf, sl->len);
454 }
455 
456 /******************************************************************************
457  *
458  * statistics logging functions.
459  *
460  ******************************************************************************/
461 
462 #ifdef CS_LOGSTATS
463 
464 static int count_all_players() {
465  int players = 0;
466  player *pl;
467  for (pl = first_player, players = 0; pl != NULL; pl = pl->next, players++);
468  return players;
469 }
470 
471 /* cst_tot is for the life of the server, cst_last is for the last series of
472  * stats
473  */
475 
476 void reset_stats(struct CS_Stats *stats) {
477  memset(stats, 0, sizeof(CS_Stats));
478  stats->time_start = time(NULL);
479 }
480 
481 #define STAT(name, fmt, val) fprintf(f, "%s %" fmt "\n", name, val);
482 
486 void write_cs_stats(void) {
487  time_t now = time(NULL);
488  int players_active = count_players();
489  int players_total = count_all_players();
490 
491  // Old statistics format logged to the log file
492  LOG(llevInfo, "STAT: players: %d active, %d total\n", players_active, players_total);
493  LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n",
494  ctime(&now),
497 
498  // Write OpenMetrics-formatted server stats to a file. This can be exported
499  // to tools like Prometheus to monitor the server.
500  if (settings.stat_file) {
501  OutputFile of;
502  FILE *f = of_open(&of, settings.stat_file);
503  if (f) {
504  // ticks can be zero due to idling, so prevent divide by zero
505  float ticks_over = cst_lst.ticks > 0 ? (float)cst_lst.ticks_overtime / cst_lst.ticks : 0;
506  float avg = cst_lst.ticks > 0 ? (float)cst_lst.total_ticktime / cst_lst.ticks : 0; // in us
507 
508  STAT("players_total", "d", players_total);
509  STAT("players_active", "d", players_active);
510  STAT("ticks_overtime_percent", "f", ticks_over * 100);
511  STAT("ticktime_max", "f", cst_lst.max_ticktime / 1e3); // in ms
512  STAT("ticktime_avg", "f", avg / 1e3); // in ms
513  STAT("bytes_in_total", "d", cst_tot.ibytes);
514  STAT("bytes_out_total", "d", cst_tot.obytes);
515 
516  int maps_in_memory = 0;
517  int maps_swapped = 0;
518  for (mapstruct *m = first_map; m != NULL; m = m->next) {
519  switch (m->in_memory) {
520  case MAP_IN_MEMORY:
521  maps_in_memory++;
522  break;
523  case MAP_SWAPPED:
524  maps_swapped++;
525  break;
526  }
527  }
528  STAT("maps_in_memory", "d", maps_in_memory);
529  STAT("maps_loaded_total", "d", maps_loaded_total);
530  STAT("maps_saved_total", "d", maps_saved_total);
531  STAT("maps_swapped", "d", maps_swapped);
532  STAT("maps_swapped_total", "d", maps_swapped_total);
533 
534  STAT("objects_alloc", "d", nrofallocobjects);
535  STAT("objects_free", "d", nroffreeobjects);
536  STAT("objects_active", "d", object_count_active());
537 
538  STAT("events_total", "d", events_total);
539  STAT("global_events_total", "d", global_events_total);
540 
541  STAT("log_total", "d", log_total);
542  } else {
543  LOG(llevError, "Unable to write to stat file: %s\n", settings.stat_file);
544  }
545  of_close(&of);
546  }
547 
550 }
551 #endif
output_file.h
global.h
first_player
player * first_player
Definition: init.cpp:106
settings
struct Settings settings
Definition: init.cpp:139
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.cpp:116
CS_Stats::ibytes
int ibytes
Definition: newclient.h:684
object_count_active
int object_count_active(void)
Definition: object.cpp:1783
CS_Stats::total_ticktime
unsigned long total_ticktime
Definition: newclient.h:695
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.cpp:447
llevError
@ llevError
Definition: logger.h:11
SockList_NullTerminate
void SockList_NullTerminate(SockList *sl)
Definition: lowlevel.cpp:237
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
player
Definition: player.h:105
socket_struct
Definition: newserver.h:89
Socket_Info::allocated_sockets
int allocated_sockets
Definition: newserver.h:144
if
if(!(yy_init))
Definition: loader.cpp:36428
maps_saved_total
int maps_saved_total
Definition: logger.cpp:40
socket_info
Socket_Info socket_info
Definition: init.cpp:49
maps_swapped_total
int maps_swapped_total
Definition: logger.cpp:41
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: lowlevel.cpp:258
SockList_AddData
void SockList_AddData(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:167
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.cpp:74
Settings::stat_file
char * stat_file
Definition: global.h:338
SockList_Ensure
static void SockList_Ensure(const SockList *sl, size_t size)
Definition: lowlevel.cpp:95
CS_Stats::ticks
unsigned long ticks
Definition: newclient.h:692
buf
StringBuffer * buf
Definition: readable.cpp:1565
Ns_Dead
@ Ns_Dead
Definition: newserver.h:67
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.cpp:65
CS_Stats::time_start
time_t time_start
Definition: newclient.h:687
log_total
int log_total
Definition: logger.cpp:43
m
static event_registration m
Definition: citylife.cpp:425
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:126
stringbuffer_finish
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.cpp:76
events_total
int events_total
Definition: events.cpp:10
stats.h
first_map
mapstruct * first_map
Definition: init.cpp:107
navar-midane_time.data
data
Definition: navar-midane_time.py:11
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.cpp:55
sproto.h
nrofallocobjects
int nrofallocobjects
Definition: object.cpp:291
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:179
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char data)
Definition: lowlevel.cpp:106
CS_Stats::obytes
int obytes
Definition: newclient.h:685
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
SockList::len
size_t len
Definition: newclient.h:675
global_events_total
int global_events_total
Definition: events.cpp:9
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:587
maps_loaded_total
int maps_loaded_total
Definition: logger.cpp:39
nroffreeobjects
int nroffreeobjects
Definition: object.cpp:290
StringBuffer
Definition: stringbuffer.cpp:25
cst_tot
CS_Stats cst_tot
llevInfo
@ llevInfo
Definition: logger.h:12
CS_Stats::max_ticktime
unsigned long max_ticktime
Definition: newclient.h:694
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.cpp:127
players
std::vector< archetype * > players
Definition: player.cpp:493
mapstruct
Definition: map.h:313
write_cs_stats
void write_cs_stats(void)
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
SockList_AddStringBuffer
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Definition: lowlevel.cpp:225
SockList_ReadPacket
int SockList_ReadPacket(int fd, SockList *sl, int len)
Definition: lowlevel.cpp:275
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.cpp:246
send
Definition: send.py:1
Write_To_Socket
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, const int len)
Definition: lowlevel.cpp:399
reset_stats
void reset_stats(struct CS_Stats *stats)
MAP_SWAPPED
#define MAP_SWAPPED
Definition: map.h:127
SockList_AddInt64
void SockList_AddInt64(SockList *sl, uint64_t data)
Definition: lowlevel.cpp:140
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.cpp:83
newclient.h
socket_struct::fd
int fd
Definition: newserver.h:91
count_players
int count_players(void)
Definition: metaserver.cpp:49
SockList_AddLen16Data
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.cpp:191
CS_Stats::ticks_overtime
unsigned long ticks_overtime
Definition: newclient.h:693
cst_lst
CS_Stats cst_lst
Definition: newclient.h:698
SockList::buf
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:676
CS_Stats
Definition: newclient.h:683
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.cpp:202
SockList
Definition: newclient.h:670
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.cpp:157
llevDebug
@ llevDebug
Definition: logger.h:13
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: lowlevel.cpp:254
CS_Stats::max_conn
short max_conn
Definition: newclient.h:686
OutputFile
Definition: output_file.h:41