Update of /cvsroot/netrek/metaserver/OLD
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32574
Added Files:
BecomeDaemon.c disp_info.c disp_new.c disp_old.c disp_web.c
main.c meta.h packets.h scan.c server.c
Log Message:
Metaserver source files before the addition of metaserver solicit and
improved web port handing.
--Carlos V.
--- NEW FILE: disp_web.c ---
/*
* disp_web.c - output in fancy new format
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "meta.h"
#define SEMI_VERBOSE /* not quite fully verbose */
/* "when" should be the length of time in seconds */
static char *
nice_time(when)
time_t when;
{
static char buf[64];
if (when < 120)
sprintf(buf, "%d seconds", when);
else if (when/60 < 120)
sprintf(buf, "%d minutes", when/60);
else if (when/3600 < 48)
sprintf(buf, "%d hours", when/3600);
else
sprintf(buf, "%d days", when/(3600*24));
return (buf);
}
int
display_web(idx, port_idx)
int idx, port_idx;
{
register int i;
USER *up;
SERVER *sp;
time_t now;
int srv, ago, *sorted;
char flagbuf[8], buf[128];
char *cp;
up = &users[idx];
up->data_size = up->buf_size = up->pos = 0;
now = time(0);
/* print header */
Uprintf(idx, "HTTP/1.0 200 OK\n");
Uprintf(idx, "Server: netrekmetarc/1.0\n");
Uprintf(idx, "MIME-version: 1.0\n");
Uprintf(idx, "Content-type: text/html\n\n");
Uprintf(idx, "\
<title>MetaServer II Server List</title>\n\
<h1>MetaServer II Server List</h1>\n");
Uprintf(idx, "<center><table border=2 cellpadding=6><tr>\
<td><h4>Server Host</h4></td>\
<td><h4>Port</h4></td>\
<td><h4>Minutes Ago</h4></td>\
<td><h4>Status</h4></td>\
<td><h4>Flags</h4></td>\
</tr>\n");
/* print player info */
sorted = sort_servers();
for (srv = 0; srv < server_count; srv++) {
/* (need to sort these?) */
sp = &servers[sorted[srv]];
if (sp->status == SS_WORKING) sp = &prev_info;
Uprintf(idx, "\n<tr><td>%s</td><td>%d</td>\n", sp->hostname, sp->port);
strcpy(flagbuf, " ");
for (i = 0; i < 6; i++)
if (sp->display_flags & flags[i].bit)
flagbuf[i] = flags[i].chr;
flagbuf[6] = sp->type[0]; /* hack in the type field */
ago = (now - sp->last_update) / 60;
switch (sp->status) {
case SS_INIT:
Uprintf(idx, "<td colspan=3>(no data yet)</td>\n");
break;
case SS_WORKING:
Uprintf(idx, "<td colspan=3>(scanning server now)</td>\n");
break;
case SS_NOCONN:
switch (sp->why_dead) {
case WD_CONN:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* No connection",
flagbuf);
break;
case WD_READ:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Bad read",
flagbuf);
break;
case WD_GARBAGE:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Garbaged read",
flagbuf);
break;
case WD_TIMEOUT:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Timed out",
flagbuf);
break;
case WD_FLOODING:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Flooding",
flagbuf);
break;
default:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "* Not responding",
flagbuf);
break;
}
break;
case SS_EMPTY:
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, "Nobody playing", flagbuf);
break;
case SS_OPEN:
sprintf(buf, "OPEN: %d player%s", sp->player_count,
(sp->player_count != 1) ? "s" : "");
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, buf, flagbuf);
break;
case SS_QUEUE:
sprintf(buf, "Wait queue: %d", sp->queue_size);
Uprintf(idx, "<td>%d</td><td>%s</td><td>%s</td>\n", ago, buf, flagbuf);
break;
default:
/* huh? */
fprintf(stderr, "Internal ERROR: strange state in disp_new\n");
break;
}
Uprintf(idx, "</tr>\n");
}
/* print footer */
Uprintf(idx, "</table></center>\n");
Uprintf(idx, "<p><i>(This is MetaServerII v%s on port %d, \n",
VERSION, ports[port_idx].port);
cp = ctime(&now);
cp[strlen(cp)-1] = '\0'; /* playing with fire? */
Uprintf(idx, "date in %s is %s, data spans %s, \n", location, cp,
nice_time(now-checktime));
Uprintf(idx, "send administrative requests to <a href=mailto:%s>%s</a>, ", admin, admin);
Uprintf(idx, "retry periods in minutes are ");
Uprintf(idx, "down:%d empty:%d open:%d queue:%d)</i><p>\n\n",
wait_noconn, wait_empty, wait_open, wait_queue);
return(0);
}
--- NEW FILE: disp_old.c ---
/*
* disp_old.c - output in format compatible with older metaserver
* (also has Uprintf() and sort_servers() routines)
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "meta.h"
int
display_old(idx, port_idx)
int idx;
{
USER *up;
SERVER *sp;
time_t now;
int srv, *sorted;
char buf[80], buf2[80];
up = &users[idx];
up->data_size = up->buf_size = up->pos = 0;
now = time(0);
/* print header */
Uprintf(idx, "\n\nMETASERVER-II %s, %s\n", VERSION, VDATE);
Uprintf(idx, "Server Check period: %d minutes\n", wait_open);
now = time(0);
/* print player info */
sorted = sort_servers();
for (srv = 0; srv < server_count; srv++) {
/* (need to sort these?) */
sp = &servers[sorted[srv]];
if (sp->status == SS_WORKING) sp = &prev_info;
sprintf(buf, "%s(%d)", sp->hostname, sp->port);
Uprintf(idx, "%-40.40s: ", buf);
strncpy(buf, ctime(&sp->last_update)+11, 8);
*(buf+8) = '\0';
strncpy(buf2, ctime(&sp->down_time)+4, 15);
*(buf2+15) = '\0';
switch (sp->status) {
case SS_INIT:
Uprintf(idx, "Haven't yet checked this one...\n");
break;
case SS_WORKING:
Uprintf(idx, "(scanning server now)\n");
break;
case SS_NOCONN:
/*
Uprintf(idx, "Couldn't connect as of %s\n", buf);
*/
Uprintf(idx, "Down as of %s\n", buf2);
break;
case SS_EMPTY:
case SS_OPEN:
Uprintf(idx, "%2d Players online at %s\n", sp->player_count, buf);
break;
case SS_QUEUE:
Uprintf(idx, "Wait queue %2d at %s\n", sp->queue_size, buf);
break;
default:
/* huh? */
fprintf(stderr, "Internal ERROR: strange state in disp_new\n");
break;
}
}
/* print footer */
Uprintf(idx, "\n\tThat's all!\n\n");
}
#define INITIAL_SIZE 64000
#define MAX_PRINT 256
#ifdef BAD_IDEA
# define EXPAND_SIZE 4096 /* must be > MAX_PRINT */
#endif
/*
* Print something to an expandable buffer. Works like sprintf(), mostly.
* Assumes that nothing we're passed will exceed MAX_PRINT bytes.
*
* When everybody has vsprintf, I'll do this right.
*/
void
Uprintf(idx, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
int idx;
char *format;
long arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7;
{
USER *up;
if (idx > 1024 || idx < 0) {
fprintf(stderr, "Internal ERROR: bogus idx %d\n", idx);
return;
}
up = &users[idx];
if (!up->data_size) {
if (up->buf != NULL) {
/* debug */
printf("HEY: Reassigning non-null buf for %d\n", idx);
/* endif */
log_msg("Reassigning non-null buf for %d\n", idx);
/* ought to free() here, but that might make us crash */
}
if ((up->buf = (char *) malloc(INITIAL_SIZE)) == NULL) {
fprintf(stderr, "malloc failure!\n");
log_msg("exit: malloc failure in Uprintf");
exit(1);
}
up->buf_size = INITIAL_SIZE;
}
if (up->buf_size - up->data_size < MAX_PRINT) {
#ifdef BAD_IDEA
/* buffer too small, realloc */
up->buf_size += EXPAND_SIZE;
if ((up->buf = (char *) realloc(up->buf, up->buf_size)) == NULL) {
fprintf(stderr, "realloc failure!\n");
log_msg("exit: realloc failure in Uprintf");
exit(1);
}
#else
/* ran out of room; bummer */
log_msg("NOTE: ran out of space in Uprintf()");
#endif
}
sprintf(up->buf + up->data_size, format, arg0, arg1, arg2, arg3, arg4,
arg5, arg6, arg7);
up->data_size += strlen(up->buf + up->data_size);
}
static int *alpha = NULL; /* alphabetical host sorting order */
/*
* Comparison function for qsort()
*/
static int
srvcmp(elmp1, elmp2)
int *elmp1, *elmp2;
{
register SERVER *s1, *s2;
register int elm1 = *elmp1, elm2 = *elmp2;
int st, pc, ho, po;
s1 = &servers[elm1];
if (s1->status == SS_WORKING) s1 = &prev_info;
s2 = &servers[elm2];
if (s2->status == SS_WORKING) s2 = &prev_info;
st = s1->status - s2->status;
if (s1->status == SS_QUEUE)
pc = s1->queue_size - s2->queue_size;
else if (s1->status == SS_NOCONN)
pc = 0;
else
pc = s1->player_count - s2->player_count;
ho = alpha[elm1] - alpha[elm2];
po = s1->port - s2->port;
/*if ((st > 0) || (!st && pc < 0) || (!st && !pc && ho > 0))*/
if ((st < 0) || (!st && pc > 0) || (!st && !pc && ho > 0) ||
(!st && !pc && !ho && po < 0))
return (1);
else
return (-1);
/* note there should not be a case in which two entries match exactly */
}
/*
* Returns a pointer to an array of ints, indicating the sorted order of
* the servers.
*
* Sorting order is:
* status
* player_count/queue_size
* alphabetical hostname
* port number
*
* IDEA: we can reduce the CPU usage by sorting the internal list by
* host and port number once, during initialization. However, this would
* require a stable sort on the other categories, which qs doesn't
* guarantee.
*/
int *
sort_servers()
{
static int *sorted = NULL;
register int i, j, *ip;
int k;
/* allocate some stuff first time through */
if (sorted == NULL) {
if (verbose) printf("Allocated sort_servers array\n"); /* DEBUG */
if ((sorted = (int *) malloc(server_count * sizeof(int))) == NULL) {
fprintf(stderr, "malloc failure in sort_servers\n");
log_msg("exit: malloc failure in sort_servers");
exit(1);
}
/* to ease the CPU load, compute the alphabetical order once */
if ((alpha = (int *) malloc(server_count * sizeof(int))) == NULL) {
fprintf(stderr, "malloc failure in sort_servers (alpha)\n");
log_msg("exit: malloc failure in sort_servers (alpha)");
exit(1);
}
for (i = 0, ip = sorted; i < server_count; i++, ip++)
*ip = i;
for (i = 0; i < server_count-1; i++)
for (j = i+1; j < server_count; j++)
if (strcmp(servers[sorted[i]].hostname,
servers[sorted[j]].hostname) > 0) {
k = sorted[i];
sorted[i] = sorted[j];
sorted[j] = k;
}
/* change sorted indices into parallel table of relative positions */
for (i = 0, ip = sorted; i < server_count; i++, ip++)
alpha[*ip] = i;
}
/* intialize the sorted array to itself */
for (i = 0, ip = sorted; i < server_count; i++, ip++)
*ip = i;
/* sort the array with the system qsort */
qsort((char *) sorted, server_count, sizeof(int), srvcmp);
return (sorted);
}
--- NEW FILE: disp_new.c ---
/*
* disp_new.c - output in fancy new format
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "meta.h"
#define SEMI_VERBOSE /* not quite fully verbose */
/* "when" should be the length of time in seconds */
char *
nice_time(when)
time_t when;
{
static char buf[64];
if (when < 120)
sprintf(buf, "%d seconds", when);
else if (when/60 < 120)
sprintf(buf, "%d minutes", when/60);
else if (when/3600 < 48)
sprintf(buf, "%d hours", when/3600);
else
sprintf(buf, "%d days", when/(3600*24));
return (buf);
}
int
display_new(idx, port_idx)
int idx, port_idx;
{
register int i;
USER *up;
SERVER *sp;
time_t now;
int srv, ago, *sorted;
char flagbuf[8], buf[128];
char *cp;
up = &users[idx];
up->data_size = up->buf_size = up->pos = 0;
now = time(0);
/* print header */
Uprintf(idx, "\n*** Connected to MetaServerII v%s on port %d ***\n",
VERSION, ports[port_idx].port);
cp = ctime(&now);
cp[strlen(cp)-1] = '\0'; /* playing with fire? */
Uprintf(idx, "Date in %s is %s (data spans %s)\n", location, cp,
nice_time(now-checktime));
Uprintf(idx, "E-mail administrative requests to %s.\n\n", admin);
Uprintf(idx, "Retry periods (in minutes): ");
Uprintf(idx, "down:%d empty:%d open:%d queue:%d\n\n",
wait_noconn, wait_empty, wait_open, wait_queue);
Uprintf(idx, "Other interesting MetaServerII ports: ");
for (i = 0; i < port_count; i++) {
if (ports[i].kind != DI_NEW)
Uprintf(idx, "%d ", ports[i].port);
}
Uprintf(idx, "\n\n");
Uprintf(idx, "%-39s %-8s %-3s %s\n", "", "", "Mins", "");
Uprintf(idx, "%-39s %-8s %-3s %-17s %s\n", "Server Host","Port","Ago",
"Status", "Flags");
Uprintf(idx, "--------------------------------------- ");
Uprintf(idx, "-------- ---- ----------------- -------\n");
/* print player info */
sorted = sort_servers();
for (srv = 0; srv < server_count; srv++) {
/* (need to sort these?) */
sp = &servers[sorted[srv]];
if (sp->status == SS_WORKING) sp = &prev_info;
Uprintf(idx, "-h %-36.36s -p %-5d ", sp->hostname, sp->port);
strcpy(flagbuf, " ");
for (i = 0; i < 6; i++)
if (sp->display_flags & flags[i].bit)
flagbuf[i] = flags[i].chr;
flagbuf[6] = sp->type[0]; /* hack in the type field */
ago = (now - sp->last_update) / 60;
switch (sp->status) {
case SS_INIT:
Uprintf(idx, "(no data yet)\n");
break;
case SS_WORKING:
Uprintf(idx, "(scanning server now)\n");
break;
case SS_NOCONN:
switch (sp->why_dead) {
case WD_CONN:
Uprintf(idx, "%3d %-17s %s\n", ago, "* No connection",
flagbuf);
break;
case WD_READ:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Bad read",
flagbuf);
break;
case WD_GARBAGE:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Garbaged read",
flagbuf);
break;
case WD_TIMEOUT:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Timed out",
flagbuf);
break;
case WD_FLOODING:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Flooding",
flagbuf);
break;
default:
Uprintf(idx, "%3d %-17s %s\n", ago, "* Not responding",
flagbuf);
break;
}
break;
case SS_EMPTY:
Uprintf(idx, "%3d %-17s %s\n", ago, "Nobody playing", flagbuf);
break;
case SS_OPEN:
sprintf(buf, "OPEN: %d player%s", sp->player_count,
(sp->player_count != 1) ? "s" : "");
Uprintf(idx, "%3d %-17s %s\n", ago, buf, flagbuf);
break;
case SS_QUEUE:
sprintf(buf, "Wait queue: %d", sp->queue_size);
Uprintf(idx, "%3d %-17s %s\n", ago, buf, flagbuf);
break;
default:
/* huh? */
fprintf(stderr, "Internal ERROR: strange state in disp_new\n");
break;
}
}
/* print footer */
Uprintf(idx, "\nThat's it!\n");
return(0);
}
/*
* Do the "verbose" output format
*/
int
display_verbose(idx, port_idx)
int idx;
{
USER *up;
SERVER *sp;
time_t now;
int srv, ago, *sorted;
char buf[80];
int i;
up = &users[idx];
up->data_size = up->buf_size = up->pos = 0;
now = time(0);
/* print header */
Uprintf(idx, "\n*** Connected to MetaServerII v%s on port %d ***\n",
VERSION, ports[port_idx].port);
Uprintf(idx, "Date in %s is %s", location, ctime(&now));
Uprintf(idx, "E-mail administrative requests to %s.\n", admin);
Uprintf(idx, "Up continuously for %s", nice_time(now-uptime));
if (uptime != checktime)
Uprintf(idx, " (data spans %s).\n", nice_time(now-checktime));
else
Uprintf(idx, ".\n");
Uprintf(idx, "Retry periods (in minutes): ");
Uprintf(idx, "down:%d empty:%d open:%d queue:%d\n\n",
wait_noconn, wait_empty, wait_open, wait_queue);
Uprintf(idx, "Other interesting MetaServerII ports:\n");
for (i = 0; i < port_count; i++)
if (ports[i].kind != DI_VERBOSE)
Uprintf(idx, "\t%d - %s\n", ports[i].port, ports[i].desc);
Uprintf(idx, "\n");
/* print player info */
sorted = sort_servers();
for (srv = 0; srv < server_count; srv++) {
/* (need to sort these?) */
sp = &servers[sorted[srv]];
if (sp->status == SS_WORKING) sp = &prev_info;
#ifdef SEMI_VERBOSE
if (sp->status == SS_NOCONN || sp->status == SS_EMPTY) continue;
#endif
Uprintf(idx, "Server: %s [%c], port %d", sp->hostname,
sp->type[0], sp->port);
/* print comment if we've got one */
if (strlen(sp->comment) > 0)
Uprintf(idx, " (%s)\n", sp->comment);
else
Uprintf(idx, "\n");
ago = (now - sp->last_update) / 60;
strncpy(buf, ctime(&sp->last_update)+11, 8);
*(buf+8) = '\0';
if (sp->status != SS_INIT && sp->status != SS_WORKING)
Uprintf(idx, "Last checked at %s (%d minute%s ago).\n", buf, ago,
(ago == 1) ? "" : "s");
Uprintf(idx, "\n");
switch (sp->status) {
case SS_INIT:
Uprintf(idx, "This server has not been examined yet.\n");
break;
case SS_WORKING:
Uprintf(idx,
"Information for this server is being retrieved right now.\n");
break;
case SS_NOCONN:
Uprintf(idx, "Index %d\n", sp->why_dead);
switch (sp->why_dead) {
case WD_UNKNOWN:
Uprintf(idx, "The server is not responding.\n");
break;
case WD_CONN:
Uprintf(idx, "Unable to connect to server.\n");
break;
case WD_READ:
Uprintf(idx, "Got errors reading from server.\n");
break;
case WD_GARBAGE:
Uprintf(idx, "Server sent garbled data.\n");
break;
case WD_TIMEOUT:
Uprintf(idx, "Timed out waiting for response.\n");
break;
case WD_FLOODING:
Uprintf(idx, "Flooding metaserver, temporarily delisted.\n");
break;
default:
log_msg("got weird sp->why_dead (%d) in display_verbose()",
sp->why_dead);
Uprintf(idx, "The server is not responding.\n");
}
break;
case SS_EMPTY:
Uprintf(idx, "Nobody is playing.\n");
break;
case SS_OPEN:
if (sp->player_count == 1)
Uprintf(idx, "There is 1 person playing here.\n");
else
Uprintf(idx, "There are %d people playing here.\n",
sp->player_count);
print_players(idx, sorted[srv]);
break;
case SS_QUEUE:
if (sp->queue_size == 1)
Uprintf(idx, "There is a wait queue with 1 person on it.\n");
else
Uprintf(idx, "There is a wait queue with %d people on it.\n",
sp->queue_size);
print_players(idx, sorted[srv]);
break;
default:
/* huh? */
fprintf(stderr, "Internal ERROR: strange state in disp_verbose\n");
break;
}
Uprintf(idx, "\n\n");
}
/* print footer */
Uprintf(idx, "\nThat's it!\n\n");
return (0);
}
#define NOBODY 0x0
#define IND 0x0
#define FED 0x1
#define ROM 0x2
#define KLI 0x4
#define ORI 0x8
#define ALLTEAM (FED|ROM|KLI|ORI)
#define MAXTEAM (ORI)
#define NUMTEAM 4
#define NUMRANKS 9
#define PNUMRANKS 18
struct rank {
char *name;
};
static struct rank ranks[NUMRANKS+1] = {
{ "Ensign"},
{ "Lieutenant"},
{ "Lt. Cmdr."},
{ "Commander"},
{ "Captain"},
{ "Flt. Capt."},
{ "Commodore"},
{ "Rear Adm."},
{ "Admiral"},
{ "(unknown)"}
};
static struct rank pranks[PNUMRANKS] =
{
{ "Recruit"},
{ "Specialist"},
{ "Cadet"},
{ "Midshipman"},
{ "Ensn., J.G."},
{ "Ensign"},
{ "Lt., J.G."},
{ "Lieutenant"},
{ "Lt. Cmdr."},
{ "Commander"},
{ "Captain"},
{ "Fleet Capt."},
{ "Commodore"},
{ "Moff"},
{ "Grand Moff"},
{ "Rear Adml."},
{ "Admiral"},
{ "Grand Adml."}
};
/*static*/ int
print_players(idx, srv)
int idx, srv;
{
SERVER *sp;
PLAYER *j;
int i, k, t, ci;
int plcount, fromm;
char namebuf[18];
Uprintf(idx, "\n");
sp = &servers[srv];
/* first, scan list */
plcount = 0;
for (i = 0; i < sp->max_players; i++)
if (sp->players[i].p_status!=PFREE && sp->players[i].p_status!=POUTFIT)
plcount++;
if (!plcount) {
Uprintf(idx, "(Player list not available.)\n");
return (0);
}
fromm = (sp->status == SS_QUEUE);
if (fromm)
Uprintf(idx, "(player list from port %d; may not be 100%% accurate)\n",
sp->port-1);
/* this was adapted from ck_players */
Uprintf(idx, " Type Rank Name Login\n");
/* for (k = IND; k <= ORI; k<<=1) {*/
for (t = 0; t <= NUMTEAM; t++) {
if (!t) k = 0;
else k = 1 << (t-1);
for (i = 0, j = &sp->players[i]; i < sp->max_players; i++, j++) {
if ((j->p_status != PFREE && j->p_status != POUTFIT)
&& j->p_team == k) {
/* ugly way to ferret out trailing spaces */
strncpy(namebuf+1, j->p_name, 16);
namebuf[0] = ' ';
namebuf[16] = namebuf[17] = '\0';
if (namebuf[strlen(namebuf)-1] == ' ') {
namebuf[0] = '"';
namebuf[strlen(namebuf)] = '"';
}
for (ci = 0; ci < 18; ci++) /* weed out control chars */
if (namebuf[ci] && ((namebuf[ci] & 0x7f) < 0x20))
namebuf[ci] = '^';
Uprintf(idx, "%c%c %s %-9.9s %-18.18s %s@%s\n",
teamlet[j->p_team],
shipnos[i],
/*classes[j->ship_type],*/
(sp->type[0] != 'P')
? ((j->ship_type<NUM_TYPES) ?
classes[j->ship_type]:"!!")
: ((j->ship_type<PNUM_TYPES) ?
pclasses[j->ship_type]:"!!"),
(sp->type[0] != 'P')
? ((j->p_rank<NUMRANKS) ?
ranks[j->p_rank].name:"(unknown)")
: ((j->p_rank<PNUMRANKS) ?
pranks[j->p_rank].name:"(unknown)"),
namebuf, /*j->p_name,*/
j->p_login, j->p_monitor );
}
}
}
}
--- NEW FILE: packets.h ---
/*
* Include file for socket I/O xtrek.
*
* Kevin P. Smith 1/29/89
*/
/* the following typedefs allow portability to machines without the
ubiquitous 32-bit architecture (KSR1, Cray, DEC Alpha) */
typedef unsigned int CARD32;
typedef unsigned short CARD16;
typedef unsigned char CARD8;
typedef int LONG;
typedef int INT32;
typedef short INT16;
#if __STDC__ || defined(sgi) || defined(AIXV3)
typedef signed char INT8;
#else
/* stupid compilers */
typedef char INT8;
#endif
/* packets sent from xtrek server to remote client */
#define SP_MESSAGE 1
#define SP_PLAYER_INFO 2 /* general player info not elsewhere */
#define SP_KILLS 3 /* # kills a player has */
#define SP_PLAYER 4 /* x,y for player */
#define SP_TORP_INFO 5 /* torp status */
#define SP_TORP 6 /* torp location */
#define SP_PHASER 7 /* phaser status and direction */
#define SP_PLASMA_INFO 8 /* player login information */
#define SP_PLASMA 9 /* like SP_TORP */
#define SP_WARNING 10 /* like SP_MESG */
#define SP_MOTD 11 /* line from .motd screen */
#define SP_YOU 12 /* info on you? */
#define SP_QUEUE 13 /* estimated loc in queue? */
#define SP_STATUS 14 /* galaxy status numbers */
#define SP_PLANET 15 /* planet armies & facilities */
#define SP_PICKOK 16 /* your team & ship was accepted */
#define SP_LOGIN 17 /* login response */
#define SP_FLAGS 18 /* give flags for a player */
#define SP_MASK 19 /* tournament mode mask */
#define SP_PSTATUS 20 /* give status for a player */
#define SP_BADVERSION 21 /* invalid version number */
#define SP_HOSTILE 22 /* hostility settings for a player */
#define SP_STATS 23 /* a player's statistics */
#define SP_PL_LOGIN 24 /* new player logs in */
#define SP_RESERVED 25 /* for future use */
#define SP_PLANET_LOC 26 /* planet name, x, y */
/* paradise packets */
#define SP_SCAN 27 /* scan packet */
#define SP_UDP_REPLY 28 /* notify client of UDP status */
#define SP_SEQUENCE 29 /* sequence # packet */
#define SP_SC_SEQUENCE 30 /* this trans is semi-critical info */
#define SP_RSA_KEY 31 /* RSA data packet */
#define SP_MOTD_PIC 32 /* motd bitmap pictures */
#define SP_STATS2 33 /* new stats packet*/
#define SP_STATUS2 34 /* new status packet*/
#define SP_PLANET2 35 /* new planet packet*/
#define SP_NEW_MOTD 36 /* New MOTD info notification uses */
#define SP_THINGY 37 /* thingy location */
#define SP_THINGY_INFO 38 /* thingy status */
#define SP_SHIP_CAP 39 /* ship capabilities */
/* packets sent from remote client to xtrek server */
#define CP_MESSAGE 1 /* send a message */
#define CP_SPEED 2 /* set speed */
#define CP_DIRECTION 3 /* change direction */
#define CP_PHASER 4 /* phaser in a direction */
#define CP_PLASMA 5 /* plasma (in a direction) */
#define CP_TORP 6 /* fire torp in a direction */
#define CP_QUIT 7 /* self destruct */
#define CP_LOGIN 8 /* log in (name, password) */
#define CP_OUTFIT 9 /* outfit to new ship */
#define CP_WAR 10 /* change war status */
#define CP_PRACTR 11 /* create practice robot? */
#define CP_SHIELD 12 /* raise/lower sheilds */
#define CP_REPAIR 13 /* enter repair mode */
#define CP_ORBIT 14 /* orbit planet/starbase */
#define CP_PLANLOCK 15 /* lock on planet */
#define CP_PLAYLOCK 16 /* lock on player */
#define CP_BOMB 17 /* bomb a planet */
#define CP_BEAM 18 /* beam armies up/down */
#define CP_CLOAK 19 /* cloak on/off */
#define CP_DET_TORPS 20 /* detonate enemy torps */
#define CP_DET_MYTORP 21 /* detonate one of my torps */
#define CP_COPILOT 22 /* toggle copilot mode */
#define CP_REFIT 23 /* refit to different ship type */
#define CP_TRACTOR 24 /* tractor on/off */
#define CP_REPRESS 25 /* pressor on/off */
#define CP_COUP 26 /* coup home planet */
#define CP_SOCKET 27 /* new socket for reconnection */
#define CP_OPTIONS 28 /* send my options to be saved */
#define CP_BYE 29 /* I'm done! */
#define CP_DOCKPERM 30 /* set docking permissions */
#define CP_UPDATES 31 /* set number of usecs per update */
#define CP_RESETSTATS 32 /* reset my stats packet */
#define CP_RESERVED 33 /* for future use */
#define SOCKVERSION 4
struct packet_handler {
int size;
int (*handler)();
};
struct mesg_spacket {
char type; /* SP_MESSAGE */
unsigned char m_flags;
unsigned char m_recpt;
unsigned char m_from;
char mesg[80];
};
struct plyr_info_spacket {
char type; /* SP_PLAYER_INFO */
char pnum;
char shiptype;
char team;
};
struct plyr_login_spacket {
char type; /* SP_PL_LOGIN */
char pnum;
char rank;
char pad1;
char name[16];
char monitor[16];
char login[16];
};
struct hostile_spacket {
char type; /* SP_HOSTILE */
char pnum;
char war;
char hostile;
};
struct stats_spacket {
char type; /* SP_STATS */
char pnum;
char pad1;
char pad2;
long tkills; /* Tournament kills */
long tlosses; /* Tournament losses */
long kills; /* overall */
long losses; /* overall */
long tticks; /* ticks of tournament play time */
long tplanets; /* Tournament planets */
long tarmies; /* Tournament armies */
long sbkills; /* Starbase kills */
long sblosses; /* Starbase losses */
long armies; /* non-tourn armies */
long planets; /* non-tourn planets */
long maxkills; /* max kills as player * 100 */
long sbmaxkills; /* max kills as sb * 100 */
};
struct flags_spacket {
char type; /* SP_FLAGS */
char pnum; /* whose flags are they? */
char pad1;
char pad2;
unsigned flags;
};
struct kills_spacket {
char type; /* SP_KILLS */
char pnum;
char pad1;
char pad2;
unsigned kills; /* where 1234=12.34 kills and 0=0.00 kills */
};
struct player_spacket {
char type; /* SP_PLAYER */
char pnum;
unsigned char dir;
char speed;
LONG x,y;
};
struct torp_info_spacket {
char type; /* SP_TORP_INFO */
char war;
char status; /* TFREE, TDET, etc... */
char pad1; /* pad needed for cross cpu compatibility */
short tnum;
short pad2;
};
struct torp_spacket {
char type; /* SP_TORP */
unsigned char dir;
short tnum;
long x,y;
};
struct phaser_spacket {
char type; /* SP_PHASER */
char pnum;
char status; /* PH_HIT, etc... */
unsigned char dir;
long x,y;
long target;
};
struct you_spacket {
char type; /* SP_YOU */
char pnum; /* Guy needs to know this... */
char hostile;
char swar;
char armies;
char pad1;
char pad2;
char pad3;
unsigned flags;
LONG damage;
LONG shield;
LONG fuel;
short etemp;
short wtemp;
short whydead;
short whodead;
};
struct status_spacket {
char type; /* SP_STATUS */
char tourn;
char pad1;
char pad2;
unsigned armsbomb;
unsigned planets;
unsigned kills;
unsigned losses;
unsigned time;
unsigned long timeprod;
};
struct warning_spacket {
char type; /* SP_WARNING */
char pad1;
char pad2;
char pad3;
char mesg[80];
};
struct planet_spacket {
char type; /* SP_PLANET */
char pnum;
char owner;
char info;
short flags;
short pad2;
long armies;
};
struct torp_cpacket {
char type; /* CP_TORP */
unsigned char dir; /* direction to fire torp */
char pad1;
char pad2;
};
struct phaser_cpacket {
char type; /* CP_PHASER */
unsigned char dir;
char pad1;
char pad2;
};
struct speed_cpacket {
char type; /* CP_SPEED */
char speed;
char pad1;
char pad2;
};
struct dir_cpacket {
char type; /* CP_DIRECTION */
unsigned char dir;
char pad1;
char pad2;
};
struct shield_cpacket {
char type; /* CP_SHIELD */
char state; /* up/down */
char pad1;
char pad2;
};
struct repair_cpacket {
char type; /* CP_REPAIR */
char state; /* on/off */
char pad1;
char pad2;
};
struct orbit_cpacket {
char type; /* CP_ORBIT */
char state; /* on/off */
char pad1;
char pad2;
};
struct practr_cpacket {
char type; /* CP_PRACTR */
char pad1;
char pad2;
char pad3;
};
struct bomb_cpacket {
char type; /* CP_BOMB */
char state;
char pad1;
char pad2;
};
struct beam_cpacket {
char type; /* CP_BEAM */
char state;
char pad1;
char pad2;
};
struct cloak_cpacket {
char type; /* CP_CLOAK */
char state;
char pad1;
char pad2;
};
struct det_torps_cpacket {
char type; /* CP_DET_TORPS */
char pad1;
char pad2;
char pad3;
};
struct copilot_cpacket {
char type; /* CP_COPLIOT */
char state;
char pad1;
char pad2;
};
struct queue_spacket {
char type; /* SP_QUEUE */
char pad1;
short pos;
};
struct outfit_cpacket {
char type; /* CP_OUTFIT */
char team;
char ship;
char pad1;
};
struct pickok_spacket {
char type; /* SP_PICKOK */
char state;
char pad2;
char pad3;
};
struct login_cpacket {
char type; /* CP_LOGIN */
char query;
char pad2;
char pad3;
char name[16];
char password[16];
char login[16];
};
struct login_spacket {
char type; /* SP_LOGIN */
char accept; /* 1/0 */
char pad2;
char pad3;
long flags;
char keymap[96];
};
struct tractor_cpacket {
char type; /* CP_TRACTOR */
char state;
char pnum;
char pad2;
};
struct repress_cpacket {
char type; /* CP_REPRESS */
char state;
char pnum;
char pad2;
};
struct det_mytorp_cpacket {
char type; /* CP_DET_MYTORP */
char pad1;
short tnum;
};
struct war_cpacket {
char type; /* CP_WAR */
char newmask;
char pad1;
char pad2;
};
struct refit_cpacket {
char type; /* CP_REFIT */
char ship;
char pad1;
char pad2;
};
struct plasma_cpacket {
char type; /* CP_PLASMA */
unsigned char dir;
char pad1;
char pad2;
};
struct plasma_info_spacket {
char type; /* SP_PLASMA_INFO */
char war;
char status; /* TFREE, TDET, etc... */
char pad1; /* pad needed for cross cpu compatibility */
short pnum;
short pad2;
};
struct plasma_spacket {
char type; /* SP_PLASMA */
char pad1;
short pnum;
long x,y;
};
struct playlock_cpacket {
char type; /* CP_PLAYLOCK */
char pnum;
char pad1;
char pad2;
};
struct planlock_cpacket {
char type; /* CP_PLANLOCK */
char pnum;
char pad1;
char pad2;
};
struct coup_cpacket {
char type; /* CP_COUP */
char pad1;
char pad2;
char pad3;
};
struct pstatus_spacket {
char type; /* SP_PSTATUS */
char pnum;
char status;
char pad1;
};
struct motd_spacket {
char type; /* SP_MOTD */
char pad1;
char pad2;
char pad3;
char line[80];
};
struct quit_cpacket {
char type; /* CP_QUIT */
char pad1;
char pad2;
char pad3;
};
struct mesg_cpacket {
char type; /* CP_MESSAGE */
char group;
char indiv;
char pad1;
char mesg[80];
};
struct mask_spacket {
char type; /* SP_MASK */
char mask;
char pad1;
char pad2;
};
struct socket_cpacket {
char type; /* CP_SOCKET */
char version;
char pad2;
char pad3;
unsigned socket;
};
struct options_cpacket {
char type; /* CP_OPTIONS */
char pad1;
char pad2;
char pad3;
unsigned flags;
char keymap[96];
};
struct bye_cpacket {
char type; /* CP_BYE */
char pad1;
char pad2;
char pad3;
};
struct badversion_spacket {
char type; /* SP_BADVERSION */
char why;
char pad2;
char pad3;
};
struct dockperm_cpacket {
char type; /* CP_DOCKPERM */
char state;
char pad2;
char pad3;
};
struct updates_cpacket {
char type; /* CP_UPDATES */
char pad1;
char pad2;
char pad3;
unsigned usecs;
};
struct resetstats_cpacket {
char type; /* CP_RESETSTATS */
char verify; /* 'Y' - just to make sure he meant it */
char pad2;
char pad3;
};
struct reserved_spacket {
char type; /* SP_RESERVED */
char pad1;
char pad2;
char pad3;
char data[16];
};
struct reserved_cpacket {
char type; /* CP_RESERVED */
char pad1;
char pad2;
char pad3;
char data[16];
char resp[16];
};
struct planet_loc_spacket {
char type; /* SP_PLANET_LOC */
char pnum;
char pad2;
char pad3;
long x;
long y;
char name[16];
};
/* paradise packets */
struct scan_spacket { /* ATM */
INT8 type; /* SP_SCAN */
INT8 pnum;
INT8 success;
INT8 pad1;
INT32 p_fuel;
INT32 p_armies;
INT32 p_shield;
INT32 p_damage;
INT32 p_etemp;
INT32 p_wtemp;
};
struct udp_reply_spacket { /* UDP */
INT8 type; /* SP_UDP_REPLY */
INT8 reply;
INT8 pad1;
INT8 pad2;
INT32 port;
};
struct sequence_spacket { /* UDP */
INT8 type; /* SP_SEQUENCE */
INT8 pad1;
CARD16 sequence;
};
struct sc_sequence_spacket { /* UDP */
INT8 type; /* SP_CP_SEQUENCE */
INT8 pad1;
CARD16 sequence;
};
/*
* Game configuration.
* KAO 1/23/93
*/
struct ship_cap_spacket { /* Server configuration of client */
INT8 type; /* screw motd method */
INT8 operation; /* 0 = add/change a ship, 1 = remove a ship */
INT16 s_type; /* SP_SHIP_CAP */
INT16 s_torpspeed;
INT16 s_phaserrange;
INT32 s_maxspeed;
INT32 s_maxfuel;
INT32 s_maxshield;
INT32 s_maxdamage;
INT32 s_maxwpntemp;
INT32 s_maxegntemp;
INT16 s_width;
INT16 s_height;
INT16 s_maxarmies;
INT8 s_letter;
INT8 pad2;
INT8 s_name[16];
INT8 s_desig1;
INT8 s_desig2;
INT16 s_bitmap;
};
struct motd_pic_spacket {
INT8 type; /* SP_MOTD_PIC */
INT8 pad1;
INT16 x, y, page;
INT16 width, height;
INT8 bits[1016];
};
/* This is used to send paradise style stats */
struct stats_spacket2 {
INT8 type; /* SP_STATS2 */
INT8 pnum;
INT8 pad1;
INT8 pad2;
INT32 genocides; /* number of genocides participated in */
INT32 maxkills; /* max kills ever * 100 */
INT32 di; /* destruction inflicted for all time * 100 */
INT32 kills; /* Kills in tournament play */
INT32 losses; /* Losses in tournament play */
INT32 armsbomb; /* Tournament armies bombed */
INT32 resbomb; /* resources bombed off */
INT32 dooshes; /* armies killed while being carried */
INT32 planets; /* Tournament planets conquered */
INT32 tticks; /* Tournament ticks */
/* SB/WB/JS stats are entirely separate */
INT32 sbkills; /* Kills as starbase */
INT32 sblosses; /* Losses as starbase */
INT32 sbticks; /* Time as starbase */
INT32 sbmaxkills; /* Max kills as starbase * 100 */
INT32 wbkills; /* Kills as warbase */
INT32 wblosses; /* Losses as warbase */
INT32 wbticks; /* Time as warbase */
INT32 wbmaxkills; /* Max kills as warbase * 100 */
INT32 jsplanets; /* planets assisted with in JS */
INT32 jsticks; /* ticks played as a JS */
INT32 rank; /* Ranking of the player */
INT32 royal; /* royaly, specialty, rank */
};
/*status info for paradise stats */
struct status_spacket2 {
INT8 type; /* SP_STATUS2 */
INT8 tourn;
INT8 pad1;
INT8 pad2;
CARD32 dooshes; /* total number of armies dooshed */
CARD32 armsbomb; /* all t-mode armies bombed */
CARD32 resbomb; /* resources bombed */
CARD32 planets; /* all t-mode planets taken */
CARD32 kills; /* all t-mode kills made */
CARD32 losses; /* all t-mode losses */
CARD32 sbkills; /* total kills in SB's */
CARD32 sblosses; /* total losses in Sb's */
CARD32 sbtime; /* total time in SB's */
CARD32 wbkills; /* kills in warbases */
CARD32 wblosses; /* losses in warbases */
CARD32 wbtime; /* total time played in wb's */
CARD32 jsplanets; /* total planets taken by jump ships */
CARD32 jstime; /* total time in a jump ship */
CARD32 time; /* t mode time in this game */
CARD32 timeprod; /* t-mode ship ticks--sort of like */
};
/*planet info for a paradise planet*/
struct planet_spacket2 {
INT8 type; /* SP_PLANET2 */
INT8 pnum; /* planet number */
INT8 owner; /* owner of the planet */
INT8 info; /* who has touched planet */
INT32 flags; /* planet's flags */
INT32 timestamp; /* timestamp for info on planet */
INT32 armies; /* armies on the planet */
};
struct obvious_packet {
INT8 type; /* SP_NEW_MOTD */
INT8 pad1; /* CP_ASK_MOTD */
};
struct thingy_info_spacket {
INT8 type; /* SP_TORP_INFO */
INT8 war;
INT16 shape; /* a thingy_types */
INT16 tnum;
INT16 owner;
};
struct thingy_spacket {
INT8 type; /* SP_TORP */
CARD8 dir;
INT16 tnum;
INT32 x, y;
};
struct rsa_key_spacket {
INT8 type; /* SP_RSA_KEY */
INT8 pad1;
INT8 pad2;
INT8 pad3;
CARD8 data[32];
};
struct ping_spacket {
INT8 type; /* SP_PING */
CARD8 number; /* id (ok to wrap) */
CARD16 lag; /* delay of last ping in ms */
CARD8 tloss_sc; /* total loss server-client 0-100% */
CARD8 tloss_cs; /* total loss client-server 0-100% */
CARD8 iloss_sc; /* inc. loss server-client 0-100% */
CARD8 iloss_cs; /* inc. loss client-server 0-100% */
};
--- NEW FILE: meta.h ---
/*
* meta.h - global vars and configuration stuff
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
/*#define DEBUG*/
/* files - adjust to taste */
#define LOGFILE "metalog"
#define CONFIGFILE "metarc"
#define CHECKFILE "metackp"
/* some time constants, in seconds */
/* MAX_BUSY 20 BUSY_EXTEN 20 */
#define MAX_BUSY 40 /* max time to wait for a server */
#define BUSY_EXTEN 40 /* extension time for distant servers */
#define TIME_RES 40 /* how often do we wake up */
/*#define START_DIST 40 /* space initial checks by this much */
#define START_DIST 5
#define MAX_USER 10 /* after this long, bye bye user */
#define MAX_CKP_AGE 60*10 /* reject ckp files after 10 mins */
/* flood controls */
#define MAX_CALLS 3 /* you get this many calls... */
#define FLOOD_TIME 10 /* ...over this many seconds. */
/* server flood controls */
#define MIN_UREAD_TIME 60
/*
* end of configurable stuff
*/
/* what version we are, and date for old format */
#define VERSION "1.0.3"
#define VDATE "6/1/93"
/* display flags */
typedef struct {
int bit;
char chr;
char *word;
} FLAG;
#define MAX_FLAG 8 /* increase to 32 as needed */
extern FLAG flags[MAX_FLAG];
#define DF_HAD_TMODE 0x0001
#define DF_ALWAYS_DEAD 0x0002
#define DF_RSA 0x0004
#define DF_TYPE 0x0040 /* not really a flag */
#define TRUE 1
#define FALSE 0
/* deal with funky SVR3 signal mechanism */
#ifdef _UTS
# define SYSV
#endif
#if defined (SYSV) && !defined(hpux)
# define SIGNAL(sig,action) sigset(sig, action)
#else
# define SIGNAL(sig,action) signal(sig, action)
#endif
/* assorted library routines */
extern char *malloc(),
*realloc();
/* player info */
typedef struct {
int p_status;
int p_rank;
int p_team;
char p_mapchars[2];
char p_name[16];
char p_login[16];
char p_monitor[16];
int ship_type;
} PLAYER;
#define MAX_PLAYER 32 /* must be >= server MAXPLAYER */
#define NUM_TYPES 8 /* #of different ship types */
#define PNUM_TYPES 16 /* #of different ship types */
/* global structure declarations */
typedef enum { SS_WORKING, SS_QUEUE, SS_OPEN, SS_EMPTY, SS_NOCONN, SS_INIT }
SERVER_STATUS; /* note: this affects sorting order */
typedef enum { WD_UNKNOWN, WD_CONN, WD_READ, WD_GARBAGE, WD_TIMEOUT,
WD_FLOODING } WHY_DEAD;
typedef enum { PS_PORTM, PS_PORT } PROBE_STATE;
typedef enum { CS_CLOSED, CS_CONNECTING, CS_CONNECTED } CONN_STATE;
typedef struct {
SERVER_STATUS status; /* status of the server */
PROBE_STATE pstate; /* what we are trying to do (port or port-1) */
CONN_STATE cstate; /* what our connection status is */
WHY_DEAD why_dead; /* if status==SS_NOCONN, why? */
char hostname[64]; /* DNS */
char ip_addr[32]; /* ASCII IP */
long addr; /* actual address */
int port;
int count;
time_t down_time;
time_t last_update;
time_t next_update;
long flags; /* internal use */
long display_flags; /* for "flags" column */
char comment[40];
char mask; /* tournament mode mask */
char type[1]; /* may expand in the future */
int max_players;
int player_count;
int queue_size;
PLAYER players[MAX_PLAYER];
} SERVER; /* (if you add pointers here, update checkpointing) */
typedef struct {
char *buf;
int pos;
int buf_size;
int data_size;
time_t start_time; /* if nonzero, then user is active */
} USER;
/* port stuff */
typedef enum { DI_OLD, DI_NEW, DI_WEB, DI_VERBOSE, DI_INFO } DISPLAY;
typedef struct {
DISPLAY port_kind;
char *port_name;
} PORT_DEF;
typedef struct {
int port;
DISPLAY kind;
char desc[64];
char extra[64];
} PORT;
typedef struct {
long addr;
} EXCLUDE;
/* some stuff from struct.h */
#define PFREE 0
#define POUTFIT 1
#define PALIVE 2
#define PEXPLODE 3
#define PDEAD 4
#define MAX_STATUS 5
#define PTQUEUE 5 /* Paradise servers only */
/* function prototypes */
extern void init_connections(/*void*/),
main_loop(/*void*/),
Uprintf(/*pseudo-var*/);
extern char *ctime(),
*n_to_a(/*long*/);
extern long lookup(/*char *host, char *iphost*/);
extern int wakeup(/*signal handler*/);
extern int *sort_servers(/*void*/);
/* global variables */
extern int verbose,
wait_noconn,
wait_empty,
wait_open,
wait_queue,
try_alt_port,
server_count,
port_count,
exclude_count,
log_dns_names,
new_conn,
mnew_conn,
busy,
busy_time;
extern char *prog,
location[],
admin[];
extern char *classes[], *pclasses[],
teamlet[],
shipnos[];
extern SERVER *servers,
prev_info;
extern USER *users;
extern PORT_DEF port_defs[];
extern PORT *ports;
extern EXCLUDE *excludes;
extern time_t uptime,
checktime;
--- NEW FILE: scan.c ---
/*
* scan.c - scan the various servers
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "meta.h"
[...1208 lines suppressed...]
cc--;
} else if (FD_ISSET(i, &writefds)) {
if (i == busy_sock) {
/* finally got ahold of the darn server */
server_connected(i);
handle_server(i);
} else {
/* looks like a user is writeable */
handle_user(i);
}
cc--;
}
}
if (verbose) fflush(stdout);
}
/*NOTREACHED*/
}
--- NEW FILE: server.c ---
/*
* server.c - interact with a Netrek server
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*
* This is sorta messed up. Things would be much simpler if we could just
* sit and block on the Netrek socket, but that could take several seconds
* to complete. We need something which works the same way, but deals with
* partial packets, etc, etc, without blowing away the CPU like we are now.
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h> /* or strings.h */
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
/*
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
*/
#include <netinet/in.h>
#include <errno.h>
#include "meta.h"
#include "packets.h"
int new_conn; /* scan.c sets to TRUE when server conn is opened */
int mnew_conn; /* whoops, need one for read_minus too */
/*
* Private stuff
*/
#define NOBODY_PLAYING "No one playing"
#define SERVER_IN_USE "Server is in use"
#define BUFSIZE 85000 /* was 65000 */
static char rdbuf[BUFSIZE+1];
static int ssock;
/* from struct.h */
char *classes[NUM_TYPES] = {
"SC", "DD", "CA", "BB", "AS", "SB", "GA", "??"
};
char *pclasses[PNUM_TYPES] = {
"SC", "DD", "CA", "BB", "AS", "SB", "AT",
"JS", "FR", "WB", "CL", "CV", "UT", "PT"
};
char teamlet[] = { 'I', 'F', 'R', 'X', 'K', 'X', 'X', 'X', 'O' };
char shipnos[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'
};
#define NUMRANKS 9
#define PNUMRANKS 18
static struct rankstrings {
char *name;
int len;
} rank[NUMRANKS] = {
{ "Ensign", 6 },
{ "Lieutenant", 9 }, /* one short in case it got cut off */
{ "Lt. Cmdr.", 9 },
{ "Commander", 9 },
{ "Captain", 7 },
{ "Flt. Capt.", 9 }, /* (ditto) */
{ "Commodore", 9 },
{ "Rear Adm.", 9 },
{ "Admiral", 7 },
/* "(unknown)" */
}, prank[PNUMRANKS] = {
{ "Recruit", 7 },
{ "Specialist", 9 },
{ "Cadet", 5 },
{ "Midshipman", 9 },
{ "Ensn., J.G.", 9 },
{ "Ensign", 6 },
{ "Lt., J.G.", 9 },
{ "Lieutenant", 9 },
{ "Lt. Cmdr.", 9 },
{ "Commander", 9 },
{ "Captain", 7 },
{ "Fleet Capt.", 9 },
{ "Commodore", 9 },
{ "Moff", 4 },
{ "Grand Moff", 9 },
{ "Rear Adml.", 9 },
{ "Admiral", 7 },
{ "Grand Adml.", 9 }
};
/*
* ---------------------------------------------------------------------------
* Stuff adapted from netrek/socket.c
* ---------------------------------------------------------------------------
*/
static foo() {}
struct packet_handler handlers[] = {
{ 0, NULL }, /* record 0 */
{ sizeof(struct mesg_spacket), foo }, /* SP_MESSAGE */
{ sizeof(struct plyr_info_spacket), foo }, /* SP_PLAYER_INFO */
{ sizeof(struct kills_spacket), foo }, /* SP_KILLS */
{ sizeof(struct player_spacket), foo }, /* SP_PLAYER */
{ sizeof(struct torp_info_spacket), foo }, /* SP_TORP_INFO */
{ sizeof(struct torp_spacket), foo }, /* SP_TORP */
{ sizeof(struct phaser_spacket), foo }, /* SP_PHASER */
{ sizeof(struct plasma_info_spacket), foo}, /* SP_PLASMA_INFO */
{ sizeof(struct plasma_spacket), foo}, /* SP_PLASMA */
{ sizeof(struct warning_spacket), foo }, /* SP_WARNING */
{ sizeof(struct motd_spacket), foo }, /* SP_MOTD */
{ sizeof(struct you_spacket), foo }, /* SP_YOU */
{ sizeof(struct queue_spacket), foo }, /* SP_QUEUE */
{ sizeof(struct status_spacket), foo }, /* SP_STATUS */
{ sizeof(struct planet_spacket), foo }, /* SP_PLANET */
{ sizeof(struct pickok_spacket), foo }, /* SP_PICKOK */
{ sizeof(struct login_spacket), foo }, /* SP_LOGIN */
{ sizeof(struct flags_spacket), foo }, /* SP_FLAGS */
{ sizeof(struct mask_spacket), foo }, /* SP_MASK */
{ sizeof(struct pstatus_spacket), foo }, /* SP_PSTATUS */
{ sizeof(struct badversion_spacket), foo }, /* SP_BADVERSION */
{ sizeof(struct hostile_spacket), foo }, /* SP_HOSTILE */
{ sizeof(struct stats_spacket), foo }, /* SP_STATS */
{ sizeof(struct plyr_login_spacket), foo }, /* SP_PL_LOGIN */
{ sizeof(struct reserved_spacket), foo }, /* SP_RESERVED */
{ sizeof(struct planet_loc_spacket), foo }, /* SP_PLANET_LOC */
/* paradise packets */
{ sizeof(struct scan_spacket), foo }, /* SP_SCAN (ATM) */
{ sizeof(struct udp_reply_spacket), foo }, /* SP_UDP_STAT */
{ sizeof(struct sequence_spacket), foo }, /* SP_SEQUENCE */
{ sizeof(struct sc_sequence_spacket), foo },/*SP_SC_SEQUENCE*/
{ sizeof(struct rsa_key_spacket), foo }, /* SP_RSA_KEY */
{ sizeof(struct motd_pic_spacket), foo }, /* SP_MOTD_PIC */
{ sizeof(struct stats_spacket2), foo }, /* SP_STATS2 */
{ sizeof(struct status_spacket2), foo }, /* SP_STATUS2 */
{ sizeof(struct planet_spacket2), foo }, /* SP_PLANET2 */
{ sizeof(struct obvious_packet), foo }, /* SP_TEMP_5 */
{ sizeof(struct thingy_spacket), foo }, /* SP_THINGY */
{ sizeof(struct thingy_info_spacket), foo },/* SP_THINGY_INFO*/
{ sizeof(struct ship_cap_spacket), foo } /* SP_SHIP_CAP */
};
int sizes[] = {
0, /* record 0 */
sizeof(struct mesg_cpacket), /* CP_MESSAGE */
sizeof(struct speed_cpacket), /* CP_SPEED */
sizeof(struct dir_cpacket), /* CP_DIRECTION */
sizeof(struct phaser_cpacket), /* CP_PHASER */
sizeof(struct plasma_cpacket), /* CP_PLASMA */
sizeof(struct torp_cpacket), /* CP_TORP */
sizeof(struct quit_cpacket), /* CP_QUIT */
sizeof(struct login_cpacket), /* CP_LOGIN */
sizeof(struct outfit_cpacket), /* CP_OUTFIT */
sizeof(struct war_cpacket), /* CP_WAR */
sizeof(struct practr_cpacket), /* CP_PRACTR */
sizeof(struct shield_cpacket), /* CP_SHIELD */
sizeof(struct repair_cpacket), /* CP_REPAIR */
sizeof(struct orbit_cpacket), /* CP_ORBIT */
sizeof(struct planlock_cpacket), /* CP_PLANLOCK */
sizeof(struct playlock_cpacket), /* CP_PLAYLOCK */
sizeof(struct bomb_cpacket), /* CP_BOMB */
sizeof(struct beam_cpacket), /* CP_BEAM */
sizeof(struct cloak_cpacket), /* CP_CLOAK */
sizeof(struct det_torps_cpacket), /* CP_DET_TORPS */
sizeof(struct det_mytorp_cpacket), /* CP_DET_MYTORP */
sizeof(struct copilot_cpacket), /* CP_COPILOT */
sizeof(struct refit_cpacket), /* CP_REFIT */
sizeof(struct tractor_cpacket), /* CP_TRACTOR */
sizeof(struct repress_cpacket), /* CP_REPRESS */
sizeof(struct coup_cpacket), /* CP_COUP */
sizeof(struct socket_cpacket), /* CP_SOCKET */
sizeof(struct options_cpacket), /* CP_OPTIONS */
sizeof(struct bye_cpacket), /* CP_BYE */
sizeof(struct dockperm_cpacket), /* CP_DOCKPERM */
sizeof(struct updates_cpacket), /* CP_UPDATES */
sizeof(struct resetstats_cpacket), /* CP_RESETSTATS */
sizeof(struct reserved_cpacket) /* CP_RESERVED */
};
#define NUM_PACKETS (sizeof(handlers) / sizeof(handlers[0]) - 1)
#define NUM_SIZES (sizeof(sizes) / sizeof(sizes[0]) - 1)
int
sendServerPacket(packet)
struct player_spacket *packet;
{
int size;
#ifdef DEBUG
printf("- Sending %d\n", packet->type);
#endif
if (packet->type<1 || packet->type>NUM_SIZES || sizes[packet->type]==0) {
fprintf(stderr, "Attempt to send strange packet %d!\n", packet->type);
return (-1);
}
size=sizes[packet->type];
if (gwrite(ssock, packet, size) != size) {
return (-1);
}
return (0);
}
/*static*/ int
gwrite(fd, buf, bytes)
int fd;
char *buf;
register int bytes;
{
long orig = bytes;
register long n;
while (bytes) {
n = write(fd, buf, bytes);
if (n < 0)
return(-1);
bytes -= n;
buf += n;
}
return(orig);
}
/*
* ---------------------------------------------------------------------------
* New stuff
* ---------------------------------------------------------------------------
*/
/*static*/ char *
scan_buf(buf, str, count)
register char *buf, *str;
int count;
{
register int i;
int len, max;
len = strlen(str);
max = count - len;
if (max <= 0) return (NULL);
for (i = 0; i < max; i++, buf++) {
if (*buf == *str && !strncmp(buf, str, len))
return (buf);
}
return (NULL);
}
/*
* Parse a "players" output (port 2591 stuff). Returns the #of players found.
*
* Note that the data here will be overwritten if we get better info from
* the normal port (this is useful because it will get player info even
* if there's a wait queue).
*
* Note that we want to do this BEFORE trying the other port, so that we
* don't wake up the server if nobody is playing.
*
* New strategy: look for "standard keywords" in a header line, which is
* expected to immediately follow after "<>==". Gather a collection of
* line offsets, and use those when scanning the "info" lines.
*
* Info line strategy: find a line which starts with F/K/R/O/I and a number
* or small letter, and grab info until we hit something which doesn't match
* (like "<>=="). Then we're done.
*
* We do have something of a dilemma here: we want to allow the display
* to be off by a char or two, but it's possible that one or more of the
* fields could be blank (for example, if the user is just logging in).
* Until somebody defines the exact format to use (I did ask!), this will
* be satisfactory.
*/
#define PL_SEP "<>=="
#ifdef FUBAR
#define POS_MAPCHARS 2
#define POS_PSEUDO POS_MAPCHARS+4
#define POS_LOGIN POS_PSEUDO+17
#define POS_DISPLAY POS_LOGIN+12
#define POS_SHIP POS_DISPLAY+18
#endif
#define POSx_MAPCHARS 0
#define POSx_RANK 1
#define POSx_PSEUDO 2
#define POSx_LOGIN 3
#define POSx_DISPLAY 4
#define POSx_TYPE 5
#define MAX_POSx 6
#define TEAMCHARS "IFRKO"
#define PLAYERCHARS "0123456789abcdefghiABCDEFGHI"
struct {
int offset;
char *title;
int len;
} offsets[MAX_POSx] = {
{ 0, "Pl:", 3 },
{ 0, "Rank", 4 },
{ 0, "Name", 4 },
{ 0, "Login", 5 },
{ 0, "Display", 7 },
{ 0, "Type", 4 },
};
/*static*/ int
parse_playerlist(buf, size)
register char *buf;
int size;
{
SERVER *sp = &servers[busy];
PLAYER *pp;
int pl_count = 0;
register char *end = buf+size;
char ship_type[4], *cp, *brkp;
char c;
int i, j, pl, len, tmpo;
#ifdef DEBUG
printf("parsing playerlist, buf = 0x%.8lx, end = 0x%.8lx\n",
buf, end); /* DEBUG */
printf("%s\n",buf);
#endif
for (i = 0; i < MAX_POSx; i++)
offsets[i].offset = -1;
/* find start of interesting stuff */
while (buf < end) {
if (!strncmp(buf, PL_SEP, strlen(PL_SEP))) break;
while (*buf != '\n' && buf < end)
buf++;
buf++;
}
/* title line is expected next */
while (*buf != '\n' && buf < end)
buf++;
buf++;
if (buf >= end) return (-1);
for (cp = buf; *cp != '\n' && cp < end; cp++)
;
if (cp >= end) return (-1);
brkp = cp;
*brkp = '\0';
/* title line is at *buf; figure out where each of the fields begins */
if ((len = strlen(buf)) < 4) return (-1);
for (i = 0, cp = buf; i < len; i++, cp++) {
for (j = 0; j < MAX_POSx; j++) {
if (*cp == *(offsets[j].title)) {
if (!strncmp(cp, offsets[j].title, offsets[j].len)) {
/*printf("offset %d (%s) = %d\n", j, offsets[j].title, i);*/
offsets[j].offset = i;
i += offsets[j].len;
cp += offsets[j].len;
break; /* out of j-loop */
}
}
}
}
/* resume past the '\0' */
buf = brkp+1;
while (buf < end) {
tmpo = offsets[POSx_MAPCHARS].offset;
if (strchr(TEAMCHARS,*(buf+tmpo)) && strchr(PLAYERCHARS,*(buf+tmpo+1))){
/* found the start */
while (buf < end) {
tmpo = offsets[POSx_MAPCHARS].offset;
c = *(buf+tmpo+1);
if (c >= '0' && c <= '9') pl = c - '0';
else {
if (islower(c)) c = toupper(c);
pl = (c - 'A') + 10;
}
if (pl < 0 || pl >= sp->max_players) {
/*printf("illegal player #%d\n", pl);*/
return (-1);
}
/*printf("Player %d\n", pl);*/
pp = &(sp->players[pl]);
pp->p_mapchars[0] = c = *(buf+tmpo);
pp->p_mapchars[1] = *(buf+tmpo+1);
switch (c) {
case 'I': pp->p_team = 0; break;
case 'F': pp->p_team = 1; break;
case 'R': pp->p_team = 2; break;
case 'K': pp->p_team = 4; break;
case 'O': pp->p_team = 8; break;
default:
/*printf("Bad Team %s\n", buf);*/
return (-1);
}
if ((tmpo = offsets[POSx_RANK].offset) == -1 ||
*(buf+tmpo) == ' ') {
pp->p_rank = NUMRANKS;
} else {
struct rankstrings *ranklist;
int nranks;
if (sp->type[0]=='P') {
nranks = PNUMRANKS;
ranklist = prank;
} else {
nranks = NUMRANKS;
ranklist = rank;
}
pp->p_rank = nranks;
for (i = 0; i < nranks; i++) {
if (!strncmp(buf+tmpo, ranklist[i].name, ranklist[i].len)) {
pp->p_rank = i;
break;
}
}
}
if ((tmpo = offsets[POSx_PSEUDO].offset) == -1 ||
*(buf+tmpo) == ' ') {
strcpy(pp->p_name, "(none)");
} else {
strncpy(pp->p_name, buf+tmpo, 15);
cp = pp->p_name + strlen(pp->p_name) -1;
if (cp > pp->p_name) {
while (*cp == ' ') cp--;
*(cp+1) = '\0';
}
/*printf("name = '%s' ", pp->p_name);*/
}
if ((tmpo = offsets[POSx_LOGIN].offset) == -1 ||
*(buf+tmpo) == ' ') {
strcpy(pp->p_login, "(none)");
} else {
if (strtok(buf+tmpo, " ") == NULL) {
/*printf("Bad login %s\n", buf);*/
return (-1);
}
strncpy(pp->p_login, buf+tmpo, 15);
*(pp->p_login+15) = '\0';
/*printf("login = '%s' ", pp->p_login);*/
}
if ((tmpo = offsets[POSx_DISPLAY].offset) == -1 ||
*(buf+tmpo) == ' ') {
strcpy(pp->p_name, "(none)");
} else {
if (strtok(buf+tmpo, " ") == NULL) {
/*printf("Bad display %s\n", buf);*/
return (-1);
}
strncpy(pp->p_monitor, buf+tmpo, 15);
*(pp->p_name+15) = '\0';
/*printf("disp = '%s'\n", pp->p_monitor);*/
}
if ((tmpo = offsets[POSx_TYPE].offset) == -1) {
strcpy(ship_type, "??");
pp->ship_type = NUM_TYPES-1;
goto skip_type;
}
if (*(buf+tmpo) == ' ') tmpo++; /* allow a blank here */
if (*(buf+tmpo) == ' ' || *(buf+tmpo+1) == ' ') {
/*printf("Bad ship type %s\n", buf);*/
return (-1);
}
ship_type[0] = *(buf+tmpo);
ship_type[1] = *(buf+tmpo+1);
ship_type[2] = '\0';
pp->ship_type = NUM_TYPES-1; /* default is "??" */
for (i = 0; i < NUM_TYPES; i++) {
if (!strcmp(ship_type, classes[i])) {
pp->ship_type = i;
break;
}
}
skip_type:
/*printf("ship type = %d (%s)\n", pp->ship_type,
classes[pp->ship_type]);*/
pp->p_status = PALIVE;
pl_count++;
/* move to start of next line */
while (*buf != '\n' && buf < end)
buf++;
buf++;
/* done yet? */
if (*buf == *PL_SEP) goto done;
}
}
while (*buf != '\n' && buf < end)
buf++;
buf++;
}
done:
sp->player_count = pl_count;
return (pl_count);
}
/*
* Read & interpret data from port-1 (sends a player listing). Uses blocking
* reads; the amount of data should be small enough that this isn't a problem.
*
* Returns -1 on error, -2 if not done yet, status otherwise.
*
* NOTE: if the output of port 2591 was garbage, we would get "0 players
* playing" from parse_playerlist(). However, if nobody was playing, then
* we should've seen "No one is playing" earlier. So, if we get 0 back from
* parse_playerlist(), assume something went wrong and return (-1).
*/
SERVER_STATUS
read_minus(sock)
int sock;
{
static int count = 0;
int cc, pc;
#ifdef DEBUG
printf("Reading fd %d\n", sock);
#endif
ssock = sock;
if (mnew_conn) {
count = 0;
mnew_conn = FALSE;
#ifdef CONNECT
/* see if we're really connected */
if ((cc = write(sock, " ", 1)) < 0) {
if (errno != EPIPE) {
if (verbose) perror("strange read_minus error");
log_msg("strange read_minus write error %d with %s", errno,
servers[busy].hostname);
}
/* no change to why_dead for read_minus */
return (-1);
}
#endif
return (-2);
}
/* if we get an error, assume it was the server hanging up on us */
if ((cc = read(sock, rdbuf+count, BUFSIZE-count)) < 0) {
if (errno != ECONNREFUSED) {
if (verbose) perror("read minus");
log_msg("read error %d in read_minus with %s", errno,
servers[busy].hostname);
}
return (-1);
}
#ifdef DEBUG
printf ("read() of 2591 yielded %d bytes\n",cc);
#endif
count += cc;
if (cc) return (-2); /* more to go (ok if buf fills) */
if (!count) return (-1); /* no info! */
rdbuf[count] = '\0';
/* see if nobody is playing */
if (scan_buf(rdbuf, NOBODY_PLAYING, count) != NULL) {
#ifdef DEBUG
printf("2591 no one is playing!\n");
#endif
return (SS_EMPTY);
}
/* see if server is up, but no verbose info */
if (scan_buf(rdbuf, SERVER_IN_USE, count) != NULL)
return (SS_OPEN);
/* okay, we got valid stuff, now parse it and stuff it into SERVER struct */
if (!(pc = parse_playerlist(rdbuf, count)))
return (-1); /* was SS_EMPTY */
else if (pc < 0)
return (-1);
else
return (SS_OPEN);
}
/*
* See if we have the info for MAX_PLAYERS players in rdbuf. Returns (-1)
* if we don't have all the info yet. Returns 0 otherwise. Exits with 0
* if it encounters an SP_QUEUE packet. Returns (-2) if the data is corrupted.
*
* We count SP_PLAYER because it's the last packet in the group sent for
* each player. If the server sends them in a different order than we could
* have bogus data for the last player. Since this is what ck_players does,
* we break iff everything else breaks.
*
* To alleviate the CPU load, this keeps track of the byte offset of the
* first interesting item in the buffer. That way we don't have to seek
* past the motd every time. Whenever the data in the buffer changes (other
* than appending new data), call this with buflen=0 to reset the stuff.
*
* Here's what the profiler showed after half a day of activity:
*
* %time cumsecs #call ms/call name
* 24.9 8.58 17371 0.49 _read
* 23.3 16.62 19318 0.42 _select
* 5.5 18.51 mcount
* 4.8 20.18 24272 0.07 __doprnt
* 4.8 21.84 1484 1.12 _connect
* 3.6 23.08 17371 0.07 _scan_packets []
* 3.3 24.21 1 1130.00 _main_loop []
* 3.0 25.23 3042 0.34 _close
* 2.9 26.23 1472 0.68 _write
* 2.0 26.91 176515 0.00 .rem
* 1.8 27.54 13274 0.05 _strpbrk
* 1.7 28.14 472 1.27 _parse_server []
*
* So this routine is still the most CPU-intensive in the program, but it's
* relatively insignificant.
*/
int
scan_packets(buflen)
int buflen;
{
register unsigned char *buf;
register int pos, type, size;
int plcount = 0;
static int first_off = 0; /* opt */
int neat; /* opt */
if (!buflen) {
/* reset */
first_off = 0;
return;
}
pos = first_off;
buf = (unsigned char *) (rdbuf + pos);
if (pos > buflen) {
log_msg("something whacked in scan_packets");
#ifdef DEBUG
printf("scan_packets has initial pos = %d, buflen = %d\n", pos,buflen);
#endif
return (-1);
}
neat = 0;
while (pos < buflen) {
#ifdef DEBUG
printf("%02.2x ",(char) *(buf));
printf("%02.2x ",(char) *(buf+1));
printf("%02.2x ",(char) *(buf+2));
printf("%02.2x \n",(char) *(buf+3));
#endif
type = (short int) *buf;
if (type < 1 || type > NUM_PACKETS) {
printf("rcvd bad packet %d (0x%x) from %s (%d)\n", type, type,
servers[busy].hostname, servers[busy].port);
return (-2);
}
size = handlers[type].size;
if (size <= 0) {
/* got bogus size, headed for infinite spin */
printf("zero size in scan_packets from %s\n",
servers[busy].hostname);
return (-2);
}
if (size+pos > buflen)
return (-1);
if (type != SP_MOTD)
neat++; /* don't advance first_off anymore */
if (type == SP_PLAYER) {
SERVER *serv = &servers[busy];
{
struct player_spacket *sp;
sp = (struct player_spacket *) buf;
}
plcount++;
if (verbose) printf("Got player # %d\n",plcount);
if (plcount >= serv->max_players)
return (0);
}
if (type == SP_QUEUE)
return (0);
buf += size;
pos += size;
if (!neat) first_off = pos; /* start here next time */
}
return (-1);
}
/*
* Parse the info we got from the server. Returns status.
*/
SERVER_STATUS
parse_server(buflen)
int buflen;
{
SERVER *sp = &servers[busy];
PLAYER *pp;
struct queue_spacket *qp;
struct plyr_login_spacket *plp;
struct plyr_info_spacket *pip;
struct pstatus_spacket *psp;
register char *buf;
register int pos;
int type, size;
int pl_seen = 0;
buf = rdbuf, pos = 0;
sp->player_count = 0;
pp = sp->players;
while (pos < buflen && pl_seen < sp->max_players) {
type = (int) *buf;
size = handlers[type].size;
if (size+pos > buflen) {
#ifdef DEBUG
printf("type = %d, size = %d, pos = %d, buflen = %d\n",
type, size, pos, buflen);
#endif
return (-1);
}
switch (type) {
case SP_QUEUE:
qp = (struct queue_spacket *) buf;
sp->queue_size = qp->pos;
return (SS_QUEUE);
case SP_PL_LOGIN:
plp = (struct plyr_login_spacket *) buf;
if (plp->pnum < 0 || plp->pnum >= sp->max_players) break;
strncpy(pp[plp->pnum].p_name, plp->name, 16);
strncpy(pp[plp->pnum].p_monitor, plp->monitor, 16);
strncpy(pp[plp->pnum].p_login, plp->login, 16);
pp[plp->pnum].p_rank = plp->rank;
break;
case SP_PLAYER_INFO:
pip = (struct plyr_info_spacket *) buf;
if (pip->pnum < 0 || pip->pnum >= sp->max_players) break;
pp[pip->pnum].ship_type = pip->shiptype;
pp[pip->pnum].p_team = pip->team;
pp[pip->pnum].p_mapchars[0] = teamlet[pip->team];
break;
case SP_PSTATUS:
psp = (struct pstatus_spacket *) buf;
if (psp->pnum < 0 || psp->pnum >= sp->max_players) break;
pp[psp->pnum].p_mapchars[1] = shipnos[psp->pnum];
if (psp->status >=0 && psp->status < MAX_STATUS) {
pp[psp->pnum].p_status = psp->status;
} else if (psp->status == PTQUEUE) {
/* Paradise server tournament queue */
pp[psp->pnum].p_status = PALIVE;
} else {
log_msg("rcvd bad status (%d) from %s", psp->status,
servers[busy].hostname);
pp[psp->pnum].p_status = PFREE;
}
/* this is kinda weak, but *we* show up as POUTFIT... */
if (psp->status != PFREE && psp->status != POUTFIT)
sp->player_count++;
pl_seen++;
break;
#ifdef GET_MASK /* can't be done w/o full login */
case SP_MASK:
mp = (struct mask_spacket *) buf;
sp->mask = mp->mask;
mask_seen++;
printf("mask = 0x%x\n", sp->mask);
break;
#endif
case SP_STATS:
case SP_PLAYER:
case SP_FLAGS:
case SP_KILLS:
case SP_HOSTILE:
default:
/* do nothing */
size = size; /* silly compiler */
}
buf += size;
pos += size;
}
/* all done */
if (!sp->player_count)
return (SS_EMPTY);
else
return (SS_OPEN);
}
/*
* Read & interpret data from the standard Netrek port. This is a whole lot
* of fun, because we don't want to block on the read() calls.
*
* Basic algorithm is:
* while (still hungry && room in buffer) {
* read more data
* if (we have all of the information for each player)
* analyze it and return
* else {
* if we've got more room
* return with -2 so we come back later
* else
* return with -1 (hopefully this won't happen!)
* }
* }
*
* Two possible adjustments: one is to interpret the data as we go along
* (but that quickly turns to spaghetti), the other is to filter out the
* motd lines and scrunch the buffer up (which should work, but will raise
* the CPU load because of all the bcopy() calls). The latter approach will
* be implemented, just in case some bozo has a 60K motd. (idea: wait until
* the buffer is >50% full before doing any scrunching.)
*
* Returns -1 on error, -2 if not yet done, status otherwise.
*/
SERVER_STATUS
read_server(sock)
int sock;
{
struct socket_cpacket sockPack;
static char *buf; /* buf & count stay across calls */
static int count, extend_time;
int cc, res;
ssock = sock;
if (new_conn) {
new_conn = FALSE;
buf = rdbuf;
extend_time = 0;
count = 0;
scan_packets(0); /* reset */
sockPack.type=CP_SOCKET;
sockPack.socket=htonl(65535);
sockPack.version=(char) SOCKVERSION;
if (sendServerPacket(&sockPack) < 0) {
/* see if server really exists */
if (errno != EPIPE) {
perror("strange error on sendServerPacket");
log_msg("strange server write 27 error %d with %s", errno,
servers[busy].hostname);
}
servers[busy].why_dead = WD_CONN; /* connection really failed */
return (-1);
}
/* the server might not have data to send just yet */
/* (minor improvement: put a select() call here) */
return (-2);
}
if ((cc = read(sock, buf, BUFSIZE-count)) < 0) {
log_msg("server read error %d from %s", errno,
servers[busy].hostname);
#ifdef DEBUG
perror("read_server read");
#endif
servers[busy].why_dead = WD_READ;
return (-1);
}
#ifdef DEBUG
printf("read, cc=%d\n", cc);
#endif
if (!cc) {
/* we've hit EOF, but we don't have all the data; assume server dead */
#ifdef DEBUG
fprintf(stderr, "EOF hit early\n");
#endif
if (verbose)
log_msg("+ early EOF reading from %s", servers[busy].hostname);
servers[busy].why_dead = WD_GARBAGE;
return (-1);
}
buf += cc, count += cc;
if (!extend_time && count > 1024) {
busy_time += BUSY_EXTEN; /* this could be in the future... */
extend_time++;
}
/* FIX: add motd scrunch code here */
if ((res = scan_packets(count)) < 0) {
if (res == (-2)) { /* corruption */
servers[busy].why_dead = WD_GARBAGE;
#ifdef DEBUG
printf ( "scan_packets returned garbage status\n" );
#endif
return (-1);
}
if (count < BUFSIZE)
return (-2); /* get more */
else {
if (verbose)fprintf(stderr,"Internal ERROR: read buffer flooded\n");
log_msg("ERROR: read buffer flooded by server %s\n",
servers[busy].hostname);
return (-1); /* ran out of room! */
}
}
/* hooray, we have all the info in the buffer! */
return (parse_server(count));
}
--- NEW FILE: disp_info.c ---
/*
* disp_info.c - send a file to the client
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include "meta.h"
/*
* display the info file
*/
int
display_info(idx, port_idx)
int idx, port_idx;
{
if (verbose) printf("%s: sending info '%s'\n", prog, ports[port_idx].extra);
send_info(idx, ports[port_idx].extra);
return (0);
}
static int
send_info(idx, file)
int idx;
char *file;
{
USER *up;
FILE *fp;
char buf[128];
up = &users[idx];
up->data_size = up->buf_size = up->pos = 0;
if ((fp = fopen(file, "r")) == NULL) {
if (errno == EMFILE)
/* I don't see how this could be happening... weird */
Uprintf(idx,
"\nToo many files open here; wait a bit and try again\n\n");
else
Uprintf(idx,"\nUnable to open %s (err %d); tell the admin (%s)\n\n",
file, errno, admin);
log_msg("Unable to open %s (errno=%d)", file, errno);
return (0);
}
while (1) {
fgets(buf, 128, fp);
if (ferror(fp))
log_msg("error reading %s: %d", file, errno);
if (ferror(fp) || feof(fp))
break;
Uprintf(idx, "%s", buf);
}
fclose(fp);
return (0);
}
--- NEW FILE: BecomeDaemon.c ---
/*
* BecomeDaemon.c
* shamelessly swiped from xdm source code.
* Appropriate copyrights kept, and hereby follow
* ERic mehlhaff, 3/11/92
*
* xdm - display manager daemon
*
* $XConsortium: daemon.c,v 1.5 89/01/20 10:43:49 jim Exp $
*
* Copyright 1988 Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. M.I.T. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* Author: Keith Packard, MIT X Consortium
*/
#include <sys/ioctl.h>
#ifdef hpux
#include <sys/ptyio.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <sys/file.h>
extern void exit ();
void BecomeDaemon ()
{
register int i;
int forkresult;
/*
* fork so that the process goes into the background automatically. Also
* has a nice side effect of having the child process get inherited by
* init (pid 1).
*/
if ( (forkresult = fork ()) ){ /* if parent */
if(forkresult < 0 ){
perror("Fork error!");
}
exit (0); /* then no more work to do */
}
/*
* Close standard file descriptors and get rid of controlling tty
*/
close (0);
close (1);
close (2);
/*
* Set up the standard file descriptors.
*/
(void) open ("/", O_RDONLY); /* root inode already in core */
(void) open ("/tmp/metaout", O_WRONLY | O_APPEND | O_CREAT, 0644);
(void) dup2 (1, 2);
if ((i = open ("/dev/tty", O_RDWR)) >= 0) { /* did open succeed? */
#if defined(SYSV) && defined(TIOCTTY)
int zero = 0;
(void) ioctl (i, TIOCTTY, &zero);
#else
(void) ioctl (i, TIOCNOTTY, (char *) 0); /* detach, BSD style */
#endif
(void) close (i);
}
#ifdef SYSV
#ifdef hpux
setsid();
#else
setpgrp (0, 0);
#endif
#else
setpgrp (0, getpid());
#endif
}
--- NEW FILE: main.c ---
/*
* main.c - args, configuration, logging
*
* MetaServerII
* Copyright (c) 1993 by Andy McFadden
*/
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <errno.h>
#include <unistd.h>
#include "meta.h"
/*
* Globals
*/
int verbose = FALSE; /* if TRUE, print extra info on terminal */
char *prog = NULL; /* our program name */
SERVER *servers = NULL; /* list of servers to check */
int server_count = 0; /* #of servers in list */
PORT *ports = NULL; /* list of ports to listen on */
int port_count = 0; /* #of ports in list */
EXCLUDE *excludes = NULL; /* list of clients to exclude */
int exclude_count = 0;
int try_alt_port = TRUE; /* try port-1 first? */
int log_dns_names = TRUE; /* do DNS lookup on hostnames for log file? */
int save_ckp = TRUE; /* save checkpoint file? */
int wait_noconn = 30; /* wait 30 mins if connection failed */
int wait_empty = 15; /* wait 15 mins if server is empty */
int wait_open = 15; /* wait 15 mins if server is open */
int wait_queue = 15; /* wait 15 mins if server has a wait queue */
time_t uptime = 0; /* when we started */
time_t checktime = 0; /* when we started collecting data */
char info_file[MAXPATHLEN]; /* pathname of info file */
char faq_file[MAXPATHLEN]; /* pathmame of faq file */
char location[80]; /* city/state/country */
char admin[80]; /* e-mail addr of MSII maintainer */
FLAG flags[MAX_FLAG] = {
{ DF_HAD_TMODE, 'T', "HAD-TMODE" },
{ DF_ALWAYS_DEAD, 'D', "ALWAYS-DEAD" },
{ DF_RSA, 'R', "RSA-RESERVED" },
};
PORT_DEF port_defs[] = {
{ DI_OLD, "old" },
{ DI_NEW, "new" },
{ DI_WEB, "web" },
{ DI_VERBOSE, "verbose" },
{ DI_INFO, "info" },
};
#define MAX_PORT_DEF (sizeof(port_defs) / sizeof(PORT_DEF))
#define PIDFILE "meta.pid"
/*
* Private variables
*/
static FILE *logfp = NULL; /* log file */
static int do_log = TRUE; /* set from config file */
static int trun = 0; /* truncate log file on startup? */
/*
* Configuration stuff
*/
typedef enum {
STMT_BOOL, STMT_INT, STMT_STRING, STMT_SERVER, STMT_PORT, STMT_EXCLUDE
} STMT_KIND;
struct keyword_t {
STMT_KIND kind;
char *keyword;
int *var;
} keywords[] = {
{ STMT_INT, "wait_noconn", &wait_noconn },
{ STMT_INT, "wait_empty", &wait_empty },
{ STMT_INT, "wait_open", &wait_open },
{ STMT_INT, "wait_queue", &wait_queue },
{ STMT_BOOL, "do_logging", &do_log },
{ STMT_BOOL, "log_dns_names", &log_dns_names },
{ STMT_BOOL, "try_alt_port", &try_alt_port },
{ STMT_BOOL, "save_ckp", &save_ckp },
{ STMT_STRING, "info_file", (int *) info_file },
{ STMT_STRING, "faq_file", (int *) faq_file },
{ STMT_STRING, "location", (int *) location },
{ STMT_STRING, "admin", (int *) admin },
{ STMT_SERVER, "servers", NULL },
{ STMT_PORT, "port", NULL },
{ STMT_EXCLUDE, "exclude", NULL },
};
#define MAX_KEYWORDS (sizeof(keywords) / sizeof(struct keyword_t))
/*
* print a message to the log
*
* (I'd use varargs, but vfprintf() doesn't exist everywhere, so why bother?)
*/
void
log_msg(format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
char *format;
int arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7;
{
char buf[256];
if (!do_log) return;
if (logfp == NULL) {
fprintf(stderr, "WARNING: attempt to write message to closed log\n");
return;
}
/* date is in hex to keep it compact */
sprintf(buf, "%.8lx: ", time(0));
sprintf(buf+10, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
if (*(buf + strlen(buf)-1) != '\n')
strcat(buf, "\n");
/* since there should only be one metaserver process, we probably don't */
/* need to seek to the end every time (but it allows truncation...) */
fseek(logfp, (off_t) 0, 2);
fprintf(logfp, buf);
}
/* add an entry to the "server" list */
static void
add_server(type, host, iphost, port, players, comment)
char *type, *host, *iphost, *port, *players, *comment;
{
int portnum = atoi(port);
int playernum = atoi(players);
int rsa;
if (servers == NULL) {
servers = (SERVER *) malloc(sizeof(SERVER));
} else {
servers = (SERVER *) realloc(servers,
sizeof(SERVER) * (server_count+1));
}
if (servers == NULL) {
fprintf(stderr, "ERROR: malloc/realloc failure (servers)\n");
exit(1);
}
servers[server_count].status = SS_INIT;
servers[server_count].cstate = CS_CLOSED;
if (!strncmp(host, "RSA", 3)) {
rsa = 1;
strcpy(servers[server_count].hostname, host+3);
} else {
rsa = 0;
strcpy(servers[server_count].hostname, host);
}
strcpy(servers[server_count].ip_addr, iphost);
if ((servers[server_count].addr = lookup(host, iphost)) == -1)
return;
servers[server_count].type[0] = *type;
servers[server_count].port = portnum;
servers[server_count].max_players = playernum;
servers[server_count].count = 0;
servers[server_count].last_update = (time_t) 0;
servers[server_count].down_time = 0;
/* set the next_update time to space the checks 40 seconds apart */
servers[server_count].next_update = (time_t) (time(0) +
server_count*START_DIST);
servers[server_count].flags = 0L;
servers[server_count].display_flags = (DF_ALWAYS_DEAD) | (DF_RSA * rsa);
if (comment == NULL)
strcpy(servers[server_count].comment, "");
else
strcpy(servers[server_count].comment, comment);
servers[server_count].player_count = 0;
server_count++;
}
/* add an entry to the "port" list */
static void
add_port(port, type, description, extra)
char *port, *type, *description, *extra;
{
DISPLAY kind;
int portnum = atoi(port);
int i;
/* see if portnum is valid and port kind is known */
if (portnum < 1024) {
fprintf(stderr, "WARNING: port %d (%s) reserved; stmt ignored\n",
portnum, port);
return;
}
for (i = 0; i < MAX_PORT_DEF; i++) {
if (!strcmp(type, port_defs[i].port_name)) {
kind = port_defs[i].port_kind;
break;
}
}
if (i == MAX_PORT_DEF) {
fprintf(stderr, "WARNING: unknown port kind '%s'\n", type);
return;
}
/* create a new entry, and add it to the list */
if (ports == NULL) {
ports = (PORT *) malloc(sizeof(PORT));
} else {
ports = (PORT *) realloc(ports,
sizeof(PORT) * (port_count+1));
}
if (ports == NULL) {
fprintf(stderr, "ERROR: malloc/realloc failure (ports)\n");
exit(1);
}
ports[port_count].port = portnum;
ports[port_count].kind = kind;
strcpy(ports[port_count].desc, description);
if (extra != NULL)
strcpy(ports[port_count].extra, extra);
else
ports[port_count].extra[0] = '\0';
#ifdef DEBUG
printf("port=%d, kind=%d, desc='%s', extra='%s'\n",
ports[port_count].port, ports[port_count].kind,
ports[port_count].desc, ports[port_count].extra);
#endif
port_count++;
}
/* add an entry to the "exclude" list */
static void
add_exclude(host)
char *host;
{
if (excludes == NULL) {
excludes = (EXCLUDE *) malloc(sizeof(EXCLUDE));
} else {
excludes = (EXCLUDE *) realloc(excludes,
sizeof(EXCLUDE) * (exclude_count+1));
}
if (excludes == NULL) {
fprintf(stderr, "ERROR: malloc/realloc failure (excludes)\n");
exit(1);
}
if ((excludes[exclude_count].addr = lookup(host, host)) == -1)
return;
#ifdef DEBUG
printf("exclude %s (0x%lx)\n", host, excludes[exclude_count].addr);
#endif
exclude_count++;
}
/* for some stupid reason my inet_ntoa() is crashing (bad shared libs??) */
char *
my_ntoa(val)
unsigned long val;
{
static char buf[16];
unsigned long foo;
char *cp;
int i;
cp = buf;
for (i = 3; i >= 0; i--) {
/* sadly, sprintf() doesn't return #of chars on BSD systems */
foo = val >> (8*i);
sprintf(cp, "%d.", foo & 0xff);
cp += strlen(cp);
}
*(cp-1) = '\0'; /* trim the last '.' off */
return (buf);
}
/* print the list of excludes after the log file is open */
static void
print_excludes()
{
int i;
for (i = 0; i < exclude_count; i++)
log_msg("exclude #%d is %s (0x%.8lx)", i, my_ntoa(excludes[i].addr),
excludes[i].addr);
}
/* find the next token */
static char *
next_token(str)
char *str;
{
/* skip leading whitespace */
while (*str && (*str == ' ' || *str == '\t' || *str == '\n'))
str++;
if (*str)
return (str);
else
return (NULL);
}
/*
* read the configuration in (may be called from a signal handler)
*/
static void
load_config()
{
FILE *fp;
char buf[128], *cp, *cp2, *cp3, *cp4, *cp5, *cp6;
int i, len, line;
int serv_mode = FALSE;
/* clear out any existing configuration stuff */
if (server_count) {
free(servers);
server_count = 0;
}
*info_file = *faq_file = *admin = *location = '\0';
if ((fp = fopen(CONFIGFILE, "r")) == NULL) {
perror("ERROR: can't open config file");
exit(1);
}
line = 0;
while (1) {
fgets(buf, 128, fp);
line++;
if (feof(fp)) break;
if (ferror(fp)) {
perror("ERROR: error reading config file");
exit(1);
}
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
/* skip comments and blank lines */
cp = next_token(buf);
if (cp == NULL || *cp == '#')
continue;
/* once serv_mode is TRUE, we don't do anything but snag server names */
if (serv_mode) {
cp = strtok(cp, " \t");
if ((cp2 = strtok(NULL, " \t")) == NULL) { /* Server name */
fprintf(stderr,
"WARNING: missing server name (line %d); ignored\n",
line);
break;
}
if ((cp3 = strtok(NULL, " \t")) == NULL) { /* IP address */
fprintf(stderr,
"WARNING: missing IP host (line %d); ignored\n", line);
break;
}
if ((cp4 = strtok(NULL, " \t")) == NULL) { /* Port */
fprintf(stderr,
"WARNING: missing port (line %d); ignored\n", line);
break;
}
if ((cp5 = strtok(NULL, " \t")) == NULL) { /* Player # */
fprintf(stderr,
"WARNING: missing player number (line %d); ignored\n", line);
break;
}
cp6 = next_token(cp4+strlen(cp5)+1);
if (verbose) printf("Line is: %s %s %s %s %s %s\n",
cp, cp2, cp3, cp4, cp5, cp6);
add_server(cp, cp2, cp3, cp4, cp5, cp6);
continue;
}
/* find a token in "keywords" which matches */
for (i = 0; i < MAX_KEYWORDS; i++) {
len = strlen(keywords[i].keyword); /* use table for speed? */
if (!strncmp(cp, keywords[i].keyword, len)) {
cp2 = next_token(cp+len);
if (cp2 != NULL && *cp2 == ':') {
break;
}
}
}
if (i == MAX_KEYWORDS) {
fprintf(stderr, "WARNING: unknown statement (line %d): %s\n",
line, buf);
continue;
}
/* parse the right-hand side with strtok (unless it's a string) */
if (keywords[i].kind == STMT_STRING || keywords[i].kind == STMT_SERVER){
cp = next_token(cp2+1);
} else if ((cp = strtok(cp2+1, " \t")) == NULL) {
fprintf(stderr, "WARNING: missing rhs (line %d); ignored\n", line);
continue;
}
switch (keywords[i].kind) {
case STMT_BOOL: /* boolean on/off */
if (!strcasecmp(cp, "on"))
*(keywords[i].var) = 1;
else if (!strcasecmp(cp, "off"))
*(keywords[i].var) = 0;
else
fprintf(stderr,
"WARNING: boolean expr must be 'on' or 'off' (line %d)\n",
line);
break;
case STMT_INT: /* integer value */
*((int *) keywords[i].var) = atoi(cp);
break;
case STMT_STRING: /* string; dest should be allocated */
if (keywords[i].var == NULL) {
fprintf(stderr, "WARNING: no storage\n");
} else {
strcpy((char *) keywords[i].var, cp);
}
break;
case STMT_PORT: /* "port" line */
if ((cp2 = strtok(NULL, " \t")) == NULL) {
fprintf(stderr, "WARNING: port missing port kind; ignored\n");
break;
}
if ((cp3 = strtok(NULL, "\"\t")) == NULL) {
fprintf(stderr, "WARNING: port missing description; ignored\n");
break;
}
cp4 = strtok(NULL, " \t");
add_port(cp, cp2, cp3, cp4);
break;
case STMT_EXCLUDE: /* "exclude" line */
add_exclude(cp);
break;
case STMT_SERVER: /* "server" line */
serv_mode = TRUE;
break;
default:
/* "shouldn't happen" */
fprintf(stderr, "Internal error: bad keyword kind %d\n",
keywords[i].kind);
}
}
/* sanity check: make sure we have something to do! */
if (!server_count) {
fprintf(stderr, "ERROR: no servers in list!\n");
exit(1);
}
if (!port_count) {
fprintf(stderr, "ERROR: no ports to listen on!\n");
exit(1);
}
#ifdef DEBUG2
for (i = 0; i < server_count; i++) {
printf("%d: %s\n", i, servers[i].hostname);
}
#endif
}
/*
* open the log file and print a friendly message
*/
static void
open_log()
{
time_t now;
char *ostr;
if (!do_log) return;
/* prepare the log file */
if (!trun)
ostr = "a"; /* just append */
else
ostr = "w"; /* truncate */
if ((logfp = fopen(LOGFILE, ostr)) == NULL) {
perror("ERROR: can't open log file for writing");
exit(1);
} else {
setvbuf(logfp, NULL, _IONBF, 0); /* no buffering */
log_msg("\n");
now = time(0);
log_msg("** MetaServerII v%s started on %s", VERSION, ctime(&now));
}
}
/*
* Save status to checkpoint file
*
* Just does a dump of server structs. Changing the struct makes existing
* checkpoint files worthless. Puts the current time and the start time at
* the head of the file.
*/
void
save_checkpoint()
{
FILE *fp;
time_t now;
if (!save_ckp) {
log_msg("checkpoint NOT saved");
return;
}
if ((fp = fopen(CHECKFILE, "w")) == NULL) {
if (verbose) perror("fopen(checkfile)");
log_msg("unable to open checkpoint file (err=%d)", errno);
return;
}
now = time(0);
if (fwrite((char *)&now, sizeof(time_t), 1, fp) <= 0) {
if (verbose) perror("fwrite(checkfile) time-now");
log_msg("unable to write to checkpoint file (t)(err=%d)", errno);
fclose(fp);
return;
}
if (fwrite((char *)&checktime, sizeof(time_t), 1, fp) <= 0) {
if (verbose) perror("fwrite(checkfile) time-begin");
log_msg("unable to write to checkpoint file (t2)(err=%d)", errno);
fclose(fp);
return;
}
if (fwrite((char *)servers, sizeof(SERVER), server_count, fp) <= 0) {
if (verbose) perror("fwrite(checkfile)");
log_msg("unable to write to checkpoint file (err=%d)", errno);
fclose(fp);
return;
}
fclose(fp);
log_msg("checkpoint saved");
}
/*
* Restore status from checkpoint file
*
* The server list may have changed, so we need to match each item from the
* checkpoint file with an item in the server list. This is not trapped
* automatically.
*
* If the checkpoint file is corrupted, we can get pretty messed up.
*/
void
restore_checkpoint()
{
SERVER *sp, srvbuf;
FILE *fp;
time_t filetime, now;
int i, j;
if ((fp = fopen(CHECKFILE, "r")) == NULL) {
if (verbose) perror("fopen(checkfile)");
log_msg("unable to open checkpoint file (r)(err=%d)", errno);
return;
}
if (fread((char *)&filetime, sizeof(time_t), 1, fp) <= 0) {
if (verbose) perror("fread(checkfile) time");
log_msg("unable to read checkpoint file (t)(err=%d)", errno);
fclose(fp);
return;
}
if (uptime - filetime > MAX_CKP_AGE) {
if (verbose) fprintf(stderr,
"Warning: checkpoint file too old (ignored)\n");
log_msg("checkpoint file ignored (%d seconds old, max is %d)",
uptime - filetime, MAX_CKP_AGE);
goto dodel;
}
if (verbose) printf("Checkpoint file is %d seconds old\n",
uptime - filetime);
if (fread((char *)&checktime, sizeof(time_t), 1, fp) <= 0) {
if (verbose) perror("fread(checkfile) time");
log_msg("unable to read checkpoint file (t)(err=%d)", errno);
fclose(fp);
return;
}
/* for each entry in the checkpoint file, find the matching SERVER */
/* (if config info has changed, ignore the checkpoint entry) */
while (1) {
if (fread((char *)(&srvbuf), sizeof(SERVER), 1, fp) <= 0)
break;
#ifdef DEBUG
printf("Got '%s'\n", srvbuf.hostname);
#endif
/* shouldn't compare against already-matched servers... */
for (i = 0, sp = servers; i < server_count; i++, sp++) {
if (!strcmp(srvbuf.hostname, sp->hostname) &&
!strcmp(srvbuf.ip_addr, sp->ip_addr) &&
!strcmp(srvbuf.comment, sp->comment) &&
(srvbuf.type[0] == sp->type[0]) &&
srvbuf.port == sp->port) {
bcopy(&srvbuf, sp, sizeof(SERVER));
}
}
}
/* do some minor cleaning in case we interrupted it last time */
/* while we're here, bump up "next check" times for new servers */
now = time(0);
for (i = j = 0, sp = servers; i < server_count; i++, sp++) {
sp->pstate = PS_PORTM;
sp->cstate = CS_CLOSED;
if (sp->status == SS_WORKING) sp->status = SS_INIT;
if (sp->status == SS_INIT)
sp->next_update = (time_t) (now + (j++) * START_DIST);
}
log_msg("checkpoint restored");
/* delete the checkpoint file */
dodel:
if (unlink(CHECKFILE) < 0) {
if (verbose) perror("unlink(checkfile)");
log_msg("unable to unlink checkfile (err=%d)", errno);
}
}
/* fatal signal received */
static int
sig_death(sig, code, scp, addr)
int sig, code;
struct sigcontext *scp;
char *addr;
{
if (verbose) printf("%s: killed by signal %d\n", prog, sig);
log_msg("exit: killed by signal %d", sig);
save_checkpoint();
#ifdef ABORT
#ifndef PROF /* always exit cleanly if we're doing profiling */
log_msg("sig = %d, code = %d, scp = 0x%.8lx, addr = 0x%.8lx\n",
sig, code, scp, addr);
log_msg("pc = 0x%.8lx\n", scp->sc_pc);
log_msg("Dumping core...\n");
abort();
#endif
#endif /*ABORT*/
exit(2);
}
/* reconfigure signal receieved */
static int
sig_reconfig(sig)
int sig;
{
if (verbose) printf("%s: received reconfigure signal %d\n", prog, sig);
/*log_msg("rereading configuration file (signal %d)", sig);*/
log_msg("ignoring config signal %d", sig);
/*load_config();*/
}
/*
* initialize signal handling stuff
*/
static void
init_signals()
{
SIGNAL(SIGINT, sig_death);
SIGNAL(SIGQUIT, sig_death);
SIGNAL(SIGTERM, sig_death);
SIGNAL(SIGHUP, sig_reconfig);
SIGNAL(SIGPIPE, SIG_IGN);
/*SIGNAL(SIGSEGV, sig_crash);*/
SIGNAL(SIGALRM, wakeup);
}
/*
* Process args
*/
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
int c;
FILE *fp;
int pid = getpid();
if ((fp = fopen(PIDFILE,"w"))==NULL) {
perror("pid file");
exit(0);
}
fprintf(fp,"%d\n", pid);
fclose(fp);
/* parse args */
while ((c = getopt(argc, argv, "vtd")) != EOF)
switch (c) {
case 'v':
verbose++;
break;
case 't':
trun++;
break;
case 'd':
BecomeDaemon();
break;
case '?':
default:
fprintf(stderr, "Netrek MetaServerII v%s\n", VERSION);
fprintf(stderr, "usage: metaserverII [-d] [-v] [-t]\n\n");
exit(2);
}
if ((prog = strrchr(argv[0], '/')) == NULL)
prog = argv[0];
else
prog++;
/* read the configuration file */
load_config();
/* initialize internal data structures */
init_connections();
/* open the log and print a friendly message */
if (do_log) open_log();
/* init the signal handling stuff */
init_signals();
uptime = checktime = time(0);
/* if there's a checkpoint file, load the old data */
restore_checkpoint();
/* print the list of excluded hosts, for reference */
print_excludes();
/* nothing has failed yet, so go wait for connections */
main_loop();
exit(0);
/*NOTREACHED*/
}