Crossfire Server  1.75.0
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, "socket write failed: error code %d, disconnecting client\n",
414  WSAGetLastError());
415 #else
416  if (errno != EWOULDBLOCK) {
417  LOG(llevInfo, "socket write failed: %s, disconnecting client\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),
495  cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn, (long)(now-cst_tot.time_start),
496  cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, (long)(now-cst_lst.time_start));
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  of_close(&of);
543  } else {
544  LOG(llevError, "Unable to write to stat file: %s\n", settings.stat_file);
545  }
546  }
547 
548  reset_stats(&cst_lst);
550 }
551 #endif
Error, serious thing.
Definition: logger.h:11
short GetShort_String(const unsigned char *data)
Definition: lowlevel.cpp:258
void SockList_AddData(SockList *sl, const void *data, size_t len)
Adds a data block.
Definition: lowlevel.cpp:167
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
Definition: lowlevel.cpp:65
size_t len
Definition: newclient.h:726
char * stringbuffer_finish(StringBuffer *sb)
Deallocate the string buffer instance and return the string.
size_t SockList_Avail(const SockList *sl)
Returns the available bytes in a SockList instance.
Definition: lowlevel.cpp:246
Information.
Definition: logger.h:12
int GetInt_String(const unsigned char *data)
Basically does the reverse of SockList_AddInt, but on strings instead.
Definition: lowlevel.cpp:254
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, const int len)
This writes data to the socket.
Definition: lowlevel.cpp:399
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:727
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
int obytes
Definition: newclient.h:736
int maps_saved_total
Definition: logger.cpp:40
std::vector< archetype * > players
Definition: player.cpp:501
int nroffreeobjects
How many OBs allocated and free (free)
Definition: object.cpp:290
unsigned long max_ticktime
Definition: newclient.h:745
Defines various flags that both the new client and new server use.
void SockList_NullTerminate(SockList *sl)
Adds a NUL byte without changing the length.
Definition: lowlevel.cpp:237
player * first_player
First player.
Definition: init.cpp:106
Socket structure, represents a client-server connection.
Definition: newserver.h:93
int count_players(void)
Definition: metaserver.cpp:48
int maps_swapped_total
Definition: logger.cpp:41
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Definition: lowlevel.cpp:447
void SockList_ResetRead(SockList *sl)
Resets the length of the stored data for reading.
Definition: lowlevel.cpp:83
enum Sock_Status status
Definition: newserver.h:94
Global type definitions and header inclusions.
int SockList_ReadPacket(int fd, SockList *sl, int len)
This reads from fd and puts the data in sl.
Definition: lowlevel.cpp:275
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 8 bit length field.
Definition: lowlevel.cpp:179
void SockList_AddInt64(SockList *sl, uint64_t data)
Adds a 64 bit value.
Definition: lowlevel.cpp:140
int maps_loaded_total
Definition: logger.cpp:39
int events_total
Definition: events.cpp:8
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:129
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:108
unsigned long ticks_overtime
Definition: newclient.h:744
Socket_Info socket_info
Socket information.
Definition: init.cpp:52
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
int log_total
Definition: logger.cpp:43
#define MAP_SWAPPED
Map spaces have been saved to disk.
Definition: map.h:130
CS_Stats cst_lst
short max_conn
Maximum connections received.
Definition: newclient.h:737
int ibytes
ibytes, obytes are bytes in, out.
Definition: newclient.h:735
char * stat_file
Definition: global.h:335
This is a game-map.
Definition: map.h:320
void SockList_AddPrintf(SockList *sl, const char *format,...)
Adds a printf like formatted string.
Definition: lowlevel.cpp:202
void SockList_Reset(SockList *sl)
Resets the length of the stored data for writing.
Definition: lowlevel.cpp:74
mapstruct * first_map
First map.
Definition: init.cpp:107
struct Settings settings
Global settings.
Definition: init.cpp:139
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 16 bit length field.
Definition: lowlevel.cpp:191
unsigned long total_ticktime
Definition: newclient.h:746
void SockList_AddInt(SockList *sl, uint32_t data)
Adds a 32 bit value.
Definition: lowlevel.cpp:127
void reset_stats(struct CS_Stats *stats)
int allocated_sockets
Number of allocated items in init_sockets.
Definition: newserver.h:148
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:590
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
Definition: lowlevel.cpp:157
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Deallocates string buffer instance and appends its contents.
Definition: lowlevel.cpp:225
int object_count_active(void)
Objects statistics.
Definition: object.cpp:1768
static event_registration m
Definition: citylife.cpp:424
int nrofallocobjects
How many OBs allocated (free + used)
Definition: object.cpp:291
void SockList_AddChar(SockList *sl, unsigned char data)
Adds an 8 bit value.
Definition: lowlevel.cpp:106
void write_cs_stats(void)
Only for debugging purposes.
Definition: logger.h:13
CS_Stats cst_tot
Statistics for the last CS_LOGTIME seconds on the server.
Definition: newclient.h:734
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
unsigned long ticks
Definition: newclient.h:743
Functions for creating text output files.
void SockList_Init(SockList *sl)
Initializes the SockList instance.
Definition: lowlevel.cpp:55
time_t time_start
Definition: newclient.h:738
void SockList_AddShort(SockList *sl, uint16_t data)
Adds a 16 bit value.
Definition: lowlevel.cpp:116
One player.
Definition: player.h:107
static void SockList_Ensure(const SockList *sl, size_t size)
Checks that at least a given number of bytes is available in a SockList instance. ...
Definition: lowlevel.cpp:95
StringBuffer * buf
Definition: readable.cpp:1563
A buffer that will be expanded as content is added to it.
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:721
int global_events_total
Definition: events.cpp:7