Vanilla Netrek Server Development Archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[VANILLA-L:364] Vanilla Metaserver Solicitation Patches 2.7pl4+Solicit



Folks,

I have constructed a patch to both the metaserver and the Vanilla server that
provides for rapid and cheap UDP based update of the metaserver information.

This is a prototype for the Metaserver Redevelopment Project.  It's a case of
20% of effort for the 80% benefit ... namely an increase in data timeliness
and a decrease in network bandwidth.

Vanilla Team : your action is to review the code in solicit.c and the change
to daemonII.c in the following patch.  This is prototype code, not intended
for immediate use.

Metaserver Team : you have to review the whole thing, and preferrably apply the
patches to a test metaserver and later the production metaservers.

There are two patches here, and it's no use installing them unless a
metaserver that listens to the UDP packets is available.

Format of the .metaservers file used by server ...

% cat .metaservers
metaserver.stl.dec.com 3521 60 300 ogg.stl.dec.com B 2592 2593 available
%

Host name of metaserver,
Port number of metaserver (UDP listen port),
Minimum update time (seconds),
Maximum update time (seconds),
Server host name,
Server type (B=Bronco),
Server player port number,
Server observer port number
Comment word (no spaces ;-( )


Warning: this patch should be applied with -p to preserve pathname prefixes.
*** manifest.old	Thu Dec 19 18:40:56 1996
--- manifest	Thu Dec 19 18:41:15 1996
***************
*** 87,92 ****
--- 87,93 ----
  Server/ntserv/slotmaint.c
  Server/ntserv/smessage.c
  Server/ntserv/socket.c
+ Server/ntserv/solicit.c
  Server/ntserv/startrobot.c
  Server/ntserv/openmem.c
  Server/ntserv/strdup.c
*** patchlevel.h.old	Thu Dec 19 18:40:45 1996
--- patchlevel.h	Thu Dec 19 18:31:40 1996
***************
*** 26,31 ****
--- 26,32 ----
  /*
   * Only compile this to add the name of your patch before it's official */
  char *patchname[10] = {
+   "Solicit",
    NULL,
  };
  #endif
*** ./ntserv/Makefile.old	Sun Dec  8 01:47:02 1996
--- ./ntserv/Makefile	Sun Dec  8 16:20:06 1996
***************
*** 22,28 ****
  
  D_OBJS =    daemonII.o  sintab.o  sysdefaults.o  data.o  slotmaint.o \
  	    util.o  $(RANDOMO) getpath.o getship.o smessage.o  queue.o \
! 	    wander2.o openmem.o
  
  #	Src files
  
--- 22,28 ----
  
  D_OBJS =    daemonII.o  sintab.o  sysdefaults.o  data.o  slotmaint.o \
  	    util.o  $(RANDOMO) getpath.o getship.o smessage.o  queue.o \
! 	    wander2.o openmem.o solicit.o
  
  #	Src files
  
*** ./ntserv/daemonII.c.old	Thu Dec 19 18:23:17 1996
--- ./ntserv/daemonII.c	Thu Dec 19 18:46:32 1996
***************
*** 386,391 ****
--- 386,392 ----
  
      if (fuse(QUEUEFUSE)){
          queues_purge();
+         solicit(0);
      }
  
      if (status->gameup & GU_PAUSED){ /* Game is paused */
***************
*** 2703,2708 ****
--- 2704,2710 ----
      /* Kill waiting players */
      for (i=0; i<MAXQUEUE; i++) queues[i].q_flags=0;
      save_planets();
+     solicit(0);
  #if !(defined(SCO) || defined(linux))
      sleep(2);
  #endif
*** ./ntserv/solicit.c.old	Thu Dec 19 18:27:59 1996
--- ./ntserv/solicit.c	Fri Dec 13 21:31:38 1996
***************
*** 0 ****
--- 1,229 ----
+ #include <stdio.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ 
+ #include "defs.h"
+ #include "struct.h"
+ #include "data.h"
+ 
+ /* bytes to reserve for outgoing packet to metaserver */
+ #define MAXMETABYTES 1024
+ 
+ /* maximum number of metaservers supported */
+ #define MAXMETASERVERS 3
+ 
+ /* minimum allowable delay between updates to metaserver */
+ #define META_MINIMUM_DELAY 60
+ /* Note: changing this may cause the metaserver to delist your server */
+ 
+ /* maximum delay between updates to metaserver */
+ #define META_MAXIMUM_DELAY 900
+ 
+ /* ship classes (wish these were in data.c/data.h) */
+ static char *ships[] = {"SC", "DD", "CA", "BB", "AS", "SB", "GA"};
+ 
+ /* structure of information about a single metaserver */
+ struct metaserver
+ {
+     /* data items derived from metaservers file */
+     char host[32];		/* address of metaserver (DNS)		*/
+     int port;			/* port of metaserver			*/
+     int minimum;		/* minimum update time			*/
+     int maximum;		/* maximum update time			*/
+     char ours[32];		/* DNS address of server 	 	*/
+     char type[2];		/* server type code (B/P/C/H/?)		*/
+     int pport;			/* server main player port (e.g. 2592)	*/
+     int oport;			/* server observer player port		*/
+     char comment[32];		/* comment string			*/
+ 
+     /* our own data about the communication with the metaserver */
+     int sock;                   /* our socket number                    */
+     struct sockaddr_in address;	/* address of metaserver		*/
+     time_t sent;		/* date time metaserver last updated	*/
+     char prior[MAXMETABYTES];	/* prior packet sent			*/
+ } metaservers[MAXMETASERVERS];
+ 
+ /* initialisation done flag */
+ static int initialised = 0;
+ 
+ /* attach to a metaserver, i.e. prepare the socket */
+ static int udp_attach(struct metaserver *m)
+ {
+     /* create the socket structure */
+     m->sock = socket(AF_INET, SOCK_DGRAM, 0);
+     if (m->sock < 0) {
+         perror("solicit: udp_attach: socket");
+         return 0;
+     }
+ 
+     /* bind the local socket */
+     m->address.sin_addr.s_addr = INADDR_ANY;
+     m->address.sin_family      = AF_INET;
+     m->address.sin_port        = 0;
+     if (bind(m->sock,(struct sockaddr *)&m->address, sizeof(m->address)) < 0) {
+         perror("solicit: udp_attach: bind");
+         return 0;
+     }
+     
+     /* build the destination address */
+     m->address.sin_family = AF_INET;
+     m->address.sin_port = htons(m->port);
+ 
+     /* attempt numeric translation first */
+     if ((m->address.sin_addr.s_addr = inet_addr(m->host)) == -1)
+     {
+         struct hostent *hp;
+         
+         /* then translation by name */
+         if ((hp = gethostbyname(m->host)) == NULL)
+         {
+             /* if it didn't work, return failure and warning */
+             fprintf(stderr, "solicit: udp_attach: host %s not known\n", 
+ 		m->host);
+             return 0;
+         }
+         else
+         {
+             m->address.sin_addr.s_addr = *(long *) hp->h_addr;
+         }
+     }
+ 
+     return 1;
+ }
+ 
+ /* transmit a packet to the metaserver */
+ static int udp_tx(struct metaserver *m, char *buffer, int length)
+ {
+     /* send the packet */
+     if (sendto(m->sock, buffer, length, 0, (struct sockaddr *)&m->address, 
+         sizeof(m->address)) < 0) {
+         perror("solicit: udp_tx: sendto");
+         return 0;
+     }
+     
+     return 1;
+ }
+ 
+ void solicit(int force)
+ {
+     int i, nplayers=0, nfree=0; 
+     char packet[MAXMETABYTES];
+     static char prior[MAXMETABYTES];
+     char *here = packet;
+     time_t now = time(NULL);
+    
+     /* perform first time initialisation */ 
+     if (initialised == 0) {
+ 	FILE *file;
+ 
+ 	/* clear metaserver socket list */
+ 	for (i=0; i<MAXMETASERVERS; i++) metaservers[i].sock = -1;
+ 
+ 	/* open the metaserver list file */
+ 	file = fopen(".metaservers", "r"); /* ??? LIBDIR prefix? */
+ 	if (file == NULL) {
+ 	    initialised++;
+ 	    return;
+ 	}
+ 	
+ 	/* read the metaserver list file */
+ 	for (i=0; i<MAXMETASERVERS; i++)
+ 	{
+ 	    struct metaserver *m = &metaservers[i];
+ 
+ 	    /* if end of file reached, stop */
+ 	    if (feof(file)) break;
+ 
+ 	    /* scan the line */
+ 	    fscanf(file, "%s %d %d %d %s %s %d %d %s/n", m->host, &m->port,
+ 		&m->minimum, &m->maximum, m->ours, m->type, &m->pport,
+ 		&m->oport, m->comment);
+ 
+ 	    /* force minimum and maximum delays (see note on #define) */
+ 	    if (m->minimum < META_MINIMUM_DELAY)
+ 		m->minimum = META_MINIMUM_DELAY;
+ 	    if (m->maximum > META_MAXIMUM_DELAY)
+ 		m->maximum = META_MAXIMUM_DELAY;
+ 
+ 	    /* attach to the metaserver (DNS lookup is only delay) */
+ 	    udp_attach(m);
+ 	    /* place metaserver addresses in /etc/hosts to speed this */
+ 	    /* use numeric metaserver address to speed this */
+ 
+ 	    /* initialise the other parts of the structure */
+ 	    m->sent = 0;
+ 	    strcpy(m->prior, "");
+ 	}
+         initialised++;
+     }
+ 
+     /* update each metaserver */
+     for (i=0; i<MAXMETASERVERS; i++)
+     {
+ 	struct metaserver *m = &metaservers[i];
+ 	int j;
+ 
+ 	/* skip empty metaserver entries */
+ 	if (m->sock == -1) continue;
+ 
+ 	/* if we told metaserver recently, don't speak yet */
+ 	if (!force)
+ 	    if ((now-m->sent) < m->minimum) continue;
+ 
+         /* count up the number of free slots and players */
+         for (j=0; j<MAXPLAYER; j++)
+             if (players[j].p_status == PFREE)
+                 nfree++;
+             else
+                 nplayers++;
+ 
+         /* if the free slots are zero, translate it to a queue length */
+         if (nfree == 0) nfree = -queues[QU_PICKUP].count;
+     
+         /* build start of the packet, the server information */
+         sprintf(here, "%s\n%s\n%s\n%d\n%d\n%d\n%d\n%s\n%s\n",
+             /* version */   "a",
+             /* address */   m->ours,
+             /* type    */   m->type,
+             /* port    */   m->pport,
+             /* observe */   m->oport,
+             /* players */   nplayers,
+             /* free    */   nfree,
+             /* t-mode  */   status->tourn ? "y" : "n",
+             /* comment */   m->comment
+         );
+         here += strlen(here);
+ 
+         /* now append per-player information to the packet */
+         for (j=0; j<MAXPLAYER; j++) {
+             /* ignore free slots */
+             if (players[j].p_status == PFREE || players[j].p_stats.st_tticks==0)
+                 continue;
+             sprintf(here, "%c\n%c\n%d\n%d\n%s\n%s@%s\n",
+                 /* number */   players[j].p_mapchars[1], 
+                 /* team   */   players[j].p_mapchars[0],
+                 /* class  */   players[j].p_ship.s_type,
+ 		/* ??? note change from design, ship type number not string */
+                 /* rank   */   players[j].p_stats.st_rank,
+ 		/* ??? note change from design, rank number not string */
+                 /* name   */   players[j].p_name,
+                 /* user   */   players[j].p_login,
+                 /* host   */   players[j].p_monitor );
+             here += strlen(here);
+         }
+ 
+ 	/* if we have exceeded the maximum time, force an update */
+ 	if ((now-m->sent) > m->maximum) force=1;
+ 
+ 	/* if we are not forcing an update, and nothing has changed, drop */
+ 	if (!force)
+ 	    if (!strcmp(packet, m->prior)) continue;
+ 
+         /* send the packet */
+         if (udp_tx(m, packet, here-packet)) {
+             m->sent=time(NULL);
+             strcpy(m->prior, packet);
+         }
+     }
+ }

     ____           _       _   _
    / ___|  _   _  | |_    | | | |   ___   _ __    ___
   | |     | | | | | __|   | |_| |  / _ \ | '__|  / _ \
   | |___  | |_| | | |_    |  _  | |  __/ | |    |  __/
    \____|  \__,_|  \__|   |_| |_|  \___| |_|     \___|
    
diff -c metaserver.old/Makefile metaserver/Makefile
*** metaserver.old/Makefile	Thu Dec 28 22:45:13 1995
--- metaserver/Makefile	Mon Dec  9 17:59:57 1996
***************
*** 25,31 ****
  all: $(TARGET)
  
  $(TARGET): $(OFILES)
! 	cc $(CFLAGS) -o $(TARGET) $(OFILES) $(LIBS)
  
  $(OFILES):	meta.h
  
--- 26,32 ----
  all: $(TARGET)
  
  $(TARGET): $(OFILES)
! 	$(CC) $(CFLAGS) -o $(TARGET) $(OFILES) $(LIBS)
  
  $(OFILES):	meta.h
  
diff -c metaserver.old/disp_new.c metaserver/disp_new.c
*** metaserver.old/disp_new.c	Thu Dec 28 00:56:12 1995
--- metaserver/disp_new.c	Wed Dec 11 19:20:52 1996
***************
*** 112,117 ****
--- 112,121 ----
  		    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);
***************
*** 233,238 ****
--- 237,245 ----
  		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()",
diff -c metaserver.old/meta.h metaserver/meta.h
*** metaserver.old/meta.h	Thu Dec 28 22:39:59 1995
--- metaserver/meta.h	Wed Dec 11 19:21:18 1996
***************
*** 25,30 ****
--- 25,32 ----
  #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
***************
*** 82,88 ****
  /* 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 } WHY_DEAD;
  typedef enum { PS_PORTM, PS_PORT } PROBE_STATE;
  typedef enum { CS_CLOSED, CS_CONNECTING, CS_CONNECTED } CONN_STATE;
  typedef struct {
--- 84,91 ----
  /* 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 {
diff -c metaserver.old/scan.c metaserver/scan.c
*** metaserver.old/scan.c	Thu Dec 28 22:42:32 1995
--- metaserver/scan.c	Thu Dec 19 18:16:15 1996
***************
*** 876,882 ****
--- 876,1100 ----
      last_busy = busy;			/* got one, update last_busy */
  }
  
+ /* Maximum size of a UDP packet accepted */
+ /* ??? need to have this documented and agreed */
+ #define MAXMETABYTES 1024
  
+ /* Port on metaserver for incoming UDP */
+ #define USOCKPORT 3521
+ 
+ void
+ uread(int usock)
+ {
+     char packet[MAXMETABYTES];
+     struct hostent *hp;
+     struct sockaddr_in from;
+     unsigned int fromlen = sizeof(from);
+     int bytes;
+     SERVER *sp, srvbuf;
+     char *p;
+     int i;
+ 
+     /* read the UDP packet from the server */
+     bytes = recvfrom(usock, packet, MAXMETABYTES, 0, (struct sockaddr *)&from,
+         &fromlen );
+     if (bytes < 0) {
+         perror("uread: recvfrom");
+ 	return;
+     }
+ 
+     /* initialise our server structure */
+     srvbuf.status = SS_OPEN;
+     srvbuf.last_update = time(NULL);
+     srvbuf.next_update = srvbuf.last_update+3600;
+     srvbuf.addr = from.sin_addr.s_addr;
+ 
+     /* null terminate it so we can use strtok on it */
+     packet[bytes] = 0;
+ #ifdef UREADTRACE
+     log_msg("%s %s", inet_ntoa(srvbuf.addr), packet );
+ #endif
+ 
+     /* version */
+     p = strtok(packet, "\n");
+     if (p == NULL) return;
+     if (p[0] != 'a') {
+ 	log_msg("uread: bad version %02.2x from %s\n", p[0],
+ 	    inet_ntoa(srvbuf.addr));
+ 	return;
+     }
+ 
+     /* address (DNS, as stated by server within packet) */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     strcpy(srvbuf.hostname, p);
+ 
+     /* address (IP, as received on socket) */
+     p = inet_ntoa ( from.sin_addr );
+     strcpy(srvbuf.ip_addr, p);
+ 
+     /* type */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     srvbuf.type[0] = p[0];
+ 
+     /* port */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     srvbuf.port = atoi(p);
+ 
+     /* observer port (ignored) */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+ 
+     /* number of players */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     srvbuf.player_count = atoi(p);
+     if (srvbuf.player_count == 0) srvbuf.status = SS_EMPTY;
+ 
+     /* number of free slots */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     if (atoi(p) > 0) srvbuf.max_players = srvbuf.player_count + atoi(p);
+     srvbuf.queue_size = -atoi(p);
+     if (srvbuf.queue_size < 0) srvbuf.queue_size = 0;
+     if (srvbuf.queue_size > 0) srvbuf.status = SS_QUEUE;
+ 
+     /* t-mode */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     srvbuf.display_flags = 0;
+     if (p[0] == 'y') {
+         srvbuf.display_flags |= DF_HAD_TMODE;
+     }
+ 
+     /* comment */
+     p = strtok(NULL, "\n");
+     if (p == NULL) goto truncated;
+     strncpy(srvbuf.comment, p, 40);
+ 
+     /* parse player list */
+     if (srvbuf.player_count > 0)
+     {
+ 	int i; /* player number */
+ 
+ 	for (i=0; i<MAX_PLAYER; i++) srvbuf.players[i].p_status = PFREE;
+ 
+ 	for(;;)
+ 	{
+ 	    /* slot number */
+ 	    p = strtok(NULL, "\n");
+ 	    if (p == NULL) break;
+ 
+ 	    /* determine offset in our player structure, give up if bad */
+ 	    for (i=0; i<MAX_PLAYER; i++) if (shipnos[i] == p[0]) break;
+ 	    if (i == MAX_PLAYER) break;
+ 
+ 	    /* save the second map character */
+ 	    srvbuf.players[i].p_mapchars[1] = p[0];
+ 
+ 	    /* team letter */
+ 	    p = strtok(NULL, "\n");
+             if (p == NULL) break;
+             srvbuf.players[i].p_mapchars[0] = p[0];
+ 	    switch (p[0])
+ 	    {
+ 		/* pathetic hack */
+ 		case 'I': srvbuf.players[i].p_team = 0; break;
+ 		case 'F': srvbuf.players[i].p_team = 1; break;
+ 		case 'R': srvbuf.players[i].p_team = 2; break;
+ 		case 'K': srvbuf.players[i].p_team = 4; break;
+ 		case 'O': srvbuf.players[i].p_team = 8; break;
+ 		default : srvbuf.players[i].p_team = 7; break;
+ 	    }
+ 
+ 	    /* ship class */
+             p = strtok(NULL, "\n");
+             if (p == NULL) break;
+             srvbuf.players[i].ship_type = atoi(p);
+ 	    /* ??? note change from design, ship type number not string */
+ 
+ 	    /* rank */
+             p = strtok(NULL, "\n");
+             if (p == NULL) break;
+             srvbuf.players[i].p_rank = atoi(p);
+ 	    /* ??? note change from design, rank number not string */
+ 
+ 	    /* name */
+             p = strtok(NULL, "\n");
+             if (p == NULL) break;
+             strncpy(srvbuf.players[i].p_name, p, 16);
+ 
+ 	    /* login */
+             p = strtok(NULL, "@");
+             if (p == NULL) break;
+             strncpy(srvbuf.players[i].p_login, p, 16);
+ 
+ 	    /* monitor */
+             p = strtok(NULL, "\n");
+             if (p == NULL) break;
+             strncpy(srvbuf.players[i].p_monitor, p, 16);
+ 
+ 	    srvbuf.players[i].p_status = PALIVE;
+ 	}
+     }
+ 
+     /* now either update an existing server entry or create a new one */
+     for (i = 0, sp = servers; i < server_count; i++, sp++) {
+         if (!strcmp(srvbuf.hostname, sp->hostname) &&
+             !strcmp(srvbuf.ip_addr, sp->ip_addr) &&
+             (srvbuf.type[0] == sp->type[0]) &&
+             srvbuf.port == sp->port) {
+ 		/* we know about the server already */
+ 
+ 		/* check for flooding */
+ 		if (srvbuf.player_count != 0) {
+ 		    if ((srvbuf.last_update - sp->last_update) < 
+ 			MIN_UREAD_TIME) {
+ 
+ 			/* server flooding, delist it */
+ 			sp->status = SS_NOCONN;
+ 			sp->why_dead = WD_FLOODING;
+ 			sp->last_update = srvbuf.last_update;
+ 			return;
+ 		    }
+ 		}
+ 		/* note above: player count zero is the only condition that
+ 		server will violate our time limit, and we hardly expect any
+ 		server operators to _want_ to use player count zero flooding */
+ 
+ 		/* server is being nice, accept the update and return */
+ 		bcopy(&srvbuf, sp, sizeof(SERVER));
+ 		log_msg("usock: updated by %s (%s)\n", srvbuf.hostname, 
+ 		    srvbuf.ip_addr );
+ 		return;
+         }
+     }
+ 
+     if (servers == NULL) {
+         servers = (SERVER *) malloc(sizeof(SERVER));
+     } else {
+         servers = (SERVER *) realloc(servers,
+                 sizeof(SERVER) * (server_count+1));
+     }
+     if (servers == NULL) {
+         fprintf(stderr, "ERROR: uread: malloc/realloc failure (servers)\n");
+         exit(1);
+     }
+ 
+     bcopy(&srvbuf, &servers[server_count], sizeof(SERVER));
+     server_count++;
+ 
+     log_msg("usock: new server %s (%s)\n", srvbuf.hostname, srvbuf.ip_addr );
+     return;
+ 
+ truncated:
+     log_msg("uread: truncated packet of %d bytes from %s\n", bytes,
+         inet_ntoa(srvbuf.addr));
+     return;
+ }
+ 
  /*
   * Basic strategy: do blocking I/O until we get everything we want.  Wake up
   * once every 60 seconds to check our status and look for more work to do.
***************
*** 892,897 ****
--- 1110,1116 ----
      int on = 1;
      fd_set readfds, writefds;
      int cc, lsock, *listen_sock = NULL;
+     int usock;
      struct itimerval udt;
  
  #ifdef DEBUG2
***************
*** 904,909 ****
--- 1123,1144 ----
      FD_ZERO(&readfd_set);
      FD_ZERO(&writefd_set);
  
+     /* create UDP socket for servers to send us updates */
+     if ((usock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+         perror("ERROR: unable to create UDP socket for server reports");
+         log_msg("exit: unable to create UDP socket");
+         exit(1);
+     }
+     addr.sin_family = AF_INET;
+     addr.sin_addr.s_addr = INADDR_ANY;
+     addr.sin_port = htons(USOCKPORT);
+     if (bind(usock, &addr, sizeof(addr)) < 0) {
+         perror("ERROR: unable to bind to UDP socket");
+         log_msg("exit: unable to bind to UDP socket");
+         exit(1);
+     }
+     FD_SET(usock, &readfd_set);
+ 
      /* Prepare sockets to accept calls */
      if ((listen_sock = (int *) malloc(port_count * sizeof(int))) == NULL) {
  	fprintf(stderr, "ERROR: malloc failure (main/listen)\n");
***************
*** 966,971 ****
--- 1201,1212 ----
  	    continue;
  	}
  	if (verbose) fseek(stdout, (off_t) 0, 2);
+ 
+ 	/* activity on servers' UDP socket, read it */
+ 	if (FD_ISSET(usock, &readfds)) {
+ 	    uread(usock);
+ 	    FD_CLR(usock, &readfds);
+ 	}
  
  	/* cc is #of interesting file descriptors */
  	for (i = 0; i < maxfd && cc; i++) {

--
James Cameron                                    (cameron@stl.dec.com)
Digital Equipment Corporation (Australia) Pty. Ltd. A.C.N. 000 446 800