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*/ }