new version from Vernon Schryver (vjs@sgi.com); see ../timed/CHANGES for details
authorKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Wed, 12 May 1993 02:51:19 +0000 (18:51 -0800)
committerKeith Bostic <bostic@ucbvax.Berkeley.EDU>
Wed, 12 May 1993 02:51:19 +0000 (18:51 -0800)
SCCS-vsn: usr.sbin/timed/timed/acksend.c 5.1
SCCS-vsn: usr.sbin/timed/timed/byteorder.c 5.1
SCCS-vsn: usr.sbin/timed/timed/candidate.c 5.1
SCCS-vsn: usr.sbin/timed/timed/correct.c 5.1
SCCS-vsn: usr.sbin/timed/timed/globals.h 5.1
SCCS-vsn: usr.sbin/timed/timed/master.c 5.1
SCCS-vsn: usr.sbin/timed/timed/measure.c 5.1
SCCS-vsn: usr.sbin/timed/timed/networkdelta.c 5.1
SCCS-vsn: usr.sbin/timed/timed/pathnames.h 5.5
SCCS-vsn: usr.sbin/timed/timed/readmsg.c 5.1
SCCS-vsn: usr.sbin/timed/timed/slave.c 5.1
SCCS-vsn: usr.sbin/timed/timed/timed.8 6.7
SCCS-vsn: usr.sbin/timed/timed/timed.c 5.1

13 files changed:
usr/src/usr.sbin/timed/timed/acksend.c
usr/src/usr.sbin/timed/timed/byteorder.c
usr/src/usr.sbin/timed/timed/candidate.c
usr/src/usr.sbin/timed/timed/correct.c
usr/src/usr.sbin/timed/timed/globals.h
usr/src/usr.sbin/timed/timed/master.c
usr/src/usr.sbin/timed/timed/measure.c
usr/src/usr.sbin/timed/timed/networkdelta.c
usr/src/usr.sbin/timed/timed/pathnames.h
usr/src/usr.sbin/timed/timed/readmsg.c
usr/src/usr.sbin/timed/timed/slave.c
usr/src/usr.sbin/timed/timed/timed.8
usr/src/usr.sbin/timed/timed/timed.c

index 9008f01..13f53ff 100644 (file)
-/*
- * Copyright (c) 1985 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)acksend.c  2.8 (Berkeley) %G%";
+static char sccsid[] = "@(#)acksend.c  5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-#include "globals.h"
-#include <protocols/timed.h>
+#ifdef sgi
+#ident "$Revision: 1.6 $"
+#endif
 
 
-#define RECEIVED       0
-#define LOST           1
-#define SECFORACK      1       /* seconds */
-#define USECFORACK     0       /* microseconds */
-#define MAXCOUNT       5
+#include "globals.h"
 
 struct tsp *answer;
 
 
 struct tsp *answer;
 
+extern u_short sequence;
+
+void
+xmit(type, seq, addr)
+       int type;
+       u_int seq;
+       struct sockaddr_in *addr;
+{
+       static struct tsp msg;
+
+       msg.tsp_type = type;
+       msg.tsp_seq = seq;
+       msg.tsp_vers = TSPVERSION;
+       (void)strcpy(msg.tsp_name, hostname);
+       bytenetorder(&msg);
+       if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+                  (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) {
+               trace_sendto_err(addr->sin_addr);
+       }
+}
+
+
 /*
 /*
- * Acksend implements reliable datagram transmission by using sequence 
+ * Acksend implements reliable datagram transmission by using sequence
  * numbers and retransmission when necessary.
  * numbers and retransmission when necessary.
- * `name' is the name of the destination
- * `addr' is the address to send to
  * If `name' is ANYADDR, this routine implements reliable broadcast.
  * If `name' is ANYADDR, this routine implements reliable broadcast.
+ *
+ * Because this function calls readmsg(), none of its args may be in
+ *     a message provided by readmsg().
  */
  */
-
-struct tsp *acksend(message, addr, name, ack, net)
-struct tsp *message;
-struct sockaddr_in *addr;
-char *name;
-int ack;
-struct netinfo *net;
+struct tsp *
+acksend(message, addr, name, ack, net, bad)
+       struct tsp *message;                    /* this message */
+       struct sockaddr_in *addr;               /* to here */
+       char *name;
+       int ack;                                /* look for this ack */
+       struct netinfo *net;                    /* receive from this network */
+       int bad;                                /* 1=losing patience */
 {
 {
+       struct timeval twait;
        int count;
        int count;
-       int flag;
-       extern u_short sequence;
-       struct timeval tout;
-       struct tsp *readmsg();
-
-       count = 0;
+       long msec;
 
        message->tsp_vers = TSPVERSION;
        message->tsp_seq = sequence;
        if (trace) {
 
        message->tsp_vers = TSPVERSION;
        message->tsp_seq = sequence;
        if (trace) {
-               fprintf(fd, "acksend: ");
-               if (name == ANYADDR)
-                       fprintf(fd, "broadcast: ");
-               else
-                       fprintf(fd, "%s: ", name);
+               fprintf(fd, "acksend: to %s: ",
+                       (name == ANYADDR ? "broadcast" : name));
                print(message, addr);
        }
        bytenetorder(message);
                print(message, addr);
        }
        bytenetorder(message);
+
+       msec = 200;
+       count = bad ? 1 : 5;    /* 5 packets in 6.4 seconds */
+       answer = 0;
        do {
        do {
-               if (sendto(sock, (char *)message, sizeof(struct tsp), 0,
-                   (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0) {
-                       syslog(LOG_ERR, "acksend: sendto: %m");
-                       exit(1);
+               if (!answer) {
+                       /* do not go crazy transmitting just because the
+                        * other guy cannot keep our sequence numbers
+                        * straight.
+                        */
+                       if (sendto(sock, (char *)message, sizeof(struct tsp),
+                                  0, (struct sockaddr*)addr,
+                                  sizeof(struct sockaddr)) < 0) {
+                               trace_sendto_err(addr->sin_addr);
+                               break;
+                       }
                }
                }
-               tout.tv_sec = SECFORACK;
-               tout.tv_usec = USECFORACK;
-               answer  = readmsg(ack, name, &tout, net);
-               if (answer != NULL) {
+
+               mstotvround(&twait, msec);
+               answer  = readmsg(ack, name, &twait, net);
+               if (answer != 0) {
                        if (answer->tsp_seq != sequence) {
                                if (trace)
                        if (answer->tsp_seq != sequence) {
                                if (trace)
-                                       fprintf(fd, "acksend: seq # %d != %d\n",
-                                           answer->tsp_seq, sequence);
+                                       fprintf(fd,"acksend: seq # %u!=%u\n",
+                                               answer->tsp_seq, sequence);
                                continue;
                        }
                                continue;
                        }
-                       flag = RECEIVED;
-               } else {
-                       flag = LOST;
-                       if (++count == MAXCOUNT) {
-                               break;
-                       }
+                       break;
                }
                }
-       } while (flag != RECEIVED);
+
+               msec *= 2;
+       } while (--count > 0);
        sequence++;
        sequence++;
+
        return(answer);
 }
        return(answer);
 }
index ac11172..f1641e9 100644 (file)
@@ -1,24 +1,27 @@
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)byteorder.c        2.7 (Berkeley) %G%";
+static char sccsid[] = "@(#)byteorder.c        5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.3 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 
 /*
  * Two routines to do the necessary byte swapping for timed protocol
  * messages. Protocol is defined in /usr/include/protocols/timed.h
  */
 
 /*
  * Two routines to do the necessary byte swapping for timed protocol
  * messages. Protocol is defined in /usr/include/protocols/timed.h
  */
-
+void
 bytenetorder(ptr)
 bytenetorder(ptr)
-struct tsp *ptr;
+       struct tsp *ptr;
 {
        ptr->tsp_seq = htons((u_short)ptr->tsp_seq);
        switch (ptr->tsp_type) {
 {
        ptr->tsp_seq = htons((u_short)ptr->tsp_seq);
        switch (ptr->tsp_type) {
@@ -36,8 +39,9 @@ struct tsp *ptr;
        }
 }
 
        }
 }
 
+void
 bytehostorder(ptr)
 bytehostorder(ptr)
-struct tsp *ptr;
+       struct tsp *ptr;
 {
        ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq);
        switch (ptr->tsp_type) {
 {
        ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq);
        switch (ptr->tsp_type) {
index 877cd72..12a8db6 100644 (file)
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)candidate.c        2.7 (Berkeley) %G%";
+static char sccsid[] = "@(#)candidate.c        5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-#include "globals.h"
-#include <protocols/timed.h>
+#ifdef sgi
+#ident "$Revision: 1.9 $"
+#endif
 
 
-#define ELECTIONWAIT   3       /* seconds */
+#include "globals.h"
 
 /*
 
 /*
- * `election' candidates a host as master: it is called by a slave 
- * which runs with the -M option set when its election timeout expires. 
+ * `election' candidates a host as master: it is called by a slave
+ * which runs with the -M option set when its election timeout expires.
  * Note the conservative approach: if a new timed comes up, or another
  * candidate sends an election request, the candidature is withdrawn.
  */
  * Note the conservative approach: if a new timed comes up, or another
  * candidate sends an election request, the candidature is withdrawn.
  */
-
+int
 election(net)
 election(net)
-struct netinfo *net;
+       struct netinfo *net;
 {
 {
-       int ret;
-       struct tsp *resp, msg, *readmsg();
-       struct timeval wait;
-       struct tsp *answer, *acksend();
-       long casual();
-       struct sockaddr_in server;
-
-       syslog(LOG_INFO, "THIS MACHINE IS A CANDIDATE");
-       if (trace) {
-               fprintf(fd, "THIS MACHINE IS A CANDIDATE\n");
+       struct tsp *resp, msg;
+       struct timeval then, wait;
+       struct tsp *answer;
+       struct hosttbl *htp;
+       char loop_lim = 0;
+
+/* This code can get totally confused if it gets slightly behind.  For
+ *     example, if readmsg() has some QUIT messages waiting from the last
+ *     round, we would send an ELECTION message, get the stale QUIT,
+ *     and give up.  This results in network storms when several machines
+ *     do it at once.
+ */
+       wait.tv_sec = 0;
+       wait.tv_usec = 0;
+       while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) {
+               if (trace)
+                       fprintf(fd, "election: discarded stale REFUSE\n");
+       }
+       while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) {
+               if (trace)
+                       fprintf(fd, "election: discarded stale QUIT\n");
        }
 
        }
 
-       ret = MASTER;
-       slvcount = 1;
-
+again:
+       syslog(LOG_INFO, "This machine is a candidate time master");
+       if (trace)
+               fprintf(fd, "This machine is a candidate time master\n");
        msg.tsp_type = TSP_ELECTION;
        msg.tsp_vers = TSPVERSION;
        (void)strcpy(msg.tsp_name, hostname);
        bytenetorder(&msg);
        if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
        msg.tsp_type = TSP_ELECTION;
        msg.tsp_vers = TSPVERSION;
        (void)strcpy(msg.tsp_name, hostname);
        bytenetorder(&msg);
        if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
-               (struct sockaddr *)&net->dest_addr,
-               sizeof(struct sockaddr_in)) < 0) {
-               syslog(LOG_ERR, "sendto: %m");
-               exit(1);
+                  (struct sockaddr*)&net->dest_addr,
+                  sizeof(struct sockaddr)) < 0) {
+               trace_sendto_err(net->dest_addr.sin_addr);
+               return(SLAVE);
        }
 
        }
 
-       do {
-               wait.tv_sec = ELECTIONWAIT;
-               wait.tv_usec = 0;
-               resp = readmsg(TSP_ANY, (char *)ANYADDR, &wait, net);
-               if (resp != NULL) {
-                       switch (resp->tsp_type) {
+       (void)gettimeofday(&then, 0);
+       then.tv_sec += 3;
+       for (;;) {
+               (void)gettimeofday(&wait, 0);
+               timevalsub(&wait,&then,&wait);
+               resp = readmsg(TSP_ANY, ANYADDR, &wait, net);
+               if (!resp)
+                       return(MASTER);
 
 
-                       case TSP_ACCEPT:
-                               (void) addmach(resp->tsp_name, &from);
-                               break;
+               switch (resp->tsp_type) {
 
 
-                       case TSP_MASTERUP:
-                       case TSP_MASTERREQ:
-                               /*
-                                * If a timedaemon is coming up at the same time,
-                                * give up the candidature: it will be the master.
-                                */
-                               ret = SLAVE;
-                               break;
-
-                       case TSP_QUIT:
-                       case TSP_REFUSE:
-                               /*
-                                * Collision: change value of election timer 
-                                * using exponential backoff.
-                                * The value of timer will be recomputed (in slave.c)
-                                * using the original interval when election will 
-                                * be successfully completed.
-                                */
-                               backoff *= 2;
-                               delay2 = casual((long)MINTOUT, 
-                                                       (long)(MAXTOUT * backoff));
-                               ret = SLAVE;
-                               break;
+               case TSP_ACCEPT:
+                       (void)addmach(resp->tsp_name, &from,fromnet);
+                       break;
 
 
-                       case TSP_ELECTION:
-                               /* no master for another round */
-                               msg.tsp_type = TSP_REFUSE;
-                               (void)strcpy(msg.tsp_name, hostname);
-                               server = from;
-                               answer = acksend(&msg, &server, resp->tsp_name,
-                                   TSP_ACK, (struct netinfo *)NULL);
-                               if (answer == NULL) {
-                                       syslog(LOG_ERR, "error in election");
-                               } else {
-                                       (void) addmach(resp->tsp_name, &from);
-                               }
-                               break;
+               case TSP_MASTERUP:
+               case TSP_MASTERREQ:
+                       /*
+                        * If another timedaemon is coming up at the same
+                        * time, give up, and let it be the master.
+                        */
+                       if (++loop_lim < 5
+                           && !good_host_name(resp->tsp_name)) {
+                               (void)addmach(resp->tsp_name, &from,fromnet);
+                               suppress(&from, resp->tsp_name, net);
+                               goto again;
+                       }
+                       rmnetmachs(net);
+                       return(SLAVE);
+
+               case TSP_QUIT:
+               case TSP_REFUSE:
+                       /*
+                        * Collision: change value of election timer
+                        * using exponential backoff. 
+                        *
+                        *  Fooey.
+                        * An exponential backoff on a delay starting at
+                        * 6 to 15 minutes for a process that takes
+                        * milliseconds is silly.  It is particularly
+                        * strange that the original code would increase
+                        * the backoff without bound.
+                        */
+                       rmnetmachs(net);
+                       return(SLAVE);
+
+               case TSP_ELECTION:
+                       /* no master for another round */
+                       htp = addmach(resp->tsp_name,&from,fromnet);
+                       msg.tsp_type = TSP_REFUSE;
+                       (void)strcpy(msg.tsp_name, hostname);
+                       answer = acksend(&msg, &htp->addr, htp->name,
+                                        TSP_ACK, 0, htp->noanswer);
+                       if (!answer) {
+                               syslog(LOG_ERR, "error in election from %s",
+                                      htp->name);
+                       }
+                       break;
 
 
-                       case TSP_SLAVEUP:
-                               (void) addmach(resp->tsp_name, &from);
-                               break;
+               case TSP_SLAVEUP:
+                       (void)addmach(resp->tsp_name, &from,fromnet);
+                       break;
 
 
-                       case TSP_SETDATE:
-                       case TSP_SETDATEREQ:
-                               break;
+               case TSP_SETDATE:
+               case TSP_SETDATEREQ:
+                       break;
 
 
-                       default:
-                               if (trace) {
-                                       fprintf(fd, "candidate: ");
-                                       print(resp, &from);
-                               }
-                               break;
+               default:
+                       if (trace) {
+                               fprintf(fd, "candidate: ");
+                               print(resp, &from);
                        }
                        }
-               } else {
                        break;
                }
                        break;
                }
-       } while (ret == MASTER);
-       return(ret);
+       }
 }
 }
index 55314b5..1ff258c 100644 (file)
-/*
- * Copyright (c) 1985 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)correct.c  2.6 (Berkeley) %G%";
+static char sccsid[] = "@(#)correct.c  5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.16 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#ifdef sgi
+#include <sys/syssgi.h>
+#endif /* sgi */
 
 
-#ifdef MEASURE
-extern FILE *fp;
-#endif
+static void adjclock __P((struct timeval *));
 
 
-/* 
- * `correct' sends to the slaves the corrections for their clocks
+/*
+ * sends to the slaves the corrections for their clocks after fixing our
+ * own
  */
  */
-
+void
 correct(avdelta)
 correct(avdelta)
-long avdelta;
+       long avdelta;
 {
 {
-       int i;
+       struct hosttbl *htp;
        int corr;
        struct timeval adjlocal;
        int corr;
        struct timeval adjlocal;
-       struct tsp msgs;
-       struct timeval mstotvround();
-       struct tsp *answer, *acksend();
-
-#ifdef MEASURE
-       for(i=0; i<slvcount; i++) {
-               if (hp[i].delta == HOSTDOWN)
-                       fprintf(fp, "%s\t", "down");
-               else { 
-                       fprintf(fp, "%d\t", hp[i].delta);
-               }
-       }
-       fprintf(fp, "\n");
-#endif
-       corr = avdelta - hp[0].delta;
-       adjlocal = mstotvround(&corr);
-       adjclock(&adjlocal);
-#ifdef MEASURE
-       fprintf(fp, "%d\t", corr);
-#endif
+       struct tsp to;
+       struct tsp *answer;
+
+       mstotvround(&adjlocal, avdelta);
 
 
-       for(i=1; i<slvcount; i++) {
-               if (hp[i].delta != HOSTDOWN)  {
-                       corr = avdelta - hp[i].delta;
-                       msgs.tsp_time = mstotvround(&corr);
-                       msgs.tsp_type = (u_char)TSP_ADJTIME;
-                       (void)strcpy(msgs.tsp_name, hostname);
-                       answer = acksend(&msgs, &hp[i].addr, hp[i].name,
-                           TSP_ACK, (struct netinfo *)NULL);
-                       if (answer == NULL) {
-                               hp[i].delta = HOSTDOWN;
-#ifdef MEASURE
-                               fprintf(fp, "%s\t", "down");
+       for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+               if (htp->delta != HOSTDOWN)  {
+                       corr = avdelta - htp->delta;
+/* If the other machine is off in the weeds, set its time directly.
+ *     If a slave gets the wrong day, the original code would simply
+ *     fix the minutes.  If you fix a network partition, you can get
+ *     into such situations.
+ */
+                       if (htp->need_set
+                           || corr >= MAXADJ*1000
+                           || corr <= -MAXADJ*1000) {
+                               htp->need_set = 0;
+                               (void)gettimeofday(&to.tsp_time,0);
+                               timevaladd(&to.tsp_time, &adjlocal);
+                               to.tsp_type = TSP_SETTIME;
                        } else {
                        } else {
-                               fprintf(fp, "%d\t", corr);
-#endif
+                               mstotvround(&to.tsp_time, corr);
+                               to.tsp_type = TSP_ADJTIME;
+                       }
+                       (void)strcpy(to.tsp_name, hostname);
+                       answer = acksend(&to, &htp->addr, htp->name,
+                                        TSP_ACK, 0, 0);
+                       if (!answer) {
+                               htp->delta = HOSTDOWN;
+                               syslog(LOG_WARNING,
+                                      "no reply to time correction from %s",
+                                      htp->name);
+                               if (++htp->noanswer >= LOSTHOST) {
+                                       if (trace) {
+                                               fprintf(fd,
+                                            "purging %s for not answering\n",
+                                                       htp->name);
+                                               (void)fflush(fd);
+                                       }
+                                       htp = remmach(htp);
+                               }
                        }
                        }
-               } else {
-#ifdef MEASURE
-                       fprintf(fp, "%s\t", "down");
-#endif
                }
        }
                }
        }
-#ifdef MEASURE
-       fprintf(fp, "\n");
-#endif
-}
 
 
-/* 
- * `mstotvround' rounds up the value of the argument to the 
- * nearest multiple of five, and converts it into a timeval 
- */
-struct timeval mstotvround(x)
-int *x;
-{
-       int temp;
-       struct timeval adj;
-
-       temp = *x % 5;
-       if (temp >= 3)
-               *x = *x-temp+5;
-       else {
-               if (temp <= -3)
-                       *x = *x - temp -5;
-               else 
-                       *x = *x-temp;
-       }
-       adj.tv_sec = *x/1000;
-       adj.tv_usec = (*x-adj.tv_sec*1000)*1000;
-       if (adj.tv_usec < 0) {
-               adj.tv_usec += 1000000;
-               adj.tv_sec--;
-       }
-       return(adj);
+       /*
+        * adjust our own clock now that we are not sending it out
+        */
+       adjclock(&adjlocal);
 }
 
 }
 
+
+static void
 adjclock(corr)
 adjclock(corr)
-struct timeval *corr;
+       struct timeval *corr;
 {
 {
+       static int passes = 0;
+       static int smoother = 0;
+       long delta;                     /* adjustment in usec */
+       long ndelta;
        struct timeval now;
        struct timeval now;
+       struct timeval adj;
+
+       if (!timerisset(corr))
+               return;
 
 
-       if (timerisset(corr)) {
-               if (corr->tv_sec < MAXADJ && corr->tv_sec > - MAXADJ) {
-                       (void)adjtime(corr, (struct timeval *)0);
-               } else {
+       adj = *corr;
+       if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) {
+               delta = adj.tv_sec*1000000 + adj.tv_usec;
+               /* If the correction is less than the minimum round
+                *      trip time for an ICMP packet, and thus
+                *      less than the likely error in the measurement,
+                *      do not do the entire correction.  Do half
+                *      or a quarter of it.
+                */
+
+               if (delta > -MIN_ROUND*1000
+                   && delta < MIN_ROUND*1000) {
+                       if (smoother <= 4)
+                               smoother++;
+                       ndelta = delta >> smoother;
+                       if (trace)
+                               fprintf(fd,
+                                       "trimming delta %ld usec to %ld\n",
+                                       delta, ndelta);
+                       adj.tv_usec = ndelta;
+                       adj.tv_sec = 0;
+               } else if (smoother > 0) {
+                       smoother--;
+               }
+               if (0 > adjtime(corr, 0)) {
+                       syslog(LOG_ERR, "adjtime: %m");
+               }
+               if (passes > 1
+                   && (delta < -BIG_ADJ || delta > BIG_ADJ)) {
+                       smoother = 0;
+                       passes = 0;
                        syslog(LOG_WARNING,
                        syslog(LOG_WARNING,
-                           "clock correction too large to adjust (%d sec)",
-                           corr->tv_sec);
-                       (void) gettimeofday(&now, (struct timezone *)0);
-                       timevaladd(&now, corr);
-                       if (settimeofday(&now, (struct timezone *)0) < 0)
-                               syslog(LOG_ERR, "can't set time");
+                              "large time adjustment of %+.3f sec",
+                              delta/1000000.0);
+               }
+       } else {
+               syslog(LOG_WARNING,
+                      "clock correction %d sec too large to adjust",
+                      adj.tv_sec);
+               (void) gettimeofday(&now, 0);
+               timevaladd(&now, corr);
+               if (settimeofday(&now, 0) < 0)
+                       syslog(LOG_ERR, "settimeofday: %m");
+       }
+
+#ifdef sgi
+       /* Accumulate the total change, and use it to adjust the basic
+        * clock rate.
+        */
+       if (++passes > 2) {
+#define F_USEC_PER_SEC (1000000*1.0)   /* reduce typos */
+#define F_NSEC_PER_SEC (F_USEC_PER_SEC*1000.0)
+
+               extern char *timetrim_fn;
+               extern char *timetrim_wpat;
+               extern long timetrim;
+               extern double tot_adj, hr_adj;  /* totals in nsec */
+               extern double tot_ticks, hr_ticks;
+
+               static double nag_tick;
+               double cur_ticks, hr_delta_ticks, tot_delta_ticks;
+               double tru_tot_adj, tru_hr_adj; /* nsecs of adjustment */
+               double tot_trim, hr_trim;   /* nsec/sec */
+               struct tms tm;
+               FILE *timetrim_st;
+
+               cur_ticks = times(&tm);
+               tot_adj += delta*1000.0;
+               hr_adj += delta*1000.0;
+
+               tot_delta_ticks = cur_ticks-tot_ticks;
+               if (tot_delta_ticks >= 16*SECDAY*CLK_TCK) {
+                       tot_adj -= rint(tot_adj/16);
+                       tot_ticks += rint(tot_delta_ticks/16);
+                       tot_delta_ticks = cur_ticks-tot_ticks;
+               }
+               hr_delta_ticks = cur_ticks-hr_ticks;
+
+               tru_hr_adj = hr_adj + timetrim*rint(hr_delta_ticks/CLK_TCK);
+               tru_tot_adj = (tot_adj
+                              + timetrim*rint(tot_delta_ticks/CLK_TCK));
+
+               if (hr_delta_ticks >= SECDAY*CLK_TCK
+                   || (tot_delta_ticks < 4*SECDAY*CLK_TCK
+                       && hr_delta_ticks >= SECHR*CLK_TCK)
+                   || (trace && hr_delta_ticks >= (SECHR/10)*CLK_TCK)) {
+
+                       tot_trim = rint(tru_tot_adj*CLK_TCK/tot_delta_ticks);
+                       hr_trim = rint(tru_hr_adj*CLK_TCK/hr_delta_ticks);
+
+                       if (trace
+                           || (abs(timetrim - hr_trim) > 100000.0
+                               && 0 == timetrim_fn
+                               && ((cur_ticks - nag_tick)
+                                   >= 24*SECDAY*CLK_TCK))) {
+                               nag_tick = cur_ticks;
+                               syslog(LOG_NOTICE,
+                  "%+.3f/%.2f or %+.3f/%.2f sec/hr; timetrim=%+.0f or %+.0f",
+                                      tru_tot_adj/F_NSEC_PER_SEC,
+                                      tot_delta_ticks/(SECHR*CLK_TCK*1.0),
+                                      tru_hr_adj/F_NSEC_PER_SEC,
+                                      hr_delta_ticks/(SECHR*CLK_TCK*1.0),
+                                      tot_trim,
+                                      hr_trim);
+                       }
+
+                       if (tot_trim < -MAX_TRIM || tot_trim > MAX_TRIM) {
+                               tot_ticks = hr_ticks;
+                               tot_adj = hr_adj;
+                       } else if (0 > syssgi(SGI_SETTIMETRIM,
+                                             (long)tot_trim)) {
+                               syslog(LOG_ERR, "SETTIMETRIM(%d): %m",
+                                      (long)tot_trim);
+                       } else {
+                               if (0 != timetrim_fn) {
+                                   timetrim_st = fopen(timetrim_fn, "w");
+                                   if (0 == timetrim_st) {
+                                       syslog(LOG_ERR, "fopen(%s): %m",
+                                              timetrim_fn);
+                                   } else {
+                                       if (0 > fprintf(timetrim_st,
+                                                       timetrim_wpat,
+                                                       (long)tot_trim,
+                                                       tru_tot_adj,
+                                                       tot_delta_ticks)) {
+                                               syslog(LOG_ERR,
+                                                      "fprintf(%s): %m",
+                                                      timetrim_fn);
+                                       }
+                                       (void)fclose(timetrim_st);
+                                   }
+                               }
+
+                               tot_adj -= ((tot_trim - timetrim)
+                                           * rint(tot_delta_ticks/CLK_TCK));
+                               timetrim = tot_trim;
+                       }
+
+                       hr_ticks = cur_ticks;
+                       hr_adj = 0;
                }
        }
                }
        }
+#endif /* sgi */
 }
 
 }
 
-timevaladd(tv1, tv2)
-       register struct timeval *tv1, *tv2;
+
+/* adjust the time in a message by the time it
+ *     spent in the queue
+ */
+void
+adj_msg_time(msg, now)
+       struct tsp *msg;
+       struct timeval *now;
 {
 {
-       
-       tv1->tv_sec += tv2->tv_sec;
-       tv1->tv_usec += tv2->tv_usec;
-       if (tv1->tv_usec >= 1000000) {
-               tv1->tv_sec++;
-               tv1->tv_usec -= 1000000;
+       msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec);
+       msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec);
+
+       while (msg->tsp_time.tv_usec < 0) {
+               msg->tsp_time.tv_sec--;
+               msg->tsp_time.tv_usec += 1000000;
        }
        }
-       if (tv1->tv_usec < 0) {
-               tv1->tv_sec--;
-               tv1->tv_usec += 1000000;
+       while (msg->tsp_time.tv_usec >= 1000000) {
+               msg->tsp_time.tv_sec++;
+               msg->tsp_time.tv_usec -= 1000000;
        }
 }
        }
 }
index 552cf95..d0e136f 100644 (file)
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  *
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  *
- *     @(#)globals.h   2.7 (Berkeley) %G%
+ *     @(#)globals.h   5.1 (Berkeley) %G%
  */
 
  */
 
+#ifdef sgi
+#ident "$Revision: 1.15 $"
+#endif
+
 #include <sys/param.h>
 #include <sys/param.h>
-#include <stdio.h>
 #include <sys/time.h>
 #include <sys/time.h>
-#include <errno.h>
-#include <syslog.h>
 #include <sys/socket.h>
 #include <sys/socket.h>
+
 #include <netinet/in.h>
 #include <netinet/in.h>
-#include <netdb.h>
 #include <arpa/inet.h>
 
 #include <arpa/inet.h>
 
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <protocols/timed.h>
+#ifdef sgi
+#include <bstring.h>
+#include <sys/clock.h>
+/* use the constant HZ instead of the function CLK_TCK */
+#undef CLK_TCK
+#define CLK_TCK HZ
+#else
+#define        SECHR   (60*60)
+#define        SECDAY  (24*SECHR)
+#endif /* sgi */
+
 extern int errno;
 extern int sock;
 
 extern int errno;
 extern int sock;
 
-#define SAMPLEINTVL    240             /* synch() freq for master, sec */
-#define        MAXADJ          20              /* max correction (sec) for adjtime */
-/*
- * Parameters for network time measurement
- * of each host using ICMP timestamp requests.
+/* Best expected round trip for a measurement.
+ * This is essentially the number of milliseconds per CPU tick (CLK_TCK?).
+ * All delays shorter than this are usually reported as 0.
  */
  */
-#define RANGE          20              /* best expected round-trip time, ms */
-#define MSGS           5               /* # of timestamp replies to average */
-#define TRIALS         10              /* max # of timestamp echos sent */
+#define MIN_ROUND ((1000-1)/CLK_TCK)
+
 
 
-#define MINTOUT                360
+#define SAMPLEINTVL    240             /* synch() freq for master in sec */
+#define        MAXADJ          20              /* max adjtime() correction in sec */
+
+#define MAX_TRIM       3000000         /* max drift in nsec/sec, 0.3% */
+#define BIG_ADJ                (MAX_TRIM/1000*SAMPLEINTVL*2)   /* max good adj */
+
+#define MINTOUT                360             /* election delays, 6-15 minutes */
 #define MAXTOUT                900
 
 #define MAXTOUT                900
 
+#define BAD_STATUS     (-1)
 #define GOOD           1
 #define UNREACHABLE    2
 #define NONSTDTIME     3
 #define GOOD           1
 #define UNREACHABLE    2
 #define NONSTDTIME     3
-#define HOSTDOWN       0x7fffffff
+#define HOSTDOWN       0x7fffffff
+
+#define OFF            0
+#define ON             1
+
+#define MAX_HOPCNT     10              /* max value for tsp_hpcnt */
+
+#define LOSTHOST       3               /* forget after this many failures */
+
+#define VALID_RANGE (MAXADJ*1000)      /* good times in milliseconds */
+#define GOOD_RANGE (MIN_ROUND*2)
+#define VGOOD_RANGE (MIN_ROUND-1)
 
 
-#define OFF    0
-#define ON     1
 
 /*
  * Global and per-network states.
  */
 
 /*
  * Global and per-network states.
  */
-#define NOMASTER       0               /* no master on any network */
-#define SLAVE          1
+#define NOMASTER       0               /* no good master */
+#define SLAVE          1
 #define MASTER         2
 #define IGNORE         4
 #define ALL            (SLAVE|MASTER|IGNORE)
 #define SUBMASTER      (SLAVE|MASTER)
 
 #define MASTER         2
 #define IGNORE         4
 #define ALL            (SLAVE|MASTER|IGNORE)
 #define SUBMASTER      (SLAVE|MASTER)
 
-#define NHOSTS         100     /* max number of hosts controlled by timed */
-
-struct host {
-       char *name;
-       struct sockaddr_in addr;
-       long delta;
+#define NHOSTS         1013            /* max of hosts controlled by timed
+                                        * This must be a prime number.
+                                        */
+struct hosttbl {
+       struct  hosttbl *h_bak;         /* hash chain */
+       struct  hosttbl *h_fwd;
+       struct  hosttbl *l_bak;         /* "sequential" list */
+       struct  hosttbl *l_fwd;
+       struct  netinfo *ntp;
+       struct  sockaddr_in addr;
+       char    name[MAXHOSTNAMELEN+1];
+       u_char  head;                   /* 1=head of hash chain */
+       u_char  good;                   /* 0=trusted host, for averaging */
+       u_char  noanswer;               /* count of failures to answer */
+       u_char  need_set;               /* need a SETTIME */
        u_short seq;
        u_short seq;
+       long    delta;
 };
 
 };
 
+/* closed hash table with internal chaining */
+extern struct hosttbl hosttbl[NHOSTS+1];
+#define self hosttbl[0]
+#define hostname (self.name)
+
+
 struct netinfo {
 struct netinfo {
-       struct netinfo *next;
-       u_long net;
-       u_long mask;
-       struct in_addr my_addr;
-       struct sockaddr_in dest_addr;   /* broadcast addr or point-point */
-       long status;
+       struct  netinfo *next;
+       struct  in_addr net;
+       u_long  mask;
+       struct  in_addr my_addr;
+       struct  sockaddr_in dest_addr;  /* broadcast addr or point-point */
+       long    status;
+       struct timeval slvwait;         /* delay before sending our time */
+       int     quit_count;             /* recent QUITs */
 };
 
 };
 
+#include "extern.h"
+
+#define tvtomsround(tv) ((tv).tv_sec*1000 + ((tv).tv_usec + 500)/1000)
+
 extern struct netinfo *nettab;
 extern int status;
 extern int trace;
 extern int sock;
 extern struct sockaddr_in from;
 extern struct netinfo *nettab;
 extern int status;
 extern int trace;
 extern int sock;
 extern struct sockaddr_in from;
+extern struct timeval from_when;       /* when the last msg arrived */
+extern u_short sequence;               /* TSP message sequence number */
 extern struct netinfo *fromnet, *slavenet;
 extern FILE *fd;
 extern struct netinfo *fromnet, *slavenet;
 extern FILE *fd;
-extern char hostname[];
-extern char tracefile[];
-extern struct host hp[];
-extern int backoff;
 extern long delay1, delay2;
 extern long delay1, delay2;
-extern int slvcount;
-extern int nslavenets;         /* Number of nets were I could be a slave */
-extern int nmasternets;                /* Number of nets were I could be a master */
-extern int nignorednets;       /* Number of ignored nets */
-extern int nnets;              /* Number of nets I am connected to */
+extern int nslavenets;                 /* nets were I could be a slave */
+extern int nmasternets;                        /* nets were I could be a master */
+extern int nignorednets;               /* ignored nets */
+extern int nnets;                      /* nets I am connected to */
+
+
+#define trace_msg(msg)         {if (trace) fprintf(fd, msg);}
+
+#define trace_sendto_err(addr) {                                       \
+       int st_errno = errno;                                           \
+       syslog(LOG_ERR, "%s %d: sendto %s: %m",                         \
+               __FILE__, __LINE__, inet_ntoa(addr));                   \
+       if (trace)                                                      \
+               fprintf(fd, "%s %d: sendto %s: %d", __FILE__, __LINE__, \
+                       inet_ntoa(addr), st_errno);                     \
+}
+
 
 
-char *strcpy(), *malloc();
+# define max(a,b)      (a<b ? b : a)
+# define min(a,b)      (a>b ? b : a)
+# define abs(x)                (x>=0 ? x : -(x))
index 3924f31..21651f1 100644 (file)
-/*
- * Copyright (c) 1985 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)master.c   2.20 (Berkeley) %G%";
+static char sccsid[] = "@(#)master.c   5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.21 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 #include <sys/file.h>
 #include <sys/file.h>
+#include <sys/types.h>
+#include <sys/times.h>
 #include <setjmp.h>
 #include <setjmp.h>
+#ifdef sgi
+#include <sys/schedctl.h>
+#endif /* sgi */
 #include <utmp.h>
 #include "pathnames.h"
 
 #include <utmp.h>
 #include "pathnames.h"
 
-extern int machup;
 extern int measure_delta;
 extern jmp_buf jmpenv;
 extern int measure_delta;
 extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
 
 
-extern u_short sequence;
+static int dictate;
+static int slvcount;                   /* slaves listening to our clock */
 
 
-#ifdef MEASURE
-int header;
-FILE *fp = NULL;
-#endif
+static void mchgdate __P((struct tsp *));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
 
 /*
 
 /*
- * The main function of `master' is to periodically compute the differences 
- * (deltas) between its clock and the clocks of the slaves, to compute the 
- * network average delta, and to send to the slaves the differences between 
+ * The main function of `master' is to periodically compute the differences
+ * (deltas) between its clock and the clocks of the slaves, to compute the
+ * network average delta, and to send to the slaves the differences between
  * their individual deltas and the network delta.
  * While waiting, it receives messages from the slaves (i.e. requests for
  * master's name, remote requests to set the network time, ...), and
  * takes the appropriate action.
  */
  * their individual deltas and the network delta.
  * While waiting, it receives messages from the slaves (i.e. requests for
  * master's name, remote requests to set the network time, ...), and
  * takes the appropriate action.
  */
-
+int
 master()
 {
 master()
 {
-       int ind;
+       struct hosttbl *htp;
        long pollingtime;
        long pollingtime;
-       struct timeval wait;
-       struct timeval time;
-       struct timezone tzone;
-       struct tsp *msg, to;
-       struct sockaddr_in saveaddr;
-       int findhost();
-       char *date();
-       struct tsp *readmsg();
-       struct tsp *answer, *acksend();
-       char olddate[32];
-       struct sockaddr_in server;
-       register struct netinfo *ntp;
-
-#ifdef MEASURE
-       if (fp == NULL) {
-               fp = fopen(_PATH_MASTERLOG, "w");
-               setlinebuf(fp);
-       }
-#endif
+#define POLLRATE 4
+       int polls;
+       struct timeval wait, ntime;
+       struct tsp *msg, *answer, to;
+       char newdate[32];
+       struct sockaddr_in taddr;
+       char tname[MAXHOSTNAMELEN];
+       struct netinfo *ntp;
+       int i;
 
 
-       syslog(LOG_INFO, "This machine is master");
+       syslog(LOG_NOTICE, "This machine is master");
        if (trace)
        if (trace)
-               fprintf(fd, "THIS MACHINE IS MASTER\n");
-
-       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+               fprintf(fd, "This machine is master\n");
+       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                if (ntp->status == MASTER)
                        masterup(ntp);
                if (ntp->status == MASTER)
                        masterup(ntp);
-       pollingtime = 0;
-
+       }
+       (void)gettimeofday(&ntime, 0);
+       pollingtime = ntime.tv_sec+3;
+       if (justquit)
+               polls = 0;
+       else
+               polls = POLLRATE-1;
+
+/* Process all outstanding messages before spending the long time necessary
+ *     to update all timers.
+ */
 loop:
 loop:
-       (void)gettimeofday(&time, (struct timezone *)0);
-       if (time.tv_sec >= pollingtime) {
-               pollingtime = time.tv_sec + SAMPLEINTVL;
-               synch(0L);
-
-               for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
-                       to.tsp_type = TSP_LOOP;
-                       to.tsp_vers = TSPVERSION;
-                       to.tsp_seq = sequence++;
-                       to.tsp_hopcnt = 10;
-                       (void)strcpy(to.tsp_name, hostname);
-                       bytenetorder(&to);
-                       if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
-                           (struct sockaddr *)&ntp->dest_addr,
-                           sizeof(struct sockaddr_in)) < 0) {
-                               syslog(LOG_ERR, "sendto: %m");
-                               exit(1);
+       (void)gettimeofday(&ntime, 0);
+       wait.tv_sec = pollingtime - ntime.tv_sec;
+       if (wait.tv_sec < 0)
+               wait.tv_sec = 0;
+       wait.tv_usec = 0;
+       msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+       if (!msg) {
+               (void)gettimeofday(&ntime, 0);
+               if (ntime.tv_sec >= pollingtime) {
+                       pollingtime = ntime.tv_sec + SAMPLEINTVL;
+                       get_goodgroup(0);
+
+/* If a bogus master told us to quit, we can have decided to ignore a
+ * network.  Therefore, periodically try to take over everything.
+ */
+                       polls = (polls + 1) % POLLRATE;
+                       if (0 == polls && nignorednets > 0) {
+                               trace_msg("Looking for nets to re-master\n");
+                               for (ntp = nettab; ntp; ntp = ntp->next) {
+                                       if (ntp->status == IGNORE
+                                           || ntp->status == NOMASTER) {
+                                               lookformaster(ntp);
+                                               if (ntp->status == MASTER) {
+                                                       masterup(ntp);
+                                                       polls = POLLRATE-1;
+                                               }
+                                       }
+                                       if (ntp->status == MASTER
+                                           && --ntp->quit_count < 0)
+                                               ntp->quit_count = 0;
+                               }
+                               if (polls != 0)
+                                       setstatus();
+                       }
+
+                       synch(0L);
+
+                       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+                               to.tsp_type = TSP_LOOP;
+                               to.tsp_vers = TSPVERSION;
+                               to.tsp_seq = sequence++;
+                               to.tsp_hopcnt = MAX_HOPCNT;
+                               (void)strcpy(to.tsp_name, hostname);
+                               bytenetorder(&to);
+                               if (sendto(sock, (char *)&to,
+                                          sizeof(struct tsp), 0,
+                                          (struct sockaddr*)&ntp->dest_addr,
+                                          sizeof(ntp->dest_addr)) < 0) {
+                                  trace_sendto_err(ntp->dest_addr.sin_addr);
+                               }
                        }
                }
                        }
                }
-       }
 
 
-       wait.tv_sec = pollingtime - time.tv_sec;
-       wait.tv_usec = 0;
-       msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
-       if (msg != NULL) {
+
+       } else {
                switch (msg->tsp_type) {
 
                case TSP_MASTERREQ:
                        break;
                switch (msg->tsp_type) {
 
                case TSP_MASTERREQ:
                        break;
+
                case TSP_SLAVEUP:
                case TSP_SLAVEUP:
-                       ind = addmach(msg->tsp_name, &from);
-                       newslave(ind, msg->tsp_seq);
+                       newslave(msg);
                        break;
                        break;
+
                case TSP_SETDATE:
                case TSP_SETDATE:
-                       saveaddr = from;
                        /*
                        /*
-                        * the following line is necessary due to syslog
-                        * calling ctime() which clobbers the static buffer
+                        * XXX check to see it is from ourself
                         */
                         */
-                       (void)strcpy(olddate, date());
-                       (void)gettimeofday(&time, &tzone);
-                       time.tv_sec = msg->tsp_time.tv_sec;
-                       time.tv_usec = msg->tsp_time.tv_usec;
-                       logwtmp("|", "date", "");
-                       (void)settimeofday(&time, &tzone);
-                       logwtmp("}", "date", "");
-                       syslog(LOG_NOTICE, "date changed from: %s", olddate);
-                       msg->tsp_type = TSP_DATEACK;
-                       msg->tsp_vers = TSPVERSION;
-                       (void)strcpy(msg->tsp_name, hostname);
-                       bytenetorder(msg);
-                       if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
-                           (struct sockaddr *)&saveaddr,
-                           sizeof(struct sockaddr_in)) < 0) {
-                               syslog(LOG_ERR, "sendto: %m");
-                               exit(1);
+#ifdef sgi
+                       (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+                       (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+                       if (!good_host_name(msg->tsp_name)) {
+                               syslog(LOG_NOTICE,
+                                      "attempted date change by %s to %s",
+                                      msg->tsp_name, newdate);
+                               spreadtime();
+                               break;
                        }
                        }
-                       spreadtime();
-                       pollingtime = 0;
+
+                       mchgdate(msg);
+                       (void)gettimeofday(&ntime, 0);
+                       pollingtime = ntime.tv_sec + SAMPLEINTVL;
                        break;
                        break;
+
                case TSP_SETDATEREQ:
                case TSP_SETDATEREQ:
-                       ind = findhost(msg->tsp_name);
-                       if (ind < 0) { 
-                           syslog(LOG_WARNING,
-                               "DATEREQ from uncontrolled machine");
-                           break;
+                       if (!fromnet || fromnet->status != MASTER)
+                               break;
+#ifdef sgi
+                       (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+                       (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+                       htp = findhost(msg->tsp_name);
+                       if (htp == 0) {
+                               syslog(LOG_ERR,
+                                      "attempted SET DATEREQ by uncontrolled %s to %s",
+                                      msg->tsp_name, newdate);
+                               break;
                        }
                        }
-                       if (hp[ind].seq !=  msg->tsp_seq) {
-                               hp[ind].seq = msg->tsp_seq;
-                               /*
-                                * the following line is necessary due to syslog
-                                * calling ctime() which clobbers the static buffer
-                                */
-                               (void)strcpy(olddate, date());
-                               (void)gettimeofday(&time, &tzone);
-                               time.tv_sec = msg->tsp_time.tv_sec;
-                               time.tv_usec = msg->tsp_time.tv_usec;
-                               logwtmp("|", "date", "");
-                               (void)settimeofday(&time, &tzone);
-                               logwtmp("{", "date", "");
+                       if (htp->seq == msg->tsp_seq)
+                               break;
+                       htp->seq = msg->tsp_seq;
+                       if (!htp->good) {
                                syslog(LOG_NOTICE,
                                syslog(LOG_NOTICE,
-                                   "date changed by %s from: %s",
-                                   msg->tsp_name, olddate);
+                               "attempted SET DATEREQ by untrusted %s to %s",
+                                      msg->tsp_name, newdate);
                                spreadtime();
                                spreadtime();
-                               pollingtime = 0;
+                               break;
                        }
                        }
+
+                       mchgdate(msg);
+                       (void)gettimeofday(&ntime, 0);
+                       pollingtime = ntime.tv_sec + SAMPLEINTVL;
                        break;
                        break;
+
                case TSP_MSITE:
                case TSP_MSITE:
+                       xmit(TSP_ACK, msg->tsp_seq, &from);
+                       break;
+
                case TSP_MSITEREQ:
                        break;
                case TSP_MSITEREQ:
                        break;
+
                case TSP_TRACEON:
                case TSP_TRACEON:
-                       if (!(trace)) {
-                               fd = fopen(tracefile, "w");
-                               setlinebuf(fd);
-                               fprintf(fd, "Tracing started on: %s\n\n", 
-                                                       date());
-                       }
-                       trace = ON;
+                       traceon();
                        break;
                        break;
+
                case TSP_TRACEOFF:
                case TSP_TRACEOFF:
-                       if (trace) {
-                               fprintf(fd, "Tracing ended on: %s\n", date());
-                               (void)fclose(fd);
-                       }
-#ifdef GPROF
-                       moncontrol(0);
-                       _mcleanup();
-                       moncontrol(1);
-#endif
-                       trace = OFF;
+                       traceoff("Tracing ended at %s\n");
                        break;
                        break;
+
                case TSP_ELECTION:
                case TSP_ELECTION:
+                       if (!fromnet)
+                               break;
+                       if (fromnet->status == MASTER) {
+                               pollingtime = 0;
+                               (void)addmach(msg->tsp_name, &from,fromnet);
+                       }
+                       taddr = from;
+                       (void)strcpy(tname, msg->tsp_name);
                        to.tsp_type = TSP_QUIT;
                        (void)strcpy(to.tsp_name, hostname);
                        to.tsp_type = TSP_QUIT;
                        (void)strcpy(to.tsp_name, hostname);
-                       server = from;
-                       answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
-                           (struct netinfo *)NULL);
+                       answer = acksend(&to, &taddr, tname,
+                                        TSP_ACK, 0, 1);
                        if (answer == NULL) {
                        if (answer == NULL) {
-                               syslog(LOG_ERR, "election error");
-                       } else {
-                               (void) addmach(msg->tsp_name, &from);
+                               syslog(LOG_ERR, "election error by %s",
+                                      tname);
                        }
                        }
-                       pollingtime = 0;
                        break;
                        break;
+
                case TSP_CONFLICT:
                        /*
                case TSP_CONFLICT:
                        /*
-                        * After a network partition, there can be 
-                        * more than one master: the first slave to 
+                        * After a network partition, there can be
+                        * more than one master: the first slave to
                         * come up will notify here the situation.
                         */
                         * come up will notify here the situation.
                         */
-
+                       if (!fromnet || fromnet->status != MASTER)
+                               break;
                        (void)strcpy(to.tsp_name, hostname);
 
                        (void)strcpy(to.tsp_name, hostname);
 
-                       if (fromnet == NULL)
-                               break;
-                       for(;;) {
+                       /* The other master often gets into the same state,
+                        * with boring results if we stay at it forever.
+                        */
+                       ntp = fromnet;  /* (acksend() can leave fromnet=0 */
+                       for (i = 0; i < 3; i++) {
                                to.tsp_type = TSP_RESOLVE;
                                to.tsp_type = TSP_RESOLVE;
-                               answer = acksend(&to, &fromnet->dest_addr,
-                                   (char *)ANYADDR, TSP_MASTERACK, fromnet);
-                               if (answer == NULL)
+                               (void)strcpy(to.tsp_name, hostname);
+                               answer = acksend(&to, &ntp->dest_addr,
+                                                ANYADDR, TSP_MASTERACK,
+                                                ntp, 0);
+                               if (!answer)
                                        break;
                                        break;
+                               htp = addmach(answer->tsp_name,&from,ntp);
                                to.tsp_type = TSP_QUIT;
                                to.tsp_type = TSP_QUIT;
-                               server = from;
-                               msg = acksend(&to, &server, answer->tsp_name,
-                                   TSP_ACK, (struct netinfo *)NULL);
+                               msg = acksend(&to, &htp->addr, htp->name,
+                                             TSP_ACK, 0, htp->noanswer);
                                if (msg == NULL) {
                                if (msg == NULL) {
-                                       syslog(LOG_ERR, "error on sending QUIT");
-                               } else {
-                                       (void) addmach(answer->tsp_name, &from);
+                                       syslog(LOG_ERR,
+                                   "no response from %s to CONFLICT-QUIT",
+                                              htp->name);
                                }
                        }
                                }
                        }
-                       masterup(fromnet);
+                       masterup(ntp);
                        pollingtime = 0;
                        break;
                        pollingtime = 0;
                        break;
+
                case TSP_RESOLVE:
                case TSP_RESOLVE:
+                       if (!fromnet || fromnet->status != MASTER)
+                               break;
                        /*
                         * do not want to call synch() while waiting
                         * to be killed!
                         */
                        /*
                         * do not want to call synch() while waiting
                         * to be killed!
                         */
-                       (void)gettimeofday(&time, (struct timezone *)0);
-                       pollingtime = time.tv_sec + SAMPLEINTVL;
+                       (void)gettimeofday(&ntime, (struct timezone *)0);
+                       pollingtime = ntime.tv_sec + SAMPLEINTVL;
                        break;
                        break;
+
                case TSP_QUIT:
                case TSP_QUIT:
-                       /* become slave */
-#ifdef MEASURE
-                       if (fp != NULL) {
-                               (void)fclose(fp);
-                               fp = NULL;
-                       }
-#endif
-                       longjmp(jmpenv, 2);
+                       doquit(msg);            /* become a slave */
                        break;
                        break;
+
                case TSP_LOOP:
                case TSP_LOOP:
+                       if (!fromnet || fromnet->status != MASTER
+                           || !strcmp(msg->tsp_name, hostname))
+                               break;
                        /*
                         * We should not have received this from a net
                        /*
                         * We should not have received this from a net
-                        * we are master on.  There must be two masters
-                        * in this case.
+                        * we are master on.  There must be two masters.
                         */
                         */
+                       htp = addmach(msg->tsp_name, &from,fromnet);
                        to.tsp_type = TSP_QUIT;
                        (void)strcpy(to.tsp_name, hostname);
                        to.tsp_type = TSP_QUIT;
                        (void)strcpy(to.tsp_name, hostname);
-                       server = from;
-                       answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
-                               (struct netinfo *)NULL);
-                       if (answer == NULL) {
+                       answer = acksend(&to, &htp->addr, htp->name,
+                                        TSP_ACK, 0, 1);
+                       if (!answer) {
                                syslog(LOG_WARNING,
                                syslog(LOG_WARNING,
-                                   "loop breakage: no reply to QUIT");
-                       } else {
-                               (void)addmach(msg->tsp_name, &from);
+                               "loop breakage: no reply from %s=%s to QUIT",
+                                   htp->name, inet_ntoa(htp->addr.sin_addr));
+                               (void)remmach(htp);
                        }
                        }
+
+               case TSP_TEST:
+                       if (trace) {
+                               fprintf(fd,
+               "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
+               nnets, nmasternets, nslavenets, nignorednets);
+                               setstatus();
+                       }
+                       pollingtime = 0;
+                       polls = POLLRATE-1;
+                       break;
+
                default:
                        if (trace) {
                default:
                        if (trace) {
-                               fprintf(fd, "garbage: ");
+                               fprintf(fd, "garbage message: ");
                                print(msg, &from);
                        }
                        break;
                                print(msg, &from);
                        }
                        break;
@@ -271,266 +326,556 @@ loop:
        goto loop;
 }
 
        goto loop;
 }
 
+
 /*
 /*
- * `synch' synchronizes all the slaves by calling measure, 
- * networkdelta and correct 
+ * change the system date on the master
  */
  */
+static void
+mchgdate(msg)
+       struct tsp *msg;
+{
+       char tname[MAXHOSTNAMELEN];
+       char olddate[32];
+       struct timeval otime, ntime;
+
+       (void)strcpy(tname, msg->tsp_name);
+
+       xmit(TSP_DATEACK, msg->tsp_seq, &from);
+
+       (void)strcpy(olddate, date());
+
+       /* adjust time for residence on the queue */
+       (void)gettimeofday(&otime, 0);
+       adj_msg_time(msg,&otime);
+
+       timevalsub(&ntime, &msg->tsp_time, &otime);
+       if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+               /*
+                * do not change the clock if we can adjust it
+                */
+               dictate = 3;
+               synch(tvtomsround(ntime));
+       } else {
+#ifdef sgi
+               if (0 > settimeofday(&msg->tsp_time, 0)) {
+                       syslog(LOG_ERR, "settimeofday(): %m");
+               }
+               logwtmp(&otime, &msg->tsp_time);
+#else
+               logwtmp("|", "date", "");
+               (void)settimeofday(&msg->tsp_time, 0);
+               logwtmp("}", "date", "");
+#endif /* sgi */
+               spreadtime();
+       }
+
+       syslog(LOG_NOTICE, "date changed by %s from %s",
+              tname, olddate);
+}
 
 
+
+/*
+ * synchronize all of the slaves
+ */
+void
 synch(mydelta)
 synch(mydelta)
-long mydelta;
+       long mydelta;
 {
 {
-       int i;
+       struct hosttbl *htp;
        int measure_status;
        int measure_status;
-       long netdelta;
-       struct timeval tack;
-#ifdef MEASURE
-#define MAXLINES       8
-       static int lines = 1;
-       struct timeval start, end;
-#endif
-       int measure();
-       int correct();
-       long networkdelta();
-       char *date();
-
-       if (slvcount > 1) {
-#ifdef MEASURE
-               (void)gettimeofday(&start, (struct timezone *)0);
-               if (header == ON || --lines == 0) {
-                       fprintf(fp, "%s\n", date());
-                       for (i=0; i<slvcount; i++)
-                               fprintf(fp, "%.7s\t", hp[i].name);
-                       fprintf(fp, "\n");
-                       lines = MAXLINES;
-                       header = OFF;
-               }
-#endif
-               machup = 1;
-               hp[0].delta = 0;
-               for(i=1; i<slvcount; i++) {
-                       tack.tv_sec = 0;
-                       tack.tv_usec = 500000;
-                       if ((measure_status = measure(&tack, &hp[i].addr)) <0) {
-                               syslog(LOG_ERR, "measure: %m");
-                               exit(1);
+       struct timeval check, stop, wait;
+#ifdef sgi
+       int pri;
+#endif /* sgi */
+
+       if (slvcount > 0) {
+               if (trace)
+                       fprintf(fd, "measurements starting at %s\n", date());
+               (void)gettimeofday(&check, 0);
+#ifdef sgi
+               /* run fast to get good time */
+               pri = schedctl(NDPRI,0,NDPHIMIN);
+               if (pri < 0)
+                       syslog(LOG_ERR, "schedctl(): %m");
+#endif /* sgi */
+               for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+                       if (htp->noanswer != 0) {
+                               measure_status = measure(500, 100,
+                                                        htp->name,
+                                                        &htp->addr,0);
+                       } else {
+                               measure_status = measure(3000, 100,
+                                                        htp->name,
+                                                        &htp->addr,0);
                        }
                        }
-                       hp[i].delta = measure_delta;
-                       if (measure_status == GOOD)
-                               machup++;
-               }
-               if (status & SLAVE) {
-                       /* called by a submaster */
-                       if (trace)
-                               fprintf(fd, "submaster correct: %d ms.\n",
-                                   mydelta);
-                       correct(mydelta);       
-               } else {
-                       if (machup > 1) {
-                               netdelta = networkdelta();
-                               if (trace)
-                                       fprintf(fd,
-                                           "master correct: %d ms.\n",
-                                           mydelta);
-                               correct(netdelta);
+                       if (measure_status != GOOD) {
+                               /* The slave did not respond.  We have
+                                * just wasted lots of time on it.
+                                */
+                               htp->delta = HOSTDOWN;
+                               if (++htp->noanswer >= LOSTHOST) {
+                                       if (trace) {
+                                               fprintf(fd,
+                                       "purging %s for not answering ICMP\n",
+                                                       htp->name);
+                                               (void)fflush(fd);
+                                       }
+                                       htp = remmach(htp);
+                               }
+                       } else {
+                               htp->delta = measure_delta;
                        }
                        }
-               }
-#ifdef MEASURE
-               gettimeofday(&end, 0);
-               end.tv_sec -= start.tv_sec;
-               end.tv_usec -= start.tv_usec;
-               if (end.tv_usec < 0) {
-                       end.tv_sec -= 1;
-                       end.tv_usec += 1000000;
-               }
-               fprintf(fp, "%d ms.\n", (end.tv_sec*1000+end.tv_usec/1000));
-#endif
-               for(i=1; i<slvcount; i++) {
-                       if (hp[i].delta == HOSTDOWN) {
-                               rmmach(i);
-#ifdef MEASURE
-                               header = ON;
-#endif
+                       (void)gettimeofday(&stop, 0);
+                       timevalsub(&stop, &stop, &check);
+                       if (stop.tv_sec >= 1) {
+                               if (trace)
+                                       (void)fflush(fd);
+                               /*
+                                * ack messages periodically
+                                */
+                               wait.tv_sec = 0;
+                               wait.tv_usec = 0;
+                               if (0 != readmsg(TSP_TRACEON,ANYADDR,
+                                                &wait,0))
+                                       traceon();
+                               (void)gettimeofday(&check, 0);
                        }
                }
                        }
                }
-       } else {
-               if (status & SLAVE) {
-                       correct(mydelta);
+#ifdef sgi
+               if (pri >= 0)
+                       (void)schedctl(NDPRI,0,pri);
+#endif /* sgi */
+               if (trace)
+                       fprintf(fd, "measurements finished at %s\n", date());
+       }
+       if (!(status & SLAVE)) {
+               if (!dictate) {
+                       mydelta = networkdelta();
+               } else {
+                       dictate--;
                }
        }
                }
        }
+       if (trace && (mydelta != 0 || (status & SLAVE)))
+               fprintf(fd,"local correction of %ld ms.\n", mydelta);
+       correct(mydelta);
 }
 
 /*
 }
 
 /*
- * 'spreadtime' sends the time to each slave after the master
- * has received the command to set the network time 
+ * sends the time to each slave after the master
+ * has received the command to set the network time
  */
  */
-
+void
 spreadtime()
 {
 spreadtime()
 {
-       int i;
+       struct hosttbl *htp;
        struct tsp to;
        struct tsp to;
-       struct tsp *answer, *acksend();
+       struct tsp *answer;
 
 
-       for(i=1; i<slvcount; i++) {
+/* Do not listen to the consensus after forcing the time.  This is because
+ *     the consensus takes a while to reach the time we are dictating.
+ */
+       dictate = 2;
+       for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
                to.tsp_type = TSP_SETTIME;
                (void)strcpy(to.tsp_name, hostname);
                to.tsp_type = TSP_SETTIME;
                (void)strcpy(to.tsp_name, hostname);
-               (void)gettimeofday(&to.tsp_time, (struct timezone *)0);
-               answer = acksend(&to, &hp[i].addr, hp[i].name, TSP_ACK,
-                   (struct netinfo *)NULL);
-               if (answer == NULL) {
+               (void)gettimeofday(&to.tsp_time, 0);
+               answer = acksend(&to, &htp->addr, htp->name,
+                                TSP_ACK, 0, htp->noanswer);
+               if (answer == 0) {
+                       /* We client does not respond, then we have
+                        * just wasted lots of time on it.
+                        */
                        syslog(LOG_WARNING,
                        syslog(LOG_WARNING,
-                           "no reply to SETTIME from: %s", hp[i].name);
+                              "no reply to SETTIME from %s", htp->name);
+                       if (++htp->noanswer >= LOSTHOST) {
+                               if (trace) {
+                                       fprintf(fd,
+                                            "purging %s for not answering",
+                                               htp->name);
+                                       (void)fflush(fd);
+                               }
+                               htp = remmach(htp);
+                       }
                }
        }
 }
 
                }
        }
 }
 
-findhost(name)
-char *name;
+void
+prthp(delta)
+       clock_t delta;
 {
 {
+       static time_t next_time;
+       time_t this_time;
+       struct tms tm;
+       struct hosttbl *htp;
+       int length, l;
        int i;
        int i;
-       int ind;
 
 
-       ind = -1;
-       for (i=1; i<slvcount; i++) {
-               if (strcmp(name, hp[i].name) == 0) {
-                       ind = i;
-                       break;
+       if (!fd)                        /* quit if tracing already off */
+               return;
+
+       this_time = times(&tm);
+       if (this_time + delta < next_time)
+               return;
+       next_time = this_time + CLK_TCK;
+
+       fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
+       htp = self.l_fwd;
+       length = 1;
+       for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
+               l = strlen(htp->name) + 1;
+               if (length+l >= 80) {
+                       fprintf(fd, "\n");
+                       length = 0;
                }
                }
+               length += l;
+               fprintf(fd, " %s", htp->name);
        }
        }
-       return(ind);
+       fprintf(fd, "\n");
+}
+
+
+static struct hosttbl *newhost_hash;
+static struct hosttbl *lasthfree = &hosttbl[0];
+
+
+struct hosttbl *                       /* answer or 0 */
+findhost(name)
+       char *name;
+{
+       int i, j;
+       struct hosttbl *htp;
+       char *p;
+
+       j= 0;
+       for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
+               j = (j << 2) ^ *p;
+       newhost_hash = &hosttbl[j % NHOSTS];
+
+       htp = newhost_hash;
+       if (htp->name[0] == '\0')
+               return(0);
+       do {
+               if (!strcmp(name, htp->name))
+                       return(htp);
+               htp = htp->h_fwd;
+       } while (htp != newhost_hash);
+       return(0);
 }
 
 /*
 }
 
 /*
- * 'addmach' adds a host to the list of controlled machines
- * if not already there 
+ * add a host to the list of controlled machines if not already there
  */
  */
-
-addmach(name, addr)
-char *name;
-struct sockaddr_in *addr;
+struct hosttbl *
+addmach(name, addr, ntp)
+       char *name;
+       struct sockaddr_in *addr;
+       struct netinfo *ntp;
 {
 {
-       int ret;
-       int findhost();
+       struct hosttbl *ret, *p, *b, *f;
 
        ret = findhost(name);
 
        ret = findhost(name);
-       if (ret < 0) {
-               hp[slvcount].addr = *addr;
-               hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN);
-               (void)strcpy(hp[slvcount].name, name);
-               hp[slvcount].seq = 0;
-               ret = slvcount;
-               if (slvcount < NHOSTS)
-                       slvcount++;
-               else {
+       if (ret == 0) {
+               if (slvcount >= NHOSTS) {
+                       if (trace) {
+                               fprintf(fd, "no more slots in host table\n");
+                               prthp(CLK_TCK);
+                       }
                        syslog(LOG_ERR, "no more slots in host table");
                        syslog(LOG_ERR, "no more slots in host table");
+                       Mflag = 0;
+                       longjmp(jmpenv, 2); /* give up and be a slave */
+               }
+
+               /* if our home hash slot is occupied, find a free entry
+                * in the hash table
+                */
+               if (newhost_hash->name[0] != '\0') {
+                       do {
+                               ret = lasthfree;
+                               if (++lasthfree > &hosttbl[NHOSTS])
+                                       lasthfree = &hosttbl[1];
+                       } while (ret->name[0] != '\0');
+
+                       if (!newhost_hash->head) {
+                               /* Move an interloper using our home.  Use
+                                * scratch pointers in case the new head is
+                                * pointing to itself.
+                                */
+                               f = newhost_hash->h_fwd;
+                               b = newhost_hash->h_bak;
+                               f->h_bak = ret;
+                               b->h_fwd = ret;
+                               f = newhost_hash->l_fwd;
+                               b = newhost_hash->l_bak;
+                               f->l_bak = ret;
+                               b->l_fwd = ret;
+                               bcopy(newhost_hash,ret,sizeof(*ret));
+                               ret = newhost_hash;
+                               ret->head = 1;
+                               ret->h_fwd = ret;
+                               ret->h_bak = ret;
+                       } else {
+                               /* link to an existing chain in our home
+                                */
+                               ret->head = 0;
+                               p = newhost_hash->h_bak;
+                               ret->h_fwd = newhost_hash;
+                               ret->h_bak = p;
+                               p->h_fwd = ret;
+                               newhost_hash->h_bak = ret;
+                       }
+               } else {
+                       ret = newhost_hash;
+                       ret->head = 1;
+                       ret->h_fwd = ret;
+                       ret->h_bak = ret;
                }
                }
+               ret->addr = *addr;
+               ret->ntp = ntp;
+               (void)strncpy(ret->name, name, sizeof(ret->name));
+               ret->good = good_host_name(name);
+               ret->l_fwd = &self;
+               ret->l_bak = self.l_bak;
+               self.l_bak->l_fwd = ret;
+               self.l_bak = ret;
+               slvcount++;
+
+               ret->noanswer = 0;
+               ret->need_set = 1;
+
        } else {
        } else {
-               /* need to clear sequence number anyhow */
-               hp[ret].seq = 0;
+               ret->noanswer = (ret->noanswer != 0);
        }
        }
-#ifdef MEASURE
-       header = ON;
-#endif
+
+       /* need to clear sequence number anyhow */
+       ret->seq = 0;
        return(ret);
 }
 
        return(ret);
 }
 
+/*
+ * remove the machine with the given index in the host table.
+ */
+struct hosttbl *
+remmach(htp)
+       struct hosttbl *htp;
+{
+       struct hosttbl *lprv, *hnxt, *f, *b;
+
+       if (trace)
+               fprintf(fd, "remove %s\n", htp->name);
+
+       /* get out of the lists */
+       htp->l_fwd->l_bak = lprv = htp->l_bak;
+       htp->l_bak->l_fwd = htp->l_fwd;
+       htp->h_fwd->h_bak = htp->h_bak;
+       htp->h_bak->h_fwd = hnxt = htp->h_fwd;
+
+       /* If we are in the home slot, pull up the chain */
+       if (htp->head && hnxt != htp) {
+               if (lprv == hnxt)
+                       lprv = htp;
+
+               /* Use scratch pointers in case the new head is pointing to
+                * itself.
+                */
+               f = hnxt->h_fwd;
+               b = hnxt->h_bak;
+               f->h_bak = htp;
+               b->h_fwd = htp;
+               f = hnxt->l_fwd;
+               b = hnxt->l_bak;
+               f->l_bak = htp;
+               b->l_fwd = htp;
+               hnxt->head = 1;
+               bcopy(hnxt, htp, sizeof(*htp));
+               lasthfree = hnxt;
+       } else {
+               lasthfree = htp;
+       }
+
+       lasthfree->name[0] = '\0';
+       lasthfree->h_fwd = 0;
+       lasthfree->l_fwd = 0;
+       slvcount--;
+
+       return lprv;
+}
+
+
 /*
  * Remove all the machines from the host table that exist on the given
  * network.  This is called when a master transitions to a slave on a
  * given network.
  */
 /*
  * Remove all the machines from the host table that exist on the given
  * network.  This is called when a master transitions to a slave on a
  * given network.
  */
-
+void
 rmnetmachs(ntp)
 rmnetmachs(ntp)
-       register struct netinfo *ntp;
+       struct netinfo *ntp;
 {
 {
-       int i;
+       struct hosttbl *htp;
 
        if (trace)
 
        if (trace)
-               prthp();
-       for (i = 1; i < slvcount; i++)
-               if ((hp[i].addr.sin_addr.s_addr & ntp->mask) == ntp->net)
-                       rmmach(i--);
+               prthp(CLK_TCK);
+       for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+               if (ntp == htp->ntp)
+                       htp = remmach(htp);
+       }
        if (trace)
        if (trace)
-               prthp();
+               prthp(CLK_TCK);
 }
 
 }
 
-/*
- * remove the machine with the given index in the host table.
- */
-rmmach(ind)
-       int ind;
+void
+masterup(net)
+       struct netinfo *net;
 {
 {
-       if (trace)
-               fprintf(fd, "rmmach: %s\n", hp[ind].name);
-       free(hp[ind].name);
-       hp[ind] = hp[--slvcount];
+       xmit(TSP_MASTERUP, 0, &net->dest_addr);
+
+       /*
+        * Do not tell new slaves our time for a while.  This ensures
+        * we do not tell them to start using our time, before we have
+        * found a good master.
+        */
+       (void)gettimeofday(&net->slvwait, 0);
 }
 
 }
 
-prthp()
+void
+newslave(msg)
+       struct tsp *msg;
 {
 {
-       int i;
+       struct hosttbl *htp;
+       struct tsp *answer, to;
+       struct timeval now;
 
 
-       fprintf(fd, "host table:");
-       for (i=1; i<slvcount; i++)
-               fprintf(fd, " %s", hp[i].name);
-       fprintf(fd, "\n");
+       if (!fromnet || fromnet->status != MASTER)
+               return;
+
+       htp = addmach(msg->tsp_name, &from,fromnet);
+       htp->seq = msg->tsp_seq;
+       if (trace)
+               prthp(0);
+
+       /*
+        * If we are stable, send our time to the slave.
+        * Do not go crazy if the date has been changed.
+        */
+       (void)gettimeofday(&now, 0);
+       if (now.tv_sec >= fromnet->slvwait.tv_sec+3
+           || now.tv_sec < fromnet->slvwait.tv_sec) {
+               to.tsp_type = TSP_SETTIME;
+               (void)strcpy(to.tsp_name, hostname);
+               (void)gettimeofday(&to.tsp_time, 0);
+               answer = acksend(&to, &htp->addr,
+                                htp->name, TSP_ACK,
+                                0, htp->noanswer);
+               if (answer) {
+                       htp->need_set = 0;
+               } else {
+                       syslog(LOG_WARNING,
+                              "no reply to initial SETTIME from %s",
+                              htp->name);
+                       htp->noanswer = LOSTHOST;
+               }
+       }
 }
 
 }
 
-masterup(net)
-struct netinfo *net;
+
+/*
+ * react to a TSP_QUIT:
+ */
+void
+doquit(msg)
+       struct tsp *msg;
 {
 {
-       struct timeval wait;
-       struct tsp to, *msg, *readmsg();
-
-       to.tsp_type = TSP_MASTERUP;
-       to.tsp_vers = TSPVERSION;
-       (void)strcpy(to.tsp_name, hostname);
-       bytenetorder(&to);
-       if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
-           (struct sockaddr *)&net->dest_addr,
-           sizeof(struct sockaddr_in)) < 0) {
-               syslog(LOG_ERR, "sendto: %m");
-               exit(1);
+       if (fromnet->status == MASTER) {
+               if (!good_host_name(msg->tsp_name)) {
+                       if (fromnet->quit_count <= 0) {
+                               syslog(LOG_NOTICE,"untrusted %s told us QUIT",
+                                      msg->tsp_name);
+                               suppress(&from, msg->tsp_name, fromnet);
+                               fromnet->quit_count = 1;
+                               return;
+                       }
+                       syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
+                              msg->tsp_name);
+                       fromnet->quit_count = 2;
+                       fromnet->status = NOMASTER;
+               } else {
+                       fromnet->status = SLAVE;
+               }
+               rmnetmachs(fromnet);
+               longjmp(jmpenv, 2);             /* give up and be a slave */
+
+       } else {
+               if (!good_host_name(msg->tsp_name)) {
+                       syslog(LOG_NOTICE, "untrusted %s told us QUIT",
+                              msg->tsp_name);
+                       fromnet->quit_count = 2;
+               }
        }
        }
+}
 
 
-       for (;;) {
-               wait.tv_sec = 1;
-               wait.tv_usec = 0;
-               msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait, net);
-               if (msg != NULL) {
-                       (void) addmach(msg->tsp_name, &from);
-               } else
-                       break;
+void
+traceon()
+{
+       if (!fd) {
+               fd = fopen(_PATH_TIMEDLOG, "w");
+               if (!fd) {
+                       trace = 0;
+                       return;
+               }
+               fprintf(fd,"Tracing started at %s\n", date());
        }
        }
+       trace = 1;
+       get_goodgroup(1);
+       setstatus();
+       prthp(CLK_TCK);
 }
 
 }
 
-newslave(ind, seq)
-u_short seq;
+
+void
+traceoff(msg)
+       char *msg;
 {
 {
-       struct tsp to;
-       struct tsp *answer, *acksend();
+       get_goodgroup(1);
+       setstatus();
+       prthp(CLK_TCK);
+       if (trace) {
+               fprintf(fd, msg, date());
+               (void)fclose(fd);
+               fd = 0;
+       }
+#ifdef GPROF
+       moncontrol(0);
+       _mcleanup();
+       moncontrol(1);
+#endif
+       trace = OFF;
+}
 
 
-       if (trace)
-               prthp();
-       if (seq == 0 || hp[ind].seq !=  seq) {
-               hp[ind].seq = seq;
-               to.tsp_type = TSP_SETTIME;
-               (void)strcpy(to.tsp_name, hostname);
-               /*
-                * give the upcoming slave the time
-                * to check its input queue before
-                * setting the time
-                */
-               sleep(1);
-               (void) gettimeofday(&to.tsp_time,
-                   (struct timezone *)0);
-               answer = acksend(&to, &hp[ind].addr,
-                   hp[ind].name, TSP_ACK,
-                   (struct netinfo *)NULL);
-               if (answer == NULL) {
-                       syslog(LOG_WARNING,
-                           "no reply to initial SETTIME from: %s",
-                           hp[ind].name);
-                       rmmach(ind);
-               }
+
+#ifdef sgi
+void
+logwtmp(otime, ntime)
+       struct timeval *otime, *ntime;
+{
+       static struct utmp wtmp[2] = {
+               {"","",OTIME_MSG,0,OLD_TIME,0,0,0},
+               {"","",NTIME_MSG,0,NEW_TIME,0,0,0}
+       };
+       static char *wtmpfile = WTMP_FILE;
+       int f;
+
+       wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000;
+       wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000;
+       if (wtmp[0].ut_time == wtmp[1].ut_time)
+               return;
+
+       setutent();
+       (void)pututline(&wtmp[0]);
+       (void)pututline(&wtmp[1]);
+       endutent();
+       if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
+               (void) write(f, (char *)wtmp, sizeof(wtmp));
+               (void) close(f);
        }
 }
        }
 }
+#endif /* sgi */
index fddf9b8..18a0ee9 100644 (file)
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)measure.c  2.8 (Berkeley) %G%";
+static char sccsid[] = "@(#)measure.c  5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.8 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 
-#define BIASP          43199999
-#define BIASN          -43200000
-#define MODULO         86400000
-#define PROCESSING_TIME        5       /* ms. to reduce error in measurement */
+#define MSEC_DAY       (SECDAY*1000)
 
 #define PACKET_IN      1024
 
 
 #define PACKET_IN      1024
 
-extern int id;
-int measure_delta;
+#define MSGS           5               /* timestamps to average */
+#define TRIALS         10              /* max # of timestamps sent */
+
 extern int sock_raw;
 extern int sock_raw;
+
+int measure_delta;
+
 static n_short seqno = 0;
 
 /*
  * Measures the differences between machines' clocks using
  * ICMP timestamp messages.
  */
 static n_short seqno = 0;
 
 /*
  * Measures the differences between machines' clocks using
  * ICMP timestamp messages.
  */
-
-measure(wait, addr)
-struct timeval *wait;
-struct sockaddr_in *addr;
+int                                    /* status val defined in globals.h */
+measure(maxmsec, wmsec, hname, addr, print)
+       u_long maxmsec;                 /* wait this many msec at most */
+       u_long wmsec;                   /* msec to wait for an answer */
+       char *hname;
+       struct sockaddr_in *addr;
+       int print;                      /* print complaints on stderr */
 {
        int length;
 {
        int length;
-       int status;
-       int msgcount, trials;
+       int measure_status;
+       int rcvcount, trials;
        int cc, count;
        fd_set ready;
        int cc, count;
        fd_set ready;
-       long sendtime, recvtime, histime;
-       long min1, min2, diff;
-       register long delta1, delta2;
-       struct timeval tv1, tout;
+       long sendtime, recvtime, histime1, histime2;
+       long idelta, odelta, total;
+       long min_idelta, min_odelta;
+       struct timeval tdone, tcur, ttrans, twait, tout;
        u_char packet[PACKET_IN], opacket[64];
        register struct icmp *icp = (struct icmp *) packet;
        register struct icmp *oicp = (struct icmp *) opacket;
        struct ip *ip = (struct ip *) packet;
 
        u_char packet[PACKET_IN], opacket[64];
        register struct icmp *icp = (struct icmp *) packet;
        register struct icmp *oicp = (struct icmp *) opacket;
        struct ip *ip = (struct ip *) packet;
 
-       min1 = min2 = 0x7fffffff;
-       status = HOSTDOWN;
+       min_idelta = min_odelta = 0x7fffffff;
+       measure_status = HOSTDOWN;
        measure_delta = HOSTDOWN;
        measure_delta = HOSTDOWN;
+       errno = 0;
+
+       /* open raw socket used to measure time differences */
+       if (sock_raw < 0) {
+               sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+               if (sock_raw < 0)  {
+                       syslog(LOG_ERR, "opening raw socket: %m");
+                       goto quit;
+               }
+       }
+           
 
 
-/* empties the icmp input queue */
+       /*
+        * empty the icmp input queue
+        */
        FD_ZERO(&ready);
        FD_ZERO(&ready);
-empty:
-       tout.tv_sec = tout.tv_usec = 0;
-       FD_SET(sock_raw, &ready);
-       if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
-               length = sizeof(struct sockaddr_in);
-               cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 
-                   (struct sockaddr *)NULL, &length);
-               if (cc < 0)
-                       return(-1);
-               goto empty;
+       for (;;) {
+               tout.tv_sec = tout.tv_usec = 0;
+               FD_SET(sock_raw, &ready);
+               if (select(sock_raw+1, &ready, 0,0, &tout)) {
+                       length = sizeof(struct sockaddr_in);
+                       cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+                                     0,&length);
+                       if (cc < 0)
+                               goto quit;
+                       continue;
+               }
+               break;
        }
 
        /*
        }
 
        /*
-        * To measure the difference, select MSGS messages whose round-trip
-        * time is smaller than RANGE if ckrange is 1, otherwise simply
-        * select MSGS messages regardless of round-trip transmission time.
-        * Choose the smallest transmission time in each of the two directions.
-        * Use these two latter quantities to compute the delta between
-        * the two clocks.
+        * Choose the smallest transmission time in each of the two
+        * directions. Use these two latter quantities to compute the delta
+        * between the two clocks.
         */
 
         */
 
-       length = sizeof(struct sockaddr_in);
        oicp->icmp_type = ICMP_TSTAMP;
        oicp->icmp_code = 0;
        oicp->icmp_type = ICMP_TSTAMP;
        oicp->icmp_code = 0;
-       oicp->icmp_cksum = 0;
-       oicp->icmp_id = id;
+       oicp->icmp_id = getpid();
        oicp->icmp_rtime = 0;
        oicp->icmp_ttime = 0;
        oicp->icmp_rtime = 0;
        oicp->icmp_ttime = 0;
+       oicp->icmp_seq = seqno;
+
        FD_ZERO(&ready);
        FD_ZERO(&ready);
-       msgcount = 0;
-       for (trials = 0; msgcount < MSGS && trials < TRIALS; ++trials) {
-               oicp->icmp_seq = ++seqno;
-               oicp->icmp_cksum = 0;
-
-               tout.tv_sec = wait->tv_sec;
-               tout.tv_usec = wait->tv_usec;
-
-               (void)gettimeofday (&tv1, (struct timezone *)0);
-               sendtime = oicp->icmp_otime = (tv1.tv_sec % (24*60*60)) * 1000 
-                                                       + tv1.tv_usec / 1000;
-               oicp->icmp_cksum = in_cksum((u_short *)oicp, sizeof(*oicp));
-       
-               count = sendto(sock_raw, (char *)opacket, sizeof(*oicp), 0, 
-                   (struct sockaddr *)addr, sizeof(struct sockaddr_in));
-               if (count < 0) {
-                       status = UNREACHABLE;
-                       return(-1);
+
+#ifdef sgi
+       sginap(1);                      /* start at a clock tick */
+#endif /* sgi */
+
+       (void)gettimeofday(&tdone, 0);
+       mstotvround(&tout, maxmsec);
+       timevaladd(&tdone, &tout);              /* when we give up */
+
+       mstotvround(&twait, wmsec);
+
+       rcvcount = 0;
+       trials = 0;
+       while (rcvcount < MSGS) {
+               (void)gettimeofday(&tcur, 0);
+
+               /*
+                * keep sending until we have sent the max
+                */
+               if (trials < TRIALS) {
+                       trials++;
+                       oicp->icmp_otime = ((tcur.tv_sec % SECDAY) * 1000
+                                           + tcur.tv_usec / 1000);
+                       oicp->icmp_cksum = 0;
+                       oicp->icmp_cksum = in_cksum((u_short*)oicp,
+                                                   sizeof(*oicp));
+
+                       count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
+                                      (struct sockaddr*)addr,
+                                      sizeof(struct sockaddr));
+                       if (count < 0) {
+                               if (measure_status == HOSTDOWN)
+                                       measure_status = UNREACHABLE;
+                               goto quit;
+                       }
+                       ++oicp->icmp_seq;
+
+                       ttrans = tcur;
+                       timevaladd(&ttrans, &twait);
+               } else {
+                       ttrans = tdone;
                }
                }
-               for (;;) {
+
+               while (rcvcount < trials) {
+                       timevalsub(&tout, &ttrans, &tcur);
+                       if (tout.tv_sec < 0)
+                               tout.tv_sec = 0;
+
                        FD_SET(sock_raw, &ready);
                        FD_SET(sock_raw, &ready);
-                       if ((count = select(FD_SETSIZE, &ready, (fd_set *)0,
-                           (fd_set *)0, &tout)) <= 0)
+                       count = select(sock_raw+1, &ready, (fd_set *)0,
+                                      (fd_set *)0, &tout);
+                       (void)gettimeofday(&tcur, (struct timezone *)0);
+                       if (count <= 0)
                                break;
                                break;
-                       cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0, 
-                           (struct sockaddr *)NULL, &length);
-                       (void)gettimeofday(&tv1, (struct timezone *)0);
+
+                       length = sizeof(struct sockaddr_in);
+                       cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+                                     0,&length);
                        if (cc < 0)
                        if (cc < 0)
-                               return(-1);
+                               goto quit;
+
+                       /* 
+                        * got something.  See if it is ours
+                        */
                        icp = (struct icmp *)(packet + (ip->ip_hl << 2));
                        icp = (struct icmp *)(packet + (ip->ip_hl << 2));
-                       if((icp->icmp_type == ICMP_TSTAMPREPLY) &&
-                           icp->icmp_id == id && icp->icmp_seq == seqno)
-                               break;
-               }
-               if (count <= 0)
-                       continue;               /* resend */
-               recvtime = (tv1.tv_sec % (24*60*60)) * 1000 +
-                   tv1.tv_usec / 1000;
-               diff = recvtime - sendtime;
-               /*
-                * diff can be less than 0 aroud midnight
-                */
-               if (diff < 0)
-                       continue;
-               msgcount++;
-               histime = ntohl((u_long)icp->icmp_rtime);
-               /*
-                * a hosts using a time format different from 
-                * ms. since midnight UT (as per RFC792) should
-                * set the high order bit of the 32-bit time
-                * value it transmits.
-                */
-               if ((histime & 0x80000000) != 0) {
-                       status = NONSTDTIME;
-                       break;
+                       if (cc < sizeof(*ip)
+                           || icp->icmp_type != ICMP_TSTAMPREPLY
+                           || icp->icmp_id != oicp->icmp_id
+                           || icp->icmp_seq < seqno
+                           || icp->icmp_seq >= oicp->icmp_seq)
+                               continue;
+
+
+                       sendtime = ntohl(icp->icmp_otime);
+                       recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
+                                   tcur.tv_usec / 1000);
+
+                       total = recvtime-sendtime;
+                       if (total < 0)  /* do not hassle midnight */
+                               continue;
+
+                       rcvcount++;
+                       histime1 = ntohl(icp->icmp_rtime);
+                       histime2 = ntohl(icp->icmp_ttime);
+                       /*
+                        * a host using a time format different from
+                        * msec. since midnight UT (as per RFC792) should
+                        * set the high order bit of the 32-bit time
+                        * value it transmits.
+                        */
+                       if ((histime1 & 0x80000000) != 0) {
+                               measure_status = NONSTDTIME;
+                               goto quit;
+                       }
+                       measure_status = GOOD;
+
+                       idelta = recvtime-histime2;
+                       odelta = histime1-sendtime;
+
+                       /* do not be confused by midnight */
+                       if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
+                       else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
+
+                       if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
+                       else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
+
+                       /* save the quantization error so that we can get a
+                        * measurement finer than our system clock.
+                        */
+                       if (total < MIN_ROUND) {
+                               measure_delta = (odelta - idelta)/2;
+                               goto quit;
+                       }
+
+                       if (idelta < min_idelta)
+                               min_idelta = idelta;
+                       if (odelta < min_odelta)
+                               min_odelta = odelta;
+
+                       measure_delta = (min_odelta - min_idelta)/2;
                }
                }
-               status = GOOD;
-               delta1 = histime - sendtime;
-               /*
-                * Handles wrap-around to avoid that around 
-                * midnight small time differences appear 
-                * enormous. However, the two machine's clocks
-                * must be within 12 hours from each other.
-                */
-               if (delta1 < BIASN)
-                       delta1 += MODULO;
-               else if (delta1 > BIASP)
-                       delta1 -= MODULO;
-               delta2 = recvtime - histime;
-               if (delta2 < BIASN)
-                       delta2 += MODULO;
-               else if (delta2 > BIASP)
-                       delta2 -= MODULO;
-               if (delta1 < min1)  
-                       min1 = delta1;
-               if (delta2 < min2)
-                       min2 = delta2;
-               if (diff < RANGE) {
-                       min1 = delta1;
-                       min2 = delta2;
+
+               if (tcur.tv_sec > tdone.tv_sec
+                   || (tcur.tv_sec == tdone.tv_sec
+                       && tcur.tv_usec >= tdone.tv_usec))
                        break;
                        break;
-               }
        }
 
        }
 
+quit:
+       seqno += TRIALS;                /* allocate our sequence numbers */
+
        /*
        /*
-        * If no answer is received for TRIALS consecutive times, 
+        * If no answer is received for TRIALS consecutive times,
         * the machine is assumed to be down
         */
         * the machine is assumed to be down
         */
-        if (status == GOOD) {
-               measure_delta = (min1 - min2)/2 + PROCESSING_TIME;
+       if (measure_status == GOOD) {
+               if (trace) {
+                       fprintf(fd,
+                               "measured delta %4d, %d trials to %-15s %s\n",
+                               measure_delta, trials,
+                               inet_ntoa(addr->sin_addr), hname);
+               }
+       } else if (print) {
+               if (errno != 0)
+                       fprintf(stderr, "measure %s: %s\n", hname,
+                               strerror(errno));
+       } else {
+               if (errno != 0) {
+                       syslog(LOG_ERR, "measure %s: %m", hname);
+               } else {
+                       syslog(LOG_ERR, "measure: %s did not respond", hname);
+               }
+               if (trace) {
+                       fprintf(fd,
+                               "measure: %s failed after %d trials\n",
+                               hname, trials);
+                       (void)fflush(fd);
+               }
+       }
+
+       return(measure_status);
+}
+
+
+
+
+
+/*
+ * round a number of milliseconds into a struct timeval
+ */
+void
+mstotvround(res, x)
+       struct timeval *res;
+       long x;
+{
+#ifndef sgi
+       if (x < 0)
+               x = -((-x + 3)/5);
+       else
+               x = (x+3)/5;
+       x *= 5;
+#endif /* sgi */
+       res->tv_sec = x/1000;
+       res->tv_usec = (x-res->tv_sec*1000)*1000;
+       if (res->tv_usec < 0) {
+               res->tv_usec += 1000000;
+               res->tv_sec--;
+       }
+}
+
+void
+timevaladd(tv1, tv2)
+       struct timeval *tv1, *tv2;
+{
+       tv1->tv_sec += tv2->tv_sec;
+       tv1->tv_usec += tv2->tv_usec;
+       if (tv1->tv_usec >= 1000000) {
+               tv1->tv_sec++;
+               tv1->tv_usec -= 1000000;
+       }
+       if (tv1->tv_usec < 0) {
+               tv1->tv_sec--;
+               tv1->tv_usec += 1000000;
+       }
+}
+
+void
+timevalsub(res, tv1, tv2)
+       struct timeval *res, *tv1, *tv2;
+{
+       res->tv_sec = tv1->tv_sec - tv2->tv_sec;
+       res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+       if (res->tv_usec >= 1000000) {
+               res->tv_sec++;
+               res->tv_usec -= 1000000;
+       }
+       if (res->tv_usec < 0) {
+               res->tv_sec--;
+               res->tv_usec += 1000000;
        }
        }
-       return(status);
 }
 }
index a050d3e..6d83cf5 100644 (file)
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)networkdelta.c     2.5 (Berkeley) %G%";
+static char sccsid[] = "@(#)networkdelta.c     5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.4 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 
 
-extern int machup;
+static long median __P((float, float *, long *, long *, unsigned int));
 
 /*
 
 /*
- * `networkdelta' selects the largest set of deltas that fall within the
- * interval RANGE, and uses them to compute the network average delta 
+ * Compute a corrected date.
+ *     Compute the median of the reasonable differences.  First compute
+ *     the median of all authorized differences, and then compute the
+ *     median of all differences that are reasonably close to the first
+ *     median.
+ *
+ * This differs from the original BSD implementation, which looked for
+ *     the largest group of machines with essentially the same date.
+ *     That assumed that machines with bad clocks would be uniformly
+ *     distributed.  Unfortunately, in real life networks, the distribution
+ *     of machines is not uniform among models of machines, and the
+ *     distribution of errors in clocks tends to be quite consistent
+ *     for a given model.  In other words, all model VI Supre Servres
+ *     from GoFast Inc. tend to have about the same error.
+ *     The original BSD implementation would chose the clock of the
+ *     most common model, and discard all others.
+ *
+ *     Therefore, get best we can do is to try to average over all
+ *     of the machines in the network, while discarding "obviously"
+ *     bad values.
  */
  */
-
-long networkdelta()
+long
+networkdelta()
 {
 {
-       int i, j, maxind, minind;
-       int ext;
-       int tempind;
-       long tempdata;
+       struct hosttbl *htp;
+       long med;
+       long lodelta, hidelta;
+       long logood, higood;
        long x[NHOSTS];
        long x[NHOSTS];
-       long average;
-
-       for (i=0; i<slvcount; i++)
-               x[i] = hp[i].delta;
-       for (i=0; i<slvcount-1; i++) {
-               tempdata = x[i];
-               tempind = i;
-               for (j=i+1; j<slvcount; j++) {
-                       if (x[j] < tempdata) {
-                               tempdata = x[j];
-                               tempind = j;
-                       }
+       long *xp;
+       int numdelta;
+       float eps;
+
+       /*
+        * compute the median of the good values
+        */
+       med = 0;
+       numdelta = 1;
+       xp = &x[0];
+       *xp = 0;                        /* account for ourself */
+       for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+               if (htp->good
+                   && htp->noanswer == 0
+                   && htp->delta != HOSTDOWN) {
+                       med += htp->delta;
+                       numdelta++;
+                       *++xp = htp->delta;
                }
                }
-               x[tempind] = x[i];
-               x[i] = tempdata;
        }
 
        }
 
-       /* this piece of code is critical: DO NOT TOUCH IT! */
-/****/
-       i=0; j=1; minind=0; maxind=1;
-       if (machup == 2)
-               goto compute;
+       /*
+        * If we are the only trusted time keeper, then do not change our
+        * clock.  There may be another time keeping service active.
+        */
+       if (numdelta == 1)
+               return 0;
+
+       med /= numdelta;
+       eps = med - x[0];
+       if (trace)
+               fprintf(fd, "median of %d values starting at %ld is about ",
+                       numdelta, med);
+       med = median(med, &eps, &x[0], xp+1, VALID_RANGE);
+
+       /*
+        * compute the median of all values near the good median
+        */
+       hidelta = med + GOOD_RANGE;
+       lodelta = med - GOOD_RANGE;
+       higood = med + VGOOD_RANGE;
+       logood = med - VGOOD_RANGE;
+       xp = &x[0];
+       htp = &self;
        do {
        do {
-               if (x[j]-x[i] <= RANGE)
-                       j++;
-               else {
-                       if (j > i+1) 
-                               j--; 
-                       if ((x[j]-x[i] <= RANGE) && (j-i >= maxind-minind)) {
-                               minind=i;
-                               maxind=j;
-                       }       
-                       i++;
-                       if(i == j)
-                               j++;
+               if (htp->noanswer == 0
+                   && htp->delta >= lodelta
+                   && htp->delta <= hidelta
+                   && (htp->good
+                       || (htp->delta >= logood
+                           && htp->delta <= higood))) {
+                       *xp++ = htp->delta;
                }
                }
-       } while (j < machup);
-       if ((x[machup-1] - x[i] <= RANGE) && (machup-i-1 >= maxind-minind)) {
-               minind=i; maxind=machup-1;
+       } while (&self != (htp = htp->l_fwd));
+
+       if (xp == &x[0]) {
+               if (trace)
+                       fprintf(fd, "nothing close to median %ld\n", med);
+               return med;
        }
        }
-/****/
-compute:
-       ext = maxind - minind + 1;
-       average = 0;
-       for (i=minind; i<=maxind; i++)
-               average += x[i];
-       average /= ext;
-       return(average);
+
+       if (xp == &x[1]) {
+               if (trace)
+                       fprintf(fd, "only value near median is %ld\n", x[0]);
+               return x[0];
+       }
+
+       if (trace)
+               fprintf(fd, "median of %d values starting at %ld is ",
+                       xp-&x[0], med);
+       return median(med, &eps, &x[0], xp, 1);
+}
+
+
+/*
+ * compute the median of an array of signed integers, using the idea
+ *     in <<Numerical Recipes>>.
+ */
+static long
+median(a, eps_ptr, x, xlim, gnuf)
+       float a;                        /* initial guess for the median */
+       float *eps_ptr;                 /* spacing near the median */
+       long *x, *xlim;                 /* the data */
+       unsigned int gnuf;              /* good enough estimate */
+{
+       long *xptr;
+       float ap = LONG_MAX;            /* bounds on the median */
+       float am = -LONG_MAX;
+       float aa;
+       int npts;                       /* # of points above & below guess */
+       float xp;                       /* closet point above the guess */
+       float xm;                       /* closet point below the guess */
+       float eps;
+       float dum, sum, sumx;
+       int pass;
+#define AMP    1.5                     /* smoothing constants */
+#define AFAC   1.5
+
+       eps = *eps_ptr;
+       if (eps < 1.0) {
+               eps = -eps;
+               if (eps < 1.0)
+                       eps = 1.0;
+       }
+
+       for (pass = 1; ; pass++) {      /* loop over the data */
+               sum = 0.0;
+               sumx = 0.0;
+               npts = 0;
+               xp = LONG_MAX;
+               xm = -LONG_MAX;
+
+               for (xptr = x; xptr != xlim; xptr++) {
+                       float xx = *xptr;
+
+                       dum = xx - a;
+                       if (dum != 0.0) {       /* avoid dividing by 0 */
+                               if (dum > 0.0) {
+                                       npts++;
+                                       if (xx < xp)
+                                               xp = xx;
+                               } else {
+                                       npts--;
+                                       if (xx > xm)
+                                               xm = xx;
+                                       dum = -dum;
+                               }
+                               dum = 1.0/(eps + dum);
+                               sum += dum;
+                               sumx += xx * dum;
+                       }
+               }
+
+               if (ap-am < gnuf || sum == 0) {
+                       if (trace)
+                               fprintf(fd,
+                                  "%ld in %d passes; early out balance=%d\n",
+                                       (long)a, pass, npts);
+                       return a;       /* guess was good enough */
+               }
+
+               aa = (sumx/sum-a)*AMP;
+               if (npts >= 2) {        /* guess was too low */
+                       am = a;
+                       aa = xp + max(0.0, aa);;
+                       if (aa > ap)
+                               aa = (a + ap)/2;
+
+               } else if (npts <= -2) {  /* guess was two high */
+                       ap = a;
+                       aa = xm + min(0.0, aa);;
+                       if (aa < am)
+                               aa = (a + am)/2;
+
+               } else {
+                       break;          /* got it */
+               }
+
+               if (a == aa) {
+                       if (trace)
+                               fprintf(fd,
+                                 "%ld in %d passes; force out balance=%d\n",
+                                       (long)a, pass, npts);
+                       return a;
+               }
+               eps = AFAC*abs(aa - a);
+               *eps_ptr = eps;
+               a = aa;
+       }
+
+       if (((x - xlim) % 2) != 0) {    /* even number of points? */
+               if (npts == 0)          /* yes, return an average */
+                       a = (xp+xm)/2;
+               else if (npts > 0)
+                       a =  (a+xp)/2;
+               else
+                       a = (xm+a)/2;
+
+       } else  if (npts != 0) {        /* odd number of points */
+               if (npts > 0)
+                       a = xp;
+               else
+                       a = xm;
+       }
+
+       if (trace)
+               fprintf(fd, "%ld in %d passes\n", (long)a, pass);
+       return a;
 }
 }
index 8e8ef15..b3a344f 100644 (file)
@@ -1,13 +1,16 @@
 /*
 /*
- * Copyright (c) 1989 The Regents of the University of California.
+ * Copyright (c) 1985 Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  * All rights reserved.
  *
  * %sccs.include.redist.c%
- *
- *     @(#)pathnames.h 5.4 (Berkeley) %G%
  */
 
 #include <paths.h>
 
  */
 
 #include <paths.h>
 
+#ifdef sgi
+#define        _PATH_MASTERLOG "/usr/adm/timed.masterlog"
+#define        _PATH_TIMEDLOG  "/usr/adm/timed.log"
+#else
 #define        _PATH_MASTERLOG "/var/log/timed.masterlog"
 #define        _PATH_TIMEDLOG  "/var/log/timed.log"
 #define        _PATH_MASTERLOG "/var/log/timed.masterlog"
 #define        _PATH_TIMEDLOG  "/var/log/timed.log"
+#endif
index 8d0b2ee..d1e38af 100644 (file)
@@ -1,50 +1,47 @@
-/*
- * Copyright (c) 1983 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)readmsg.c  2.13 (Berkeley) %G%";
+static char sccsid[] = "@(#)readmsg.c  5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.17 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 
 extern char *tsptype[];
 
 /*
  * LOOKAT checks if the message is of the requested type and comes from
 
 extern char *tsptype[];
 
 /*
  * LOOKAT checks if the message is of the requested type and comes from
- * the right machine, returning 1 in case of affirmative answer 
+ * the right machine, returning 1 in case of affirmative answer
  */
  */
-
 #define LOOKAT(msg, mtype, mfrom, netp, froms) \
 #define LOOKAT(msg, mtype, mfrom, netp, froms) \
-       (((((mtype) == TSP_ANY) || ((mtype) == (msg).tsp_type)) && \
-       (((mfrom) == NULL) || (strcmp((mfrom), (msg).tsp_name) == 0)) && \
-       (((netp) == NULL) || \
-       (((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net))) \
-       ? 1 : 0)
-
-#define MORETIME(rtime, rtout) \
-       (((rtime).tv_sec > (rtout).tv_sec || \
-           ((rtime).tv_sec == (rtout).tv_sec && \
-               (rtime).tv_usec >= (rtout).tv_usec)) \
-       ? 0 : 1)
+       (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) &&           \
+        ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) &&          \
+        ((netp) == 0 ||                                                \
+         ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
 
 struct timeval rtime, rwait, rtout;
 struct tsp msgin;
 static struct tsplist {
        struct tsp info;
 
 struct timeval rtime, rwait, rtout;
 struct tsp msgin;
 static struct tsplist {
        struct tsp info;
+       struct timeval when;
        struct sockaddr_in addr;
        struct tsplist *p;
 } msgslist;
 struct sockaddr_in from;
 struct netinfo *fromnet;
        struct sockaddr_in addr;
        struct tsplist *p;
 } msgslist;
 struct sockaddr_in from;
 struct netinfo *fromnet;
+struct timeval from_when;
 
 /*
 
 /*
- * `readmsg' returns message `type' sent by `machfrom' if it finds it 
- * either in the receive queue, or in a linked list of previously received 
+ * `readmsg' returns message `type' sent by `machfrom' if it finds it
+ * either in the receive queue, or in a linked list of previously received
  * messages that it maintains.
  * Otherwise it waits to see if the appropriate message arrives within
  * `intvl' seconds. If not, it returns NULL.
  * messages that it maintains.
  * Otherwise it waits to see if the appropriate message arrives within
  * `intvl' seconds. If not, it returns NULL.
@@ -52,29 +49,39 @@ struct netinfo *fromnet;
 
 struct tsp *
 readmsg(type, machfrom, intvl, netfrom)
 
 struct tsp *
 readmsg(type, machfrom, intvl, netfrom)
-
-int type;
-char *machfrom;
-struct timeval *intvl;
-struct netinfo *netfrom;
+       int type;
+       char *machfrom;
+       struct timeval *intvl;
+       struct netinfo *netfrom;
 {
        int length;
        fd_set ready;
        static struct tsplist *head = &msgslist;
        static struct tsplist *tail = &msgslist;
 {
        int length;
        fd_set ready;
        static struct tsplist *head = &msgslist;
        static struct tsplist *tail = &msgslist;
+       static int msgcnt = 0;
        struct tsplist *prev;
        register struct netinfo *ntp;
        register struct tsplist *ptr;
 
        if (trace) {
        struct tsplist *prev;
        register struct netinfo *ntp;
        register struct tsplist *ptr;
 
        if (trace) {
-               fprintf(fd, "looking for %s from %s\n",
-                       tsptype[type], machfrom == NULL ? "ANY" : machfrom);
-               ptr = head->p;
-               fprintf(fd, "msgqueue:\n");
-               while (ptr != NULL) {
-                       fprintf(fd, "\t");
-                       print(&ptr->info, &ptr->addr);
-                       ptr = ptr->p;
+               fprintf(fd, "readmsg: looking for %s from %s, %s\n",
+                       tsptype[type], machfrom == NULL ? "ANY" : machfrom,
+                       netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
+               if (head->p != 0) {
+                       length = 1;
+                       for (ptr = head->p; ptr != 0; ptr = ptr->p) {
+                               /* do not repeat the hundreds of messages */
+                               if (++length > 3) {
+                                       if (ptr == tail) {
+                                               fprintf(fd,"\t ...%d skipped\n",
+                                                       length);
+                                       } else {
+                                               continue;
+                                       }
+                               }
+                               fprintf(fd, length > 1 ? "\t" : "queue:\t");
+                               print(&ptr->info, &ptr->addr);
+                       }
                }
        }
 
                }
        }
 
@@ -82,23 +89,25 @@ struct netinfo *netfrom;
        prev = head;
 
        /*
        prev = head;
 
        /*
-        * Look for the requested message scanning through the 
-        * linked list. If found, return it and free the space 
+        * Look for the requested message scanning through the
+        * linked list. If found, return it and free the space
         */
 
        while (ptr != NULL) {
                if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
         */
 
        while (ptr != NULL) {
                if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
+again:
                        msgin = ptr->info;
                        from = ptr->addr;
                        msgin = ptr->info;
                        from = ptr->addr;
+                       from_when = ptr->when;
                        prev->p = ptr->p;
                        prev->p = ptr->p;
-                       if (ptr == tail) 
+                       if (ptr == tail)
                                tail = prev;
                        free((char *)ptr);
                        fromnet = NULL;
                        if (netfrom == NULL)
                            for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                                    if ((ntp->mask & from.sin_addr.s_addr) ==
                                tail = prev;
                        free((char *)ptr);
                        fromnet = NULL;
                        if (netfrom == NULL)
                            for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                                    if ((ntp->mask & from.sin_addr.s_addr) ==
-                                       ntp->net) {
+                                       ntp->net.s_addr) {
                                            fromnet = ntp;
                                            break;
                                    }
                                            fromnet = ntp;
                                            break;
                                    }
@@ -106,9 +115,23 @@ struct netinfo *netfrom;
                        else
                            fromnet = netfrom;
                        if (trace) {
                        else
                            fromnet = netfrom;
                        if (trace) {
-                               fprintf(fd, "readmsg: ");
+                               fprintf(fd, "readmsg: found ");
                                print(&msgin, &from);
                        }
                                print(&msgin, &from);
                        }
+
+/* The protocol can get far behind.  When it does, it gets
+ *     hopelessly confused.  So delete duplicate messages.
+ */
+                       for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
+                               if (ptr->addr.sin_addr.s_addr
+                                       == from.sin_addr.s_addr
+                                   && ptr->info.tsp_type == msgin.tsp_type) {
+                                       if (trace)
+                                               fprintf(fd, "\tdup ");
+                                       goto again;
+                               }
+                       }
+                       msgcnt--;
                        return(&msgin);
                } else {
                        prev = ptr;
                        return(&msgin);
                } else {
                        prev = ptr;
@@ -118,155 +141,168 @@ struct netinfo *netfrom;
 
        /*
         * If the message was not in the linked list, it may still be
 
        /*
         * If the message was not in the linked list, it may still be
-        * coming from the network. Set the timer and wait 
+        * coming from the network. Set the timer and wait
         * on a select to read the next incoming message: if it is the
         * right one, return it, otherwise insert it in the linked list.
         */
 
         * on a select to read the next incoming message: if it is the
         * right one, return it, otherwise insert it in the linked list.
         */
 
-       (void)gettimeofday(&rtime, (struct timezone *)0);
-       rtout.tv_sec = rtime.tv_sec + intvl->tv_sec;
-       rtout.tv_usec = rtime.tv_usec + intvl->tv_usec;
-       if (rtout.tv_usec > 1000000) {
-               rtout.tv_usec -= 1000000;
-               rtout.tv_sec++;
-       }
-
+       (void)gettimeofday(&rtout, 0);
+       timevaladd(&rtout, intvl);
        FD_ZERO(&ready);
        FD_ZERO(&ready);
-       for (; MORETIME(rtime, rtout);
-           (void)gettimeofday(&rtime, (struct timezone *)0)) {
-               rwait.tv_sec = rtout.tv_sec - rtime.tv_sec;
-               rwait.tv_usec = rtout.tv_usec - rtime.tv_usec;
-               if (rwait.tv_usec < 0) {
-                       rwait.tv_usec += 1000000;
-                       rwait.tv_sec--;
-               }
-               if (rwait.tv_sec < 0) 
+       for (;;) {
+               (void)gettimeofday(&rtime, 0);
+               timevalsub(&rwait, &rtout, &rtime);
+               if (rwait.tv_sec < 0)
                        rwait.tv_sec = rwait.tv_usec = 0;
                        rwait.tv_sec = rwait.tv_usec = 0;
+               else if (rwait.tv_sec == 0
+                        && rwait.tv_usec < 1000000/CLK_TCK)
+                       rwait.tv_usec = 1000000/CLK_TCK;
 
                if (trace) {
 
                if (trace) {
-                       fprintf(fd, "readmsg: wait: (%d %d)\n", 
-                                               rwait.tv_sec, rwait.tv_usec);
+                       fprintf(fd, "readmsg: wait %ld.%6ld at %s\n",
+                               rwait.tv_sec, rwait.tv_usec, date());
+                       /* Notice a full disk, as we flush trace info.
+                        * It is better to flush periodically than at
+                        * every line because the tracing consists of bursts
+                        * of many lines.  Without care, tracing slows
+                        * down the code enough to break the protocol.
+                        */
+                       if (rwait.tv_sec != 0
+                           && EOF == fflush(fd))
+                               traceoff("Tracing ended for cause at %s\n");
                }
                }
-               FD_SET(sock, &ready);
-               if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
-                   &rwait)) {
-                       length = sizeof(struct sockaddr_in);
-                       if (recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 
-                           0, (struct sockaddr *)&from, &length) < 0) {
-                               syslog(LOG_ERR,
-                                   "receiving datagram packet: %m");
-                               exit(1);
-                       }
 
 
-                       bytehostorder(&msgin);
+               FD_SET(sock, &ready);
+               if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0,
+                          &rwait)) {
+                       if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
+                               return(0);
+                       continue;
+               }
+               length = sizeof(from);
+               if (recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
+                            (struct sockaddr*)&from, &length) < 0) {
+                       syslog(LOG_ERR, "recvfrom: %m");
+                       exit(1);
+               }
+               (void)gettimeofday(&from_when, (struct timezone *)0);
+               bytehostorder(&msgin);
 
 
-                       if (msgin.tsp_vers > TSPVERSION) {
-                               if (trace) {
-                                   fprintf(fd, "readmsg: version mismatch\n");
-                                   /* should do a dump of the packet, but... */
-                               }
-                               continue;
+               if (msgin.tsp_vers > TSPVERSION) {
+                       if (trace) {
+                           fprintf(fd,"readmsg: version mismatch\n");
+                           /* should do a dump of the packet */
                        }
                        }
+                       continue;
+               }
 
 
-                       fromnet = NULL;
-                       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
-                               if ((ntp->mask & from.sin_addr.s_addr) ==
-                                   ntp->net) {
-                                       fromnet = ntp;
-                                       break;
-                               }
-
-                       /*
-                        * drop packets from nets we are ignoring permanently
-                        */
-                       if (fromnet == NULL) {
-                               /* 
-                                * The following messages may originate on
-                                * this host with an ignored network address
-                                */
-                               if (msgin.tsp_type != TSP_TRACEON &&
-                                   msgin.tsp_type != TSP_SETDATE &&
-                                   msgin.tsp_type != TSP_MSITE &&
-#ifdef TESTING
-                                   msgin.tsp_type != TSP_TEST &&
-#endif
-                                   msgin.tsp_type != TSP_TRACEOFF) {
-                                       if (trace) {
-                                           fprintf(fd, "readmsg: discarded: ");
-                                           print(&msgin, &from);
-                                       }
-                                       continue;
-                               }
+               fromnet = NULL;
+               for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+                       if ((ntp->mask & from.sin_addr.s_addr) ==
+                           ntp->net.s_addr) {
+                               fromnet = ntp;
+                               break;
                        }
 
                        }
 
+               /*
+                * drop packets from nets we are ignoring permanently
+                */
+               if (fromnet == NULL) {
                        /*
                        /*
-                        * Throw away messages coming from this machine, unless
-                        * they are of some particular type.
-                        * This gets rid of broadcast messages and reduces
-                        * master processing time.
+                        * The following messages may originate on
+                        * this host with an ignored network address
                         */
                         */
-                       if ( !(strcmp(msgin.tsp_name, hostname) != 0 ||
-                                       msgin.tsp_type == TSP_SETDATE ||
-#ifdef TESTING
-                                       msgin.tsp_type == TSP_TEST ||
-#endif
-                                       msgin.tsp_type == TSP_MSITE ||
-                                       (msgin.tsp_type == TSP_LOOP &&
-                                       msgin.tsp_hopcnt != 10) ||
-                                       msgin.tsp_type == TSP_TRACEON ||
-                                       msgin.tsp_type == TSP_TRACEOFF)) {
+                       if (msgin.tsp_type != TSP_TRACEON &&
+                           msgin.tsp_type != TSP_SETDATE &&
+                           msgin.tsp_type != TSP_MSITE &&
+                           msgin.tsp_type != TSP_TEST &&
+                           msgin.tsp_type != TSP_TRACEOFF) {
                                if (trace) {
                                if (trace) {
-                                       fprintf(fd, "readmsg: discarded: ");
-                                       print(&msgin, &from);
+                                   fprintf(fd,"readmsg: discard null net ");
+                                   print(&msgin, &from);
                                }
                                continue;
                        }
                                }
                                continue;
                        }
+               }
 
 
-                       /*
-                        * Send acknowledgements here; this is faster and avoids
-                        * deadlocks that would occur if acks were sent from a 
-                        * higher level routine.  Different acknowledgements are
-                        * necessary, depending on status.
-                        */
-                       if (fromnet->status == MASTER)
-                               masterack();
-                       else if (fromnet->status == SLAVE)
-                               slaveack();
-                       else
-                               ignoreack();
-                               
-                       if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
-                               if (trace) {
-                                       fprintf(fd, "readmsg: ");
-                                       print(&msgin, &from);
-                               }
-                               return(&msgin);
-                       } else {
-                               tail->p = (struct tsplist *)
-                                               malloc(sizeof(struct tsplist)); 
-                               tail = tail->p;
-                               tail->p = NULL;
-                               tail->info = msgin;
-                               tail->addr = from;
+               /*
+                * Throw away messages coming from this machine,
+                * unless they are of some particular type.
+                * This gets rid of broadcast messages and reduces
+                * master processing time.
+                */
+               if (!strcmp(msgin.tsp_name, hostname)
+                   && msgin.tsp_type != TSP_SETDATE
+                   && msgin.tsp_type != TSP_TEST
+                   && msgin.tsp_type != TSP_MSITE
+                   && msgin.tsp_type != TSP_TRACEON
+                   && msgin.tsp_type != TSP_TRACEOFF
+                   && msgin.tsp_type != TSP_LOOP) {
+                       if (trace) {
+                               fprintf(fd, "readmsg: discard own ");
+                               print(&msgin, &from);
+                       }
+                       continue;
+               }
+
+               /*
+                * Send acknowledgements here; this is faster and
+                * avoids deadlocks that would occur if acks were
+                * sent from a higher level routine.  Different
+                * acknowledgements are necessary, depending on
+                * status.
+                */
+               if (fromnet == NULL)    /* do not de-reference 0 */
+                       ignoreack();
+               else if (fromnet->status == MASTER)
+                       masterack();
+               else if (fromnet->status == SLAVE)
+                       slaveack();
+               else
+                       ignoreack();
+
+               if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
+                       if (trace) {
+                               fprintf(fd, "readmsg: ");
+                               print(&msgin, &from);
+                       }
+                       return(&msgin);
+               } else if (++msgcnt > NHOSTS*3) {
+
+/* The protocol gets hopelessly confused if it gets too far
+*      behind.  However, it seems able to recover from all cases of lost
+*      packets.  Therefore, if we are swamped, throw everything away.
+*/
+                       if (trace)
+                               fprintf(fd,
+                                       "readmsg: discarding %d msgs\n",
+                                       msgcnt);
+                       msgcnt = 0;
+                       while ((ptr=head->p) != NULL) {
+                               head->p = ptr->p;
+                               free((char *)ptr);
                        }
                        }
+                       tail = head;
                } else {
                } else {
-                       break;
+                       tail->p = (struct tsplist *)
+                                   malloc(sizeof(struct tsplist));
+                       tail = tail->p;
+                       tail->p = NULL;
+                       tail->info = msgin;
+                       tail->addr = from;
+                       /* timestamp msgs so SETTIMEs are correct */
+                       tail->when = from_when;
                }
        }
                }
        }
-       return((struct tsp *)NULL);
 }
 
 /*
 }
 
 /*
- * `slaveack' sends the necessary acknowledgements: 
- * only the type ACK is to be sent by a slave 
+ * Send the necessary acknowledgements:
+ * only the type ACK is to be sent by a slave
  */
  */
-
+void
 slaveack()
 {
 slaveack()
 {
-       int length;
-       struct tsp resp;
-
-       length = sizeof(struct sockaddr_in);
        switch(msgin.tsp_type) {
 
        case TSP_ADJTIME:
        switch(msgin.tsp_type) {
 
        case TSP_ADJTIME:
@@ -276,22 +312,18 @@ slaveack()
        case TSP_TRACEON:
        case TSP_TRACEOFF:
        case TSP_QUIT:
        case TSP_TRACEON:
        case TSP_TRACEOFF:
        case TSP_QUIT:
-               resp = msgin;
-               resp.tsp_type = TSP_ACK;
-               resp.tsp_vers = TSPVERSION;
-               (void)strcpy(resp.tsp_name, hostname);
                if (trace) {
                        fprintf(fd, "Slaveack: ");
                if (trace) {
                        fprintf(fd, "Slaveack: ");
-                       print(&resp, &from);
-               }
-               bytenetorder(&resp);     /* this is not really necessary here */
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0, 
-                   (struct sockaddr *)&from, length) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
+                       print(&msgin, &from);
                }
                }
+               xmit(TSP_ACK,msgin.tsp_seq, &from);
                break;
                break;
+
        default:
        default:
+               if (trace) {
+                       fprintf(fd, "Slaveack: no ack: ");
+                       print(&msgin, &from);
+               }
                break;
        }
 }
                break;
        }
 }
@@ -300,49 +332,39 @@ slaveack()
  * Certain packets may arrive from this machine on ignored networks.
  * These packets should be acknowledged.
  */
  * Certain packets may arrive from this machine on ignored networks.
  * These packets should be acknowledged.
  */
-
+void
 ignoreack()
 {
 ignoreack()
 {
-       int length;
-       struct tsp resp;
-
-       length = sizeof(struct sockaddr_in);
        switch(msgin.tsp_type) {
 
        case TSP_TRACEON:
        case TSP_TRACEOFF:
        switch(msgin.tsp_type) {
 
        case TSP_TRACEON:
        case TSP_TRACEOFF:
-               resp = msgin;
-               resp.tsp_type = TSP_ACK;
-               resp.tsp_vers = TSPVERSION;
-               (void)strcpy(resp.tsp_name, hostname);
+       case TSP_QUIT:
                if (trace) {
                        fprintf(fd, "Ignoreack: ");
                if (trace) {
                        fprintf(fd, "Ignoreack: ");
-                       print(&resp, &from);
-               }
-               bytenetorder(&resp);     /* this is not really necessary here */
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0, 
-                   (struct sockaddr *)&from, length) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
+                       print(&msgin, &from);
                }
                }
+               xmit(TSP_ACK,msgin.tsp_seq, &from);
                break;
                break;
+
        default:
        default:
+               if (trace) {
+                       fprintf(fd, "Ignoreack: no ack: ");
+                       print(&msgin, &from);
+               }
                break;
        }
 }
 
 /*
                break;
        }
 }
 
 /*
- * `masterack' sends the necessary acknowledgments 
- * to the messages received by a master 
+ * `masterack' sends the necessary acknowledgments
+ * to the messages received by a master
  */
  */
-
+void
 masterack()
 {
 masterack()
 {
-       int length;
        struct tsp resp;
 
        struct tsp resp;
 
-       length = sizeof(struct sockaddr_in);
-
        resp = msgin;
        resp.tsp_vers = TSPVERSION;
        (void)strcpy(resp.tsp_name, hostname);
        resp = msgin;
        resp.tsp_vers = TSPVERSION;
        (void)strcpy(resp.tsp_name, hostname);
@@ -352,92 +374,89 @@ masterack()
        case TSP_QUIT:
        case TSP_TRACEON:
        case TSP_TRACEOFF:
        case TSP_QUIT:
        case TSP_TRACEON:
        case TSP_TRACEOFF:
-       case TSP_MSITE:
        case TSP_MSITEREQ:
        case TSP_MSITEREQ:
-               resp.tsp_type = TSP_ACK;
-               bytenetorder(&resp);
                if (trace) {
                        fprintf(fd, "Masterack: ");
                if (trace) {
                        fprintf(fd, "Masterack: ");
-                       print(&resp, &from);
-               }
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0, 
-                   (struct sockaddr *)&from, length) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
+                       print(&msgin, &from);
                }
                }
+               xmit(TSP_ACK,msgin.tsp_seq, &from);
                break;
                break;
+
        case TSP_RESOLVE:
        case TSP_MASTERREQ:
        case TSP_RESOLVE:
        case TSP_MASTERREQ:
-               resp.tsp_type = TSP_MASTERACK;
-               bytenetorder(&resp);
                if (trace) {
                        fprintf(fd, "Masterack: ");
                if (trace) {
                        fprintf(fd, "Masterack: ");
-                       print(&resp, &from);
-               }
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0, 
-                   (struct sockaddr *)&from, length) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
+                       print(&msgin, &from);
                }
                }
+               xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
                break;
                break;
-       case TSP_SETDATEREQ:
-               resp.tsp_type = TSP_DATEACK;
-               bytenetorder(&resp);
+
+       default:
                if (trace) {
                if (trace) {
-                       fprintf(fd, "Masterack: ");
-                       print(&resp, &from);
-               }
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0, 
-                   (struct sockaddr *)&from, length) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
+                       fprintf(fd,"Masterack: no ack: ");
+                       print(&msgin, &from);
                }
                break;
                }
                break;
-       default:
-               break;
        }
 }
 
 /*
        }
 }
 
 /*
- * Print a TSP message 
+ * Print a TSP message
  */
  */
+void
 print(msg, addr)
 print(msg, addr)
-struct tsp *msg;
-struct sockaddr_in *addr;
+       struct tsp *msg;
+       struct sockaddr_in *addr;
 {
 {
+       char tm[26];
        switch (msg->tsp_type) {
 
        case TSP_LOOP:
        switch (msg->tsp_type) {
 
        case TSP_LOOP:
-               fprintf(fd, "%s %d %d (#%d) %s %s\n",
+               fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
                        msg->tsp_hopcnt,
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
                        msg->tsp_hopcnt,
-                       msg->tsp_name,
-                       inet_ntoa(addr->sin_addr));
+                       inet_ntoa(addr->sin_addr),
+                       msg->tsp_name);
                break;
 
        case TSP_SETTIME:
                break;
 
        case TSP_SETTIME:
-       case TSP_ADJTIME:
        case TSP_SETDATE:
        case TSP_SETDATEREQ:
        case TSP_SETDATE:
        case TSP_SETDATEREQ:
-               fprintf(fd, "%s %d %d (%d, %d) %s %s\n",
+#ifdef sgi
+               (void)cftime(tm, "%D %T", &msg->tsp_time.tv_sec);
+#else
+               strncpy(tm, ctime(&msg->tsp_time.tv_sec)+3+1, sizeof(tm));
+               tm[15] = '\0';          /* ugh */
+#endif /* sgi */
+               fprintf(fd, "%s %d %-6u %s %-15s %s\n",
+                       tsptype[msg->tsp_type],
+                       msg->tsp_vers,
+                       msg->tsp_seq,
+                       tm,
+                       inet_ntoa(addr->sin_addr),
+                       msg->tsp_name);
+               break;
+
+       case TSP_ADJTIME:
+               fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
-                       msg->tsp_time.tv_sec, 
-                       msg->tsp_time.tv_usec, 
-                       msg->tsp_name,
-                       inet_ntoa(addr->sin_addr));
+                       msg->tsp_time.tv_sec,
+                       msg->tsp_time.tv_usec,
+                       inet_ntoa(addr->sin_addr),
+                       msg->tsp_name);
                break;
 
        default:
                break;
 
        default:
-               fprintf(fd, "%s %d %d %s %s\n",
+               fprintf(fd, "%s %d %-6u %-15s %s\n",
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
                        tsptype[msg->tsp_type],
                        msg->tsp_vers,
                        msg->tsp_seq,
-                       msg->tsp_name,
-                       inet_ntoa(addr->sin_addr));
+                       inet_ntoa(addr->sin_addr),
+                       msg->tsp_name);
                break;
        }
 }
                break;
        }
 }
index c35ec48..ae5cedb 100644 (file)
-/*
- * Copyright (c) 1985 Regents of the University of California.
+/*-
+ * Copyright (c) 1985, 1993 The Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)slave.c    2.22 (Berkeley) %G%";
+static char sccsid[] = "@(#)slave.c    5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
+#ifdef sgi
+#ident "$Revision: 1.20 $"
+#endif
+
 #include "globals.h"
 #include "globals.h"
-#include <protocols/timed.h>
 #include <setjmp.h>
 #include "pathnames.h"
 
 extern jmp_buf jmpenv;
 #include <setjmp.h>
 #include "pathnames.h"
 
 extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
 
 extern u_short sequence;
 
 
 extern u_short sequence;
 
+static char master_name[MAXHOSTNAMELEN+1];
+static struct netinfo *old_slavenet;
+static int old_status;
+
+static void schgdate __P((struct tsp *, char *));
+static void setmaster __P((struct tsp *));
+static void answerdelay __P((void));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
+
+int
 slave()
 {
 slave()
 {
-       int length;
-       int senddateack;
-       long electiontime, refusetime, looktime;
+       int tries;
+       long electiontime, refusetime, looktime, looptime, adjtime;
        u_short seq;
        u_short seq;
-       char candidate[MAXHOSTNAMELEN];
-       struct tsp *msg, to, *readmsg();
-       struct sockaddr_in saveaddr, msaveaddr;
-       struct timeval time, wait;
-       struct tsp *answer, *acksend();
+       long fastelection;
+#define FASTTOUT 3
+       struct in_addr cadr;
+       struct timeval otime;
+       struct sockaddr_in taddr;
+       char tname[MAXHOSTNAMELEN];
+       struct tsp *msg, to;
+       struct timeval ntime, wait;
+       struct tsp *answer;
        int timeout();
        int timeout();
-       char *date();
-       long casual();
-       int bytenetorder();
        char olddate[32];
        char olddate[32];
-       struct sockaddr_in server;
-       register struct netinfo *ntp;
-       int ind;
-       struct tsp resp;
-       extern int Mflag;
-       extern int justquit;
-#ifdef MEASURE
-       extern FILE *fp;
-#endif
-       if (slavenet) {
-               resp.tsp_type = TSP_SLAVEUP;
-               resp.tsp_vers = TSPVERSION;
-               (void)strcpy(resp.tsp_name, hostname);
-               bytenetorder(&resp);
-               if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0,
-                   (struct sockaddr *)&slavenet->dest_addr,
-                   sizeof(struct sockaddr_in)) < 0) {
-                       syslog(LOG_ERR, "sendto: %m");
-                       exit(1);
-               }
-       }
+       char newdate[32];
+       struct netinfo *ntp;
+       struct hosttbl *htp;
 
 
-       if (status & MASTER) {
-#ifdef MEASURE
-               if (fp == NULL) {
-                       fp = fopen(_PATH_MASTERLOG, "w");
-                       setlinebuf(fp);
-               }
-#endif
-               syslog(LOG_INFO, "THIS MACHINE IS A SUBMASTER");
-               if (trace) {
-                       fprintf(fd, "THIS MACHINE IS A SUBMASTER\n");
-               }
-               for (ntp = nettab; ntp != NULL; ntp = ntp->next)
-                       if (ntp->status == MASTER)
-                               masterup(ntp);
-
-       } else {
-               syslog(LOG_INFO, "THIS MACHINE IS A SLAVE");
-               if (trace) {
-                       fprintf(fd, "THIS MACHINE IS A SLAVE\n");
-               }
-       }
 
 
+       old_slavenet = 0;
        seq = 0;
        seq = 0;
-       senddateack = OFF;
        refusetime = 0;
        refusetime = 0;
+       adjtime = 0;
 
 
-       (void)gettimeofday(&time, (struct timezone *)0);
-       electiontime = time.tv_sec + delay2;
-       if (Mflag)
-               if (justquit)
-                       looktime = time.tv_sec + delay2;
-               else 
-                       looktime = 1;
+       (void)gettimeofday(&ntime, 0);
+       electiontime = ntime.tv_sec + delay2;
+       fastelection = ntime.tv_sec + FASTTOUT;
+       if (justquit)
+               looktime = electiontime;
        else
        else
-               looktime = 0;
+               looktime = fastelection;
+       looptime = fastelection;
+
+       if (slavenet)
+               xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
+       if (status & MASTER) {
+               for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+                       if (ntp->status == MASTER)
+                               masterup(ntp);
+               }
+       }
 
 loop:
 
 loop:
-       length = sizeof(struct sockaddr_in);
-       (void)gettimeofday(&time, (struct timezone *)0);
-       if (time.tv_sec > electiontime) {
-               if (trace) 
+       get_goodgroup(0);
+       (void)gettimeofday(&ntime, (struct timezone *)0);
+       if (ntime.tv_sec > electiontime) {
+               if (trace)
                        fprintf(fd, "election timer expired\n");
                longjmp(jmpenv, 1);
        }
                        fprintf(fd, "election timer expired\n");
                longjmp(jmpenv, 1);
        }
-       if (looktime && time.tv_sec > looktime) {
-               if (trace) 
-                       fprintf(fd, "Looking for nets to master and loops\n");
-               
-               if (nignorednets > 0) {
+
+       if (ntime.tv_sec >= looktime) {
+               if (trace)
+                       fprintf(fd, "Looking for nets to master\n");
+
+               if (Mflag && nignorednets > 0) {
                        for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                        for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
-                               if (ntp->status == IGNORE) {
+                               if (ntp->status == IGNORE
+                                   || ntp->status == NOMASTER) {
                                        lookformaster(ntp);
                                        lookformaster(ntp);
-                                       if (ntp->status == MASTER)
+                                       if (ntp->status == MASTER) {
                                                masterup(ntp);
                                                masterup(ntp);
-                                       else
-                                               ntp->status = IGNORE;
+                                       } else if (ntp->status == MASTER) {
+                                               ntp->status = NOMASTER;
+                                       }
                                }
                                }
+                               if (ntp->status == MASTER
+                                   && --ntp->quit_count < 0)
+                                       ntp->quit_count = 0;
                        }
                        }
+                       makeslave(slavenet);    /* prune extras */
                        setstatus();
                        setstatus();
-#ifdef MEASURE
-                       /*
-                        * Check to see if we just became master
-                        * (file not open)
-                        */
-                       if (fp == NULL) {
-                               fp = fopen(_PATH_MASTERLOG, "w");
-                               setlinebuf(fp);
-                       }
-#endif
                }
                }
-
+               (void)gettimeofday(&ntime, 0);
+               looktime = ntime.tv_sec + delay2;
+       }
+       if (ntime.tv_sec >= looptime) {
+               if (trace)
+                       fprintf(fd, "Looking for loops\n");
                for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                    if (ntp->status == MASTER) {
                        to.tsp_type = TSP_LOOP;
                        to.tsp_vers = TSPVERSION;
                        to.tsp_seq = sequence++;
                for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                    if (ntp->status == MASTER) {
                        to.tsp_type = TSP_LOOP;
                        to.tsp_vers = TSPVERSION;
                        to.tsp_seq = sequence++;
-                       to.tsp_hopcnt = 10;
+                       to.tsp_hopcnt = MAX_HOPCNT;
                        (void)strcpy(to.tsp_name, hostname);
                        bytenetorder(&to);
                        if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
                        (void)strcpy(to.tsp_name, hostname);
                        bytenetorder(&to);
                        if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
-                           (struct sockaddr *)&ntp->dest_addr,
-                           sizeof(struct sockaddr_in)) < 0) {
-                               syslog(LOG_ERR, "sendto: %m");
-                               exit(1);
+                                  (struct sockaddr*)&ntp->dest_addr,
+                                  sizeof(ntp->dest_addr)) < 0) {
+                               trace_sendto_err(ntp->dest_addr.sin_addr);
                        }
                    }
                }
                        }
                    }
                }
-               (void)gettimeofday(&time, (struct timezone *)0);
-               looktime = time.tv_sec + delay2;
+               (void)gettimeofday(&ntime, 0);
+               looptime = ntime.tv_sec + delay2;
        }
        }
-       wait.tv_sec = electiontime - time.tv_sec + 10;
+
+       wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
+       if (wait.tv_sec < 0)
+               wait.tv_sec = 0;
+       wait.tv_sec += FASTTOUT;
        wait.tv_usec = 0;
        wait.tv_usec = 0;
-       msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
+       msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+
        if (msg != NULL) {
        if (msg != NULL) {
+               /*
+                * filter stuff not for us
+                */
                switch (msg->tsp_type) {
                case TSP_SETDATE:
                switch (msg->tsp_type) {
                case TSP_SETDATE:
-#ifdef TESTING
-               case TSP_TEST:
-#endif
-               case TSP_MSITE:
                case TSP_TRACEOFF:
                case TSP_TRACEON:
                case TSP_TRACEOFF:
                case TSP_TRACEON:
+                       /*
+                        * XXX check to see they are from ourself
+                        */
+                       break;
+
+               case TSP_TEST:
+               case TSP_MSITE:
                        break;
                        break;
+
                case TSP_MASTERUP:
                case TSP_MASTERUP:
-                       if (fromnet == NULL) {
+                       if (!fromnet) {
                                if (trace) {
                                        fprintf(fd, "slave ignored: ");
                                        print(msg, &from);
                                if (trace) {
                                        fprintf(fd, "slave ignored: ");
                                        print(msg, &from);
@@ -168,8 +171,11 @@ loop:
                                goto loop;
                        }
                        break;
                                goto loop;
                        }
                        break;
+
                default:
                default:
-                       if (fromnet == NULL || fromnet->status == IGNORE) {
+                       if (!fromnet
+                           || fromnet->status == IGNORE
+                           || fromnet->status == NOMASTER) {
                                if (trace) {
                                        fprintf(fd, "slave ignored: ");
                                        print(msg, &from);
                                if (trace) {
                                        fprintf(fd, "slave ignored: ");
                                        print(msg, &from);
@@ -179,360 +185,410 @@ loop:
                        break;
                }
 
                        break;
                }
 
+
+               /*
+                * now process the message
+                */
                switch (msg->tsp_type) {
 
                case TSP_ADJTIME:
                switch (msg->tsp_type) {
 
                case TSP_ADJTIME:
-                       if (fromnet->status != SLAVE)
+                       if (fromnet != slavenet)
+                               break;
+                       if (!good_host_name(msg->tsp_name)) {
+                               syslog(LOG_NOTICE,
+                                  "attempted time adjustment by %s",
+                                      msg->tsp_name);
+                               suppress(&from, msg->tsp_name, fromnet);
                                break;
                                break;
-                       (void)gettimeofday(&time, (struct timezone *)0);
-                       electiontime = time.tv_sec + delay2;
+                       }
+                       /*
+                        * Speed up loop detection in case we have a loop.
+                        * Otherwise the clocks can race until the loop
+                        * is found.
+                        */
+                       (void)gettimeofday(&otime, 0);
+                       if (adjtime < otime.tv_sec)
+                               looptime -= (looptime-otime.tv_sec)/2 + 1;
+
+                       setmaster(msg);
                        if (seq != msg->tsp_seq) {
                                seq = msg->tsp_seq;
                        if (seq != msg->tsp_seq) {
                                seq = msg->tsp_seq;
-                               if ((status & SUBMASTER) == SUBMASTER) {
-                                       synch((msg->tsp_time.tv_sec * 1000) + 
-                                           (msg->tsp_time.tv_usec / 1000));
-                               } else {
-                                       adjclock(&(msg->tsp_time));
-                               }
+                               synch(tvtomsround(msg->tsp_time));
                        }
                        }
+                       (void)gettimeofday(&ntime, 0);
+                       electiontime = ntime.tv_sec + delay2;
+                       fastelection = ntime.tv_sec + FASTTOUT;
+                       adjtime = ntime.tv_sec + SAMPLEINTVL*2;
                        break;
                        break;
+
                case TSP_SETTIME:
                case TSP_SETTIME:
-                       if (fromnet->status != SLAVE)
+                       if (fromnet != slavenet)
                                break;
                        if (seq == msg->tsp_seq)
                                break;
                                break;
                        if (seq == msg->tsp_seq)
                                break;
-
                        seq = msg->tsp_seq;
 
                        seq = msg->tsp_seq;
 
+                       /* adjust time for residence on the queue */
+                       (void)gettimeofday(&otime, 0);
+                       adj_msg_time(msg,&otime);
+#ifdef sgi
+                       (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+                       (void)cftime(olddate, "%D %T", &otime.tv_sec);
+#else
+                       /*
+                        * the following line is necessary due to syslog
+                        * calling ctime() which clobbers the static buffer
+                        */
                        (void)strcpy(olddate, date());
                        (void)strcpy(olddate, date());
-                       logwtmp("|", "date", "");
-                       (void)settimeofday(&msg->tsp_time,
-                               (struct timezone *)0);
-                       logwtmp("{", "date", "");
-                       syslog(LOG_NOTICE, "date changed by %s from: %s",
-                               msg->tsp_name, olddate);
-                       if ((status & SUBMASTER) == SUBMASTER)
-                               spreadtime();
-                       (void)gettimeofday(&time, (struct timezone *)0);
-                       electiontime = time.tv_sec + delay2;
+                       (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
 
 
-                       if (senddateack == ON) {
-                               senddateack = OFF;
-                               msg->tsp_type = TSP_DATEACK;
-                               (void)strcpy(msg->tsp_name, hostname);
-                               bytenetorder(msg);
-                               length = sizeof(struct sockaddr_in);
-                               if (sendto(sock, (char *)msg, 
-                                   sizeof(struct tsp), 0,
-                                   (struct sockaddr *)&saveaddr,
-                                   length) < 0) {
-                                       syslog(LOG_ERR, "sendto: %m");
-                                       exit(1);
+                       if (!good_host_name(msg->tsp_name)) {
+                               syslog(LOG_NOTICE,
+                           "attempted time setting by untrusted %s to %s",
+                                      msg->tsp_name, newdate);
+                               suppress(&from, msg->tsp_name, fromnet);
+                               break;
+                       }
+
+                       setmaster(msg);
+                       timevalsub(&ntime, &msg->tsp_time, &otime);
+                       if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+                               /*
+                                * do not change the clock if we can adjust it
+                                */
+                               synch(tvtomsround(ntime));
+                       } else {
+#ifdef sgi
+                               if (0 > settimeofday(&msg->tsp_time, 0)) {
+                                       syslog(LOG_ERR,"settimeofdate(): %m");
+                                       break;
                                }
                                }
+                               logwtmp(&otime, &msg->tsp_time);
+#else
+                               logwtmp("|", "date", "");
+                               (void)settimeofday(&msg->tsp_time, 0);
+                               logwtmp("}", "date", "");
+#endif /* sgi */
+                               syslog(LOG_NOTICE,
+                                      "date changed by %s from %s",
+                                       msg->tsp_name, olddate);
+                               if (status & MASTER)
+                                       spreadtime();
                        }
                        }
+                       (void)gettimeofday(&ntime, 0);
+                       electiontime = ntime.tv_sec + delay2;
+                       fastelection = ntime.tv_sec + FASTTOUT;
+
+/* This patches a bad protocol bug.  Imagine a system with several networks,
+ * where there are a pair of redundant gateways between a pair of networks,
+ * each running timed.  Assume that we start with a third machine mastering
+ * one of the networks, and one of the gateways mastering the other.
+ * Imagine that the third machine goes away and the non-master gateway
+ * decides to replace it.  If things are timed just 'right,' we will have
+ * each gateway mastering one network for a little while.  If a SETTIME
+ * message gets into the network at that time, perhaps from the newly
+ * masterful gateway as it was taking control, the SETTIME will loop
+ * forever.  Each time a gateway receives it on its slave side, it will
+ * call spreadtime to forward it on its mastered network.  We are now in
+ * a permanent loop, since the SETTIME msgs will keep any clock
+ * in the network from advancing.  Normally, the 'LOOP' stuff will detect
+ * and correct the situation.  However, with the clocks stopped, the
+ * 'looptime' timer cannot expire.  While they are in this state, the
+ * masters will try to saturate the network with SETTIME packets.
+ */
+                       looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
                        break;
                        break;
+
                case TSP_MASTERUP:
                        if (slavenet && fromnet != slavenet)
                                break;
                case TSP_MASTERUP:
                        if (slavenet && fromnet != slavenet)
                                break;
+                       if (!good_host_name(msg->tsp_name)) {
+                               suppress(&from, msg->tsp_name, fromnet);
+                               if (electiontime > fastelection)
+                                       electiontime = fastelection;
+                               break;
+                       }
                        makeslave(fromnet);
                        makeslave(fromnet);
+                       setmaster(msg);
                        setstatus();
                        setstatus();
-                       msg->tsp_type = TSP_SLAVEUP;
-                       msg->tsp_vers = TSPVERSION;
-                       (void)strcpy(msg->tsp_name, hostname);
-                       bytenetorder(msg);
                        answerdelay();
                        answerdelay();
-                       length = sizeof(struct sockaddr_in);
-                       if (sendto(sock, (char *)msg, sizeof(struct tsp), 0, 
-                           (struct sockaddr *)&from, length) < 0) {
-                               syslog(LOG_ERR, "sendto: %m");
-                               exit(1);
-                       }
-                       backoff = 1;
-                       delay2 = casual((long)MINTOUT, (long)MAXTOUT);
-                       (void)gettimeofday(&time, (struct timezone *)0);
-                       electiontime = time.tv_sec + delay2;
+                       xmit(TSP_SLAVEUP, 0, &from);
+                       (void)gettimeofday(&ntime, 0);
+                       electiontime = ntime.tv_sec + delay2;
+                       fastelection = ntime.tv_sec + FASTTOUT;
                        refusetime = 0;
                        break;
                        refusetime = 0;
                        break;
+
                case TSP_MASTERREQ:
                        if (fromnet->status != SLAVE)
                                break;
                case TSP_MASTERREQ:
                        if (fromnet->status != SLAVE)
                                break;
-                       (void)gettimeofday(&time, (struct timezone *)0);
-                       electiontime = time.tv_sec + delay2;
+                       (void)gettimeofday(&ntime, 0);
+                       electiontime = ntime.tv_sec + delay2;
                        break;
                        break;
+
                case TSP_SETDATE:
                case TSP_SETDATE:
-                       saveaddr = from;
-                       msg->tsp_type = TSP_SETDATEREQ;
-                       msg->tsp_vers = TSPVERSION;
-                       (void)strcpy(msg->tsp_name, hostname);
-                       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
-                               if (ntp->status == SLAVE)
-                                       break;
-                       }
-                       if (ntp == NULL)
-                               break;
-                       answer = acksend(msg, &ntp->dest_addr, (char *)ANYADDR,
-                           TSP_DATEACK, ntp);
-                       if (answer != NULL) {
-                               msg->tsp_type = TSP_ACK;
-                               bytenetorder(msg);
-                               length = sizeof(struct sockaddr_in);
-                               if (sendto(sock, (char *)msg,
-                                   sizeof(struct tsp), 0,
-                                   (struct sockaddr *)&saveaddr,
-                                   length) < 0) {
-                                       syslog(LOG_ERR, "sendto: %m");
-                                       exit(1);
-                               }
-                               senddateack = ON;
-                       }
+#ifdef sgi
+                       (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+                       (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+                       schgdate(msg, newdate);
                        break;
                        break;
+
                case TSP_SETDATEREQ:
                case TSP_SETDATEREQ:
-                       saveaddr = from;
-                       if (status != SUBMASTER || fromnet->status != MASTER)
+                       if (fromnet->status != MASTER)
+                               break;
+#ifdef sgi
+                       (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+                       (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+                       htp = findhost(msg->tsp_name);
+                       if (0 == htp) {
+                               syslog(LOG_WARNING,
+                                      "DATEREQ from uncontrolled machine");
                                break;
                                break;
-                       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
-                               if (ntp->status == SLAVE)
-                                       break;
-                       }
-                       ind = findhost(msg->tsp_name);
-                       if (ind < 0) {
-                           syslog(LOG_WARNING,
-                               "DATEREQ from uncontrolled machine");
-                           break;
                        }
                        }
-                       syslog(LOG_DEBUG,
-                           "forwarding date change request for %s",
-                           msg->tsp_name);
-                       (void)strcpy(msg->tsp_name, hostname);
-                       answer = acksend(msg, &ntp->dest_addr, (char *)ANYADDR,
-                           TSP_DATEACK, ntp);
-                       if (answer != NULL) {
-                               msg->tsp_type = TSP_DATEACK;
-                               bytenetorder(msg);
-                               length = sizeof(struct sockaddr_in);
-                               if (sendto(sock, (char *)msg,
-                                   sizeof(struct tsp), 0,
-                                   (struct sockaddr *)&saveaddr,
-                                   length) < 0) {
-                                       syslog(LOG_ERR, "sendto: %m");
-                                       exit(1);
-                               }
+                       if (!htp->good) {
+                               syslog(LOG_WARNING,
+                               "attempted date change by untrusted %s to %s",
+                                      htp->name, newdate);
+                               spreadtime();
+                               break;
                        }
                        }
+                       schgdate(msg, newdate);
                        break;
                        break;
+
                case TSP_TRACEON:
                case TSP_TRACEON:
-                       if (!(trace)) {
-                               fd = fopen(tracefile, "w");
-                               setlinebuf(fd);
-                               fprintf(fd, "Tracing started on: %s\n\n", 
-                                                               date());
-                       }
-                       trace = ON;
+                       traceon();
                        break;
                        break;
+
                case TSP_TRACEOFF:
                case TSP_TRACEOFF:
-                       if (trace) {
-                               fprintf(fd, "Tracing ended on: %s\n", date());
-                               (void)fclose(fd);
-                       }
-#ifdef GPROF
-                       moncontrol(0);
-                       _mcleanup();
-                       moncontrol(1);
-#endif
-                       trace = OFF;
+                       traceoff("Tracing ended at %s\n");
                        break;
                        break;
+
                case TSP_SLAVEUP:
                case TSP_SLAVEUP:
-                       if ((status & MASTER) && fromnet->status == MASTER) {
-                               ind = addmach(msg->tsp_name, &from);
-                               newslave(ind, msg->tsp_seq);
-                       }
+                       newslave(msg);
                        break;
                        break;
+
                case TSP_ELECTION:
                        if (fromnet->status == SLAVE) {
                case TSP_ELECTION:
                        if (fromnet->status == SLAVE) {
-                               (void)gettimeofday(&time, (struct timezone *)0);
-                               electiontime = time.tv_sec + delay2;
-                               seq = 0;            /* reset sequence number */
-                               if (time.tv_sec < refusetime)
-                                       msg->tsp_type = TSP_REFUSE;
-                               else {
-                                       msg->tsp_type = TSP_ACCEPT;
-                                       refusetime = time.tv_sec + 30;
+                               (void)gettimeofday(&ntime, 0);
+                               electiontime = ntime.tv_sec + delay2;
+                               fastelection = ntime.tv_sec + FASTTOUT;
+                               seq = 0;
+                               if (!good_host_name(msg->tsp_name)) {
+                                       syslog(LOG_NOTICE,
+                                              "suppress election of %s",
+                                              msg->tsp_name);
+                                       to.tsp_type = TSP_QUIT;
+                                       electiontime = fastelection;
+                               } else if (cadr.s_addr != from.sin_addr.s_addr
+                                          && ntime.tv_sec < refusetime) {
+/* if the candidate has to repeat itself, the old code would refuse it
+ * the second time.  That would prevent elections.
+ */
+                                       to.tsp_type = TSP_REFUSE;
+                               } else {
+                                       cadr.s_addr = from.sin_addr.s_addr;
+                                       to.tsp_type = TSP_ACCEPT;
+                                       refusetime = ntime.tv_sec + 30;
                                }
                                }
-                               (void)strcpy(candidate, msg->tsp_name);
-                               (void)strcpy(msg->tsp_name, hostname);
+                               taddr = from;
+                               (void)strcpy(tname, msg->tsp_name);
+                               (void)strcpy(to.tsp_name, hostname);
                                answerdelay();
                                answerdelay();
-                               server = from;
-                               answer = acksend(msg, &server, candidate, TSP_ACK,
-                                   (struct netinfo *)NULL);
-                               if (answer == NULL)
+                               if (!acksend(&to, &taddr, tname,
+                                            TSP_ACK, 0, 0))
                                        syslog(LOG_WARNING,
                                        syslog(LOG_WARNING,
-                                          "no answer from master candidate\n");
+                                            "no answer from candidate %s\n",
+                                              tname);
+
                        } else {        /* fromnet->status == MASTER */
                        } else {        /* fromnet->status == MASTER */
+                               htp = addmach(msg->tsp_name, &from,fromnet);
                                to.tsp_type = TSP_QUIT;
                                (void)strcpy(to.tsp_name, hostname);
                                to.tsp_type = TSP_QUIT;
                                (void)strcpy(to.tsp_name, hostname);
-                               server = from;
-                               answer = acksend(&to, &server, msg->tsp_name,
-                                   TSP_ACK, (struct netinfo *)NULL);
-                               if (answer == NULL) {
-                                       syslog(LOG_WARNING,
-                                           "election error: no reply to QUIT");
-                               } else {
-                                       (void) addmach(msg->tsp_name, &from);
+                               if (!acksend(&to, &htp->addr, htp->name,
+                                            TSP_ACK, 0, htp->noanswer)) {
+                                       syslog(LOG_ERR,
+                                         "no reply from %s to ELECTION-QUIT",
+                                              htp->name);
+                                       (void)remmach(htp);
                                }
                        }
                        break;
                                }
                        }
                        break;
-                case TSP_CONFLICT:
+
+               case TSP_CONFLICT:
                        if (fromnet->status != MASTER)
                                break;
                        if (fromnet->status != MASTER)
                                break;
-                        /*
-                         * After a network partition, there can be
-                         * more than one master: the first slave to
-                         * come up will notify here the situation.
-                         */
-                        (void)strcpy(to.tsp_name, hostname);
-
-                        if (fromnet == NULL)
-                                break;
-                        for(;;) {
-                                to.tsp_type = TSP_RESOLVE;
-                                answer = acksend(&to, &fromnet->dest_addr,
-                                    (char *)ANYADDR, TSP_MASTERACK, fromnet);
-                                if (answer == NULL)
-                                        break;
-                                to.tsp_type = TSP_QUIT;
-                                server = from;
-                                msg = acksend(&to, &server, answer->tsp_name,
-                                    TSP_ACK, (struct netinfo *)NULL);
-                                if (msg == NULL) {
-                                        syslog(LOG_WARNING,
-                                           "conflict error: no reply to QUIT");
-                               } else {
-                                        (void) addmach(answer->tsp_name, &from);
+                       /*
+                        * After a network partition, there can be
+                        * more than one master: the first slave to
+                        * come up will notify here the situation.
+                        */
+                       (void)strcpy(to.tsp_name, hostname);
+
+                       /* The other master often gets into the same state,
+                        * with boring results.
+                        */
+                       ntp = fromnet;  /* (acksend() can leave fromnet=0 */
+                       for (tries = 0; tries < 3; tries++) {
+                               to.tsp_type = TSP_RESOLVE;
+                               answer = acksend(&to, &ntp->dest_addr,
+                                                ANYADDR, TSP_MASTERACK,
+                                                ntp, 0);
+                               if (answer == NULL)
+                                       break;
+                               htp = addmach(answer->tsp_name,&from,ntp);
+                               to.tsp_type = TSP_QUIT;
+                               answer = acksend(&to, &htp->addr, htp->name,
+                                                TSP_ACK, 0, htp->noanswer);
+                               if (!answer) {
+                                       syslog(LOG_WARNING,
+                                 "conflict error: no reply from %s to QUIT",
+                                               htp->name);
+                                       (void)remmach(htp);
                                }
                                }
-                        }
-                        masterup(fromnet);
-                        break;
+                       }
+                       masterup(ntp);
+                       break;
+
                case TSP_MSITE:
                        if (!slavenet)
                                break;
                case TSP_MSITE:
                        if (!slavenet)
                                break;
-                       msaveaddr = from;
-                       msg->tsp_type = TSP_MSITEREQ;
-                       msg->tsp_vers = TSPVERSION;
-                       (void)strcpy(msg->tsp_name, hostname);
-                       answer = acksend(msg, &slavenet->dest_addr,
-                                        (char *)ANYADDR, TSP_ACK, slavenet);
-                       if (answer != NULL) {
-                               msg->tsp_type = TSP_ACK;
-                               length = sizeof(struct sockaddr_in);
-                               bytenetorder(msg);
-                               if (sendto(sock, (char *)msg,
-                                   sizeof(struct tsp), 0,
-                                   (struct sockaddr *)&msaveaddr,
-                                   length) < 0) {
-                                       syslog(LOG_ERR, "sendto: %m");
-                                       exit(1);
+                       taddr = from;
+                       to.tsp_type = TSP_MSITEREQ;
+                       to.tsp_vers = TSPVERSION;
+                       to.tsp_seq = 0;
+                       (void)strcpy(to.tsp_name, hostname);
+                       answer = acksend(&to, &slavenet->dest_addr,
+                                        ANYADDR, TSP_ACK,
+                                        slavenet, 0);
+                       if (answer != NULL
+                           && good_host_name(answer->tsp_name)) {
+                               setmaster(answer);
+                               to.tsp_type = TSP_ACK;
+                               (void)strcpy(to.tsp_name, answer->tsp_name);
+                               bytenetorder(&to);
+                               if (sendto(sock, (char *)&to,
+                                          sizeof(struct tsp), 0,
+                                          (struct sockaddr*)&taddr, sizeof(taddr)) < 0) {
+                                       trace_sendto_err(taddr.sin_addr);
                                }
                        }
                        break;
                                }
                        }
                        break;
+
+               case TSP_MSITEREQ:
+                       break;
+
                case TSP_ACCEPT:
                case TSP_REFUSE:
                case TSP_ACCEPT:
                case TSP_REFUSE:
-                       break;
                case TSP_RESOLVE:
                        break;
                case TSP_RESOLVE:
                        break;
+
                case TSP_QUIT:
                case TSP_QUIT:
-                       /* become slave */
-#ifdef MEASURE
-                       if (fp != NULL) {
-                               (void)fclose(fp);
-                               fp = NULL;
-                       }
-#endif
-                       longjmp(jmpenv, 2);
+                       doquit(msg);            /* become a slave */
                        break;
                        break;
-#ifdef TESTING
+
                case TSP_TEST:
                        electiontime = 0;
                        break;
                case TSP_TEST:
                        electiontime = 0;
                        break;
-#endif
-               case TSP_MSITEREQ:
-                       if (status & MASTER)
-                               break;
-                       if (trace) {
-                               fprintf(fd, "garbage: ");
-                               print(msg, &from);
-                       }
-                       break;
 
                case TSP_LOOP:
                        /* looking for loops of masters */
 
                case TSP_LOOP:
                        /* looking for loops of masters */
-                       if ( !(status & MASTER))
+                       if (!(status & MASTER))
                                break;
                        if (fromnet->status == SLAVE) {
                                break;
                        if (fromnet->status == SLAVE) {
-                           if ( !strcmp(msg->tsp_name, hostname)) {
-                                 for(;;) {
+                           if (!strcmp(msg->tsp_name, hostname)) {
+                               /*
+                                * Someone forwarded our message back to
+                                * us.  There must be a loop.  Tell the
+                                * master of this network to quit.
+                                *
+                                * The other master often gets into
+                                * the same state, with boring results.
+                                */
+                               ntp = fromnet;
+                               for (tries = 0; tries < 3; tries++) {
                                    to.tsp_type = TSP_RESOLVE;
                                    to.tsp_type = TSP_RESOLVE;
-                                   answer = acksend(&to, &fromnet->dest_addr,
-                                       (char *)ANYADDR, TSP_MASTERACK,
-                                       fromnet);
+                                   answer = acksend(&to, &ntp->dest_addr,
+                                                    ANYADDR, TSP_MASTERACK,
+                                                    ntp,0);
                                    if (answer == NULL)
                                    if (answer == NULL)
-                                           break;
+                                       break;
+                                   taddr = from;
+                                   (void)strcpy(tname, answer->tsp_name);
                                    to.tsp_type = TSP_QUIT;
                                    (void)strcpy(to.tsp_name, hostname);
                                    to.tsp_type = TSP_QUIT;
                                    (void)strcpy(to.tsp_name, hostname);
-                                   server = from;
-                                   answer = acksend(&to, &server,
-                                       answer->tsp_name, TSP_ACK,
-                                       (struct netinfo *)NULL);
-                                   if (answer == NULL) {
-                                       syslog(LOG_ERR, "loop kill error");
+                                   if (!acksend(&to, &taddr, tname,
+                                                TSP_ACK, 0, 1)) {
+                                       syslog(LOG_ERR,
+                                       "no reply from %s to slave LOOP-QUIT",
+                                                tname);
                                    } else {
                                        electiontime = 0;
                                    }
                                    } else {
                                        electiontime = 0;
                                    }
-                                 }
+                               }
+                               (void)gettimeofday(&ntime, 0);
+                               looptime = ntime.tv_sec + FASTTOUT;
                            } else {
                            } else {
-                               if (msg->tsp_hopcnt-- <= 0)
+                               if (msg->tsp_hopcnt-- < 1)
                                    break;
                                bytenetorder(msg);
                                    break;
                                bytenetorder(msg);
-                               ntp = nettab;
-                               for (; ntp != NULL; ntp = ntp->next)
-                                   if (ntp->status == MASTER)
-                                       if (sendto(sock, (char *)msg, 
-                                           sizeof(struct tsp), 0,
-                                           (struct sockaddr *)&ntp->dest_addr,
-                                           length) < 0) {
-                                               syslog(LOG_ERR, "sendto: %m");
-                                               exit(1);
-                                       }
+                               for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+                                   if (ntp->status == MASTER
+                                       && 0 > sendto(sock, (char *)msg,
+                                                     sizeof(struct tsp), 0,
+                                             (struct sockaddr*)&ntp->dest_addr,
+                                                     sizeof(ntp->dest_addr)))
+                                   trace_sendto_err(ntp->dest_addr.sin_addr);
+                               }
                            }
                            }
-                       } else {
+                       } else {        /* fromnet->status == MASTER */
                            /*
                             * We should not have received this from a net
                            /*
                             * We should not have received this from a net
-                            * we are master on.  There must be two masters
-                            * in this case.
+                            * we are master on.  There must be two masters,
+                            * unless the packet was really from us.
                             */
                             */
-                           if (fromnet->my_addr.s_addr == from.sin_addr.s_addr)
+                           if (from.sin_addr.s_addr
+                               == fromnet->my_addr.s_addr) {
+                               if (trace)
+                                   fprintf(fd,"discarding forwarded LOOP\n");
                                break;
                                break;
-                           for (;;) {
+                           }
+
+                           /*
+                            * The other master often gets into the same
+                            * state, with boring results.
+                            */
+                           ntp = fromnet;
+                           for (tries = 0; tries < 3; tries++) {
                                to.tsp_type = TSP_RESOLVE;
                                to.tsp_type = TSP_RESOLVE;
-                               answer = acksend(&to, &fromnet->dest_addr,
-                                   (char *)ANYADDR, TSP_MASTERACK,
-                                   fromnet);
-                               if (answer == NULL)
+                               answer = acksend(&to, &ntp->dest_addr,
+                                                ANYADDR, TSP_MASTERACK,
+                                               ntp,0);
+                               if (!answer)
                                        break;
                                        break;
+                               htp = addmach(answer->tsp_name,
+                                             &from,ntp);
                                to.tsp_type = TSP_QUIT;
                                (void)strcpy(to.tsp_name, hostname);
                                to.tsp_type = TSP_QUIT;
                                (void)strcpy(to.tsp_name, hostname);
-                               server = from;
-                               answer = acksend(&to, &server, answer->tsp_name,
-                                   TSP_ACK, (struct netinfo *)NULL);
-                               if (answer == NULL) {
-                                       syslog(LOG_ERR, "loop kill error2");
-                               } else {
-                                       (void)addmach(msg->tsp_name, &from);
+                               if (!acksend(&to,&htp->addr,htp->name,
+                                            TSP_ACK, 0, htp->noanswer)) {
+                                       syslog(LOG_ERR,
+                                   "no reply from %s to master LOOP-QUIT",
+                                              htp->name);
+                                       (void)remmach(htp);
                                }
                            }
                                }
                            }
+                           (void)gettimeofday(&ntime, 0);
+                           looptime = ntime.tv_sec + FASTTOUT;
                        }
                        break;
                default:
                        if (trace) {
                        }
                        break;
                default:
                        if (trace) {
-                               fprintf(fd, "garbage: ");
+                               fprintf(fd, "garbage message: ");
                                print(msg, &from);
                        }
                        break;
                                print(msg, &from);
                        }
                        break;
@@ -541,12 +597,86 @@ loop:
        goto loop;
 }
 
        goto loop;
 }
 
+
+/*
+ * tell the world who our master is
+ */
+static void
+setmaster(msg)
+       struct tsp *msg;
+{
+       if (slavenet
+           && (slavenet != old_slavenet
+               || strcmp(msg->tsp_name, master_name)
+               || old_status != status)) {
+               (void)strcpy(master_name, msg->tsp_name);
+               old_slavenet = slavenet;
+               old_status = status;
+
+               if (status & MASTER) {
+                       syslog(LOG_NOTICE, "submaster to %s", master_name);
+                       if (trace)
+                               fprintf(fd, "submaster to %s\n", master_name);
+
+               } else {
+                       syslog(LOG_NOTICE, "slave to %s", master_name);
+                       if (trace)
+                               fprintf(fd, "slave to %s\n", master_name);
+               }
+       }
+}
+
+
+
+/*
+ * handle date change request on a slave
+ */
+static void
+schgdate(msg, newdate)
+       struct tsp *msg;
+       char *newdate;
+{
+       struct tsp to;
+       u_short seq;
+       struct sockaddr_in taddr;
+       struct timeval otime;
+
+       if (!slavenet)
+               return;                 /* no where to forward */
+
+       taddr = from;
+       seq = msg->tsp_seq;
+
+       syslog(LOG_INFO,
+              "forwarding date change by %s to %s",
+              msg->tsp_name, newdate);
+
+       /* adjust time for residence on the queue */
+       (void)gettimeofday(&otime, 0);
+       adj_msg_time(msg, &otime);
+
+       to.tsp_type = TSP_SETDATEREQ;
+       to.tsp_time = msg->tsp_time;
+       (void)strcpy(to.tsp_name, hostname);
+       if (!acksend(&to, &slavenet->dest_addr,
+                    ANYADDR, TSP_DATEACK,
+                    slavenet, 0))
+               return;                 /* no answer */
+
+       xmit(TSP_DATEACK, seq, &taddr);
+}
+
+
 /*
  * Used before answering a broadcast message to avoid network
  * contention and likely collisions.
  */
 /*
  * Used before answering a broadcast message to avoid network
  * contention and likely collisions.
  */
+static void
 answerdelay()
 {
 answerdelay()
 {
+#ifdef sgi
+       sginap(delay1);
+#else
        struct timeval timeout;
 
        timeout.tv_sec = 0;
        struct timeval timeout;
 
        timeout.tv_sec = 0;
@@ -555,4 +685,5 @@ answerdelay()
        (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
            &timeout);
        return;
        (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
            &timeout);
        return;
+#endif /* sgi */
 }
 }
index 5c38741..0c8f1f2 100644 (file)
@@ -3,7 +3,7 @@
 .\"
 .\" %sccs.include.redist.roff%
 .\"
 .\"
 .\" %sccs.include.redist.roff%
 .\"
-.\"     @(#)timed.8    6.6 (Berkeley) %G%
+.\"     @(#)timed.8    6.7 (Berkeley) %G%
 .\"
 .Dd 
 .Dt TIMED 8
 .\"
 .Dd 
 .Dt TIMED 8
 .Nm timed
 .Op Fl M
 .Op Fl t
 .Nm timed
 .Op Fl M
 .Op Fl t
+.Op Fl d
 .Op Fl i Ar network
 .Op Fl n Ar network
 .Op Fl i Ar network
 .Op Fl n Ar network
+.Op Fl F Ar host1 host2 ...
 .Sh DESCRIPTION
 This
 .Sh DESCRIPTION
 This
-is the time server daemon and is normally invoked
+is a time server daemon and is normally invoked
 at boot time from the
 .Xr rc 8
 file.  
 at boot time from the
 .Xr rc 8
 file.  
@@ -60,7 +62,9 @@ A
 .Nm timed
 running without the
 .Fl M
 .Nm timed
 running without the
 .Fl M
-flag will remain a slave.
+or
+.Fl F
+flags will remain a slave.
 The 
 .Fl t
 flag enables
 The 
 .Fl t
 flag enables
@@ -70,6 +74,10 @@ file
 .Pa /var/log/timed.log .
 Tracing can be turned on or off by the program
 .Xr timedc 8 .
 .Pa /var/log/timed.log .
 Tracing can be turned on or off by the program
 .Xr timedc 8 .
+The
+.Fl d
+flag is for debugging the daemon.
+It causes the program to not put itself into the background.
 Normally
 .Nm timed
 checks for a master time server on each network to which
 Normally
 .Nm timed
 checks for a master time server on each network to which
@@ -107,6 +115,59 @@ The
 and 
 .Fl i
 flags are meaningless if used together.
 and 
 .Fl i
 flags are meaningless if used together.
+.Pp
+.Nm Timed
+checks for a master time server on each network to which
+it is connected, except as modified by the
+.Fl n
+and
+.Fl i
+options described above.
+If it finds masters on more than one network, it chooses one network
+on which to be a "slave," and then periodically checks the other
+networks to see if the masters there have disappeared.
+.Pp
+One way to synchronize a group of machines is to use an NTP daemon to 
+synchronize the clock of one machine to a distant standard or a radio
+receiver and 
+.Fl F Ar hostname
+to tell its timed daemon to trust only itself.
+.Pp
+Messages printed by the kernel on the system console occur with
+interrupts disabled. 
+This means that the clock stops while they are printing.
+A machine with many disk or network hardware problems and consequent
+messages cannot keep good time by itself.  Each message typically causes
+the clock to lose a dozen milliseconds.  A time daemon can
+correct the result.
+.Pp
+Messages in the system log about machines that failed to respond
+usually indicate machines that crashed or were turned off.
+Complaints about machines that failed to respond to initial time
+settings are often associated with "multi-homed" machines
+that looked for time masters on more than one network and eventually
+chose to become a slave on the other network.
+.SH WARNING
+If two or more time daemons, whether 
+.Nm timed ,
+.Xr NTP ,
+try to adjust the same clock, temporal chaos will result.
+If both 
+.Nm
+and another time daemon are run on the same machine,
+ensure that the 
+.Fl F
+flag is used, so that 
+.Nm timed
+never attempts to adjust the local clock.
+.Pp 
+The protocol is based on UDP/IP broadcasts.  All machines within
+the range of a broadcast that are using the TSP protocol must cooperate.
+There cannot be more than a single administrative domain using the
+.Fl F
+flag among all machines reached by a broadcast packet.
+Failure to follow this rule is usually indicated by complaints concerning
+"untrusted" machines in the system log.
 .Sh FILES
 .Bl -tag -width /var/log/timed.masterlog -compact
 .It Pa /var/log/timed.log
 .Sh FILES
 .Bl -tag -width /var/log/timed.masterlog -compact
 .It Pa /var/log/timed.log
index 5307f08..4987b06 100644 (file)
@@ -1,4 +1,3 @@
-/*
  * Copyright (c) 1985 Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1985 Regents of the University of California.
  * All rights reserved.
  *
@@ -12,157 +11,271 @@ char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)timed.c    2.21 (Berkeley) %G%";
+static char sccsid[] = "@(#)timed.c    5.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-#include "globals.h"
+#ifdef sgi
+#ident "$Revision: 1.25 $"
+#endif /* sgi */
+
 #define TSPTYPES
 #define TSPTYPES
-#include <protocols/timed.h>
+#include "globals.h"
 #include <net/if.h>
 #include <net/if.h>
-#include <arpa/inet.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <setjmp.h>
 #include "pathnames.h"
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <setjmp.h>
 #include "pathnames.h"
-
-int id;
-int trace;
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#ifdef sgi
+#include <unistd.h>
+#include <sys/syssgi.h>
+#include <sys/schedctl.h>
+#endif /* sgi */
+
+int trace = 0;
 int sock, sock_raw = -1;
 int status = 0;
 int sock, sock_raw = -1;
 int status = 0;
-int backoff;
-int slvcount;                          /* no. of slaves controlled by master */
-int machup;
 u_short sequence;                      /* sequence number */
 long delay1;
 long delay2;
 u_short sequence;                      /* sequence number */
 long delay1;
 long delay2;
-long random();
-char hostname[MAXHOSTNAMELEN];
-struct host hp[NHOSTS];
-char tracefile[] = _PATH_TIMEDLOG;
-FILE *fd;
+
+int nslavenets;                                /* nets were I could be a slave */
+int nmasternets;                       /* nets were I could be a master */
+int nignorednets;                      /* ignored nets */
+int nnets;                             /* nets I am connected to */
+
+FILE *fd;                              /* trace file FD */
+
 jmp_buf jmpenv;
 jmp_buf jmpenv;
-struct netinfo *nettab = NULL;
-int nslavenets;                /* Number of networks were I could be a slave */
-int nmasternets;       /* Number of networks were I could be a master */
-int nignorednets;      /* Number of ignored networks */
-int nnets;             /* Number of networks I am connected to */
+
+struct netinfo *nettab = 0;
 struct netinfo *slavenet;
 struct netinfo *slavenet;
-struct netinfo *firstslavenet();
 int Mflag;
 int justquit = 0;
 int Mflag;
 int justquit = 0;
+int debug;
 
 
-struct nets {
-       char *name;
-       long net;
+static struct nets {
+       char    *name;
+       long    net;
        struct nets *next;
        struct nets *next;
-} *nets = (struct nets *)0;
+} *nets = 0;
+
+struct hosttbl hosttbl[NHOSTS+1];      /* known hosts */
+
+static struct goodhost {               /* hosts that we trust */
+       char    name[MAXHOSTNAMELEN+1];
+       struct goodhost *next;
+       char    perm;
+} *goodhosts;
+
+static char *goodgroup;                        /* net group of trusted hosts */
+static void checkignorednets __P((void));
+static void pickslavenet __P((struct netinfo *));
+static void add_good_host __P((char *, int));
+
+#ifdef sgi
+char *timetrim_fn;
+char *timetrim_wpat = "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
+char *timetrim_rpat = "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
+long timetrim;
+double tot_adj, hr_adj;                        /* totals in nsec */
+double tot_ticks, hr_ticks;
+
+int bufspace = 60*1024;
+#endif
+
 
 /*
  * The timedaemons synchronize the clocks of hosts in a local area network.
  * One daemon runs as master, all the others as slaves. The master
  * performs the task of computing clock differences and sends correction
 
 /*
  * The timedaemons synchronize the clocks of hosts in a local area network.
  * One daemon runs as master, all the others as slaves. The master
  * performs the task of computing clock differences and sends correction
- * values to the slaves. 
- * Slaves start an election to choose a new master when the latter disappears 
+ * values to the slaves.
+ * Slaves start an election to choose a new master when the latter disappears
  * because of a machine crash, network partition, or when killed.
  * A resolution protocol is used to kill all but one of the masters
  * because of a machine crash, network partition, or when killed.
  * A resolution protocol is used to kill all but one of the masters
- * that happen to exist in segments of a partitioned network when the 
+ * that happen to exist in segments of a partitioned network when the
  * network partition is fixed.
  *
  * Authors: Riccardo Gusella & Stefano Zatti
  * network partition is fixed.
  *
  * Authors: Riccardo Gusella & Stefano Zatti
+ *
+ * overhauled at Silicon Graphics
  */
  */
-
+int
 main(argc, argv)
 main(argc, argv)
-int argc;
-char **argv;
+       int argc;
+       char *argv[];
 {
        int on;
        int ret;
 {
        int on;
        int ret;
-       long seed;
        int nflag, iflag;
        int nflag, iflag;
-       struct timeval time;
+       struct timeval ntime;
        struct servent *srvp;
        struct servent *srvp;
-       long casual();
-       char *date();
-       int n;
-       int flag;
        char buf[BUFSIZ], *cp, *cplim;
        struct ifconf ifc;
        char buf[BUFSIZ], *cp, *cplim;
        struct ifconf ifc;
-       struct ifreq ifreq, *ifr;
+       struct ifreq ifreq, ifreqf, *ifr;
        register struct netinfo *ntp;
        struct netinfo *ntip;
        struct netinfo *savefromnet;
        register struct netinfo *ntp;
        struct netinfo *ntip;
        struct netinfo *savefromnet;
+       struct netent *nentp;
+       struct nets *nt;
        struct sockaddr_in server;
        u_short port;
        struct sockaddr_in server;
        u_short port;
-       uid_t getuid();
+       char c;
+       extern char *optarg;
+       extern int optind, opterr;
+#ifdef sgi
+       FILE *timetrim_st;
+#endif
+
+#define        IN_MSG "timed: -i and -n make no sense together\n"
+#ifdef sgi
+       struct tms tms;
+#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp] [-P trimfile]\n"
+#else
+#ifdef HAVENIS
+#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
+#else
+#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
+#endif /* HAVENIS */
+#endif /* sgi */
 
 #ifdef lint
        ntip = NULL;
 #endif
 
 
 #ifdef lint
        ntip = NULL;
 #endif
 
-       Mflag = 0;
        on = 1;
        on = 1;
-       backoff = 1;
-       trace = OFF;
        nflag = OFF;
        iflag = OFF;
        nflag = OFF;
        iflag = OFF;
-       openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
 
 
-       if (getuid() != 0) {
-               fprintf(stderr, "Timed: not superuser\n");
-               exit(1);
+#ifdef sgi
+       if (0 > syssgi(SGI_GETTIMETRIM, &timetrim)) {
+               perror("timed: syssgi(GETTIMETRIM)");
+               timetrim = 0;
        }
        }
+       tot_ticks = hr_ticks = times(&tms);
+#endif /* sgi */
+
+       opterr = 0;
+       while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != EOF) {
+               switch (c) {
+               case 'M':
+                       Mflag = 1;
+                       break;
 
 
-       while (--argc > 0 && **++argv == '-') {
-               (*argv)++;
-               do {
-                       switch (**argv) {
+               case 't':
+                       trace = 1;
+                       break;
 
 
-                       case 'M':
-                               Mflag = 1; 
-                               break;
-                       case 't':
-                               trace = ON; 
-                               break;
-                       case 'n':
-                               argc--, argv++;
-                               if (iflag) {
-                                       fprintf(stderr,
-                                   "timed: -i and -n make no sense together\n");
-                               } else {
-                                       nflag = ON;
-                                       addnetname(*argv);
+               case 'n':
+                       if (iflag) {
+                               fprintf(stderr, IN_MSG);
+                               exit(1);
+                       } else {
+                               nflag = ON;
+                               addnetname(optarg);
+                       }
+                       break;
+
+               case 'i':
+                       if (nflag) {
+                               fprintf(stderr, IN_MSG);
+                               exit(1);
+                       } else {
+                               iflag = ON;
+                               addnetname(optarg);
+                       }
+                       break;
+
+               case 'F':
+                       add_good_host(optarg,1);
+                       while (optind < argc && argv[optind][0] != '-')
+                               add_good_host(argv[optind++], 1);
+                       break;
+
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'G':
+                       if (goodgroup != 0) {
+                               fprintf(stderr,"timed: only one net group\n");
+                               exit(1);
+                       }
+                       goodgroup = optarg;
+                       break;
+#ifdef sgi
+               case 'P':
+                       timetrim_fn = optarg;
+                       timetrim_st = fopen(timetrim_fn, "r+");
+                       if (0 == timetrim_st) {
+                               if (errno != ENOENT) {
+                                       (void)fprintf(stderr,"timed: ");
+                                       perror(timetrim_fn);
+                                       timetrim_fn = 0;
                                }
                                }
-                               while (*(++(*argv)+1)) ;
-                               break;
-                       case 'i':
-                               argc--, argv++;
-                               if (nflag) {
-                                       fprintf(stderr,
-                                   "timed: -i and -n make no sense together\n");
+                       } else {
+                               int i;
+                               long trim;
+                               double adj, ticks;
+
+                               i = fscanf(timetrim_st, timetrim_rpat,
+                                          &trim, &adj, &ticks);
+                               if (i < 1
+                                   || trim > MAX_TRIM
+                                   || trim < -MAX_TRIM
+                                   || i == 2
+                                   || (i == 3
+                                       && trim != rint(adj*CLK_TCK/ticks))) {
+                                       if (trace && i != EOF)
+                                               (void)fprintf(stderr,
+                                   "timed: unrecognized contents in %s\n",
+                                                             timetrim_fn);
                                } else {
                                } else {
-                                       iflag = ON;
-                                       addnetname(*argv);
+                                       if (0 > syssgi(SGI_SETTIMETRIM,
+                                                      trim)) {
+                                        perror("timed: syssgi(SETTIMETRIM)");
+                                       } else {
+                                               timetrim = trim;
+                                       }
+                                       if (i == 3) {
+                                               tot_adj = adj;
+                                               tot_ticks -= ticks;
+                                       }
                                }
                                }
-                               while (*(++(*argv)+1)) ;
-                               break;
-                       default:
-                               fprintf(stderr, "timed: -%c: unknown option\n", 
-                                                       **argv);
-                               break;
+                               (void)fclose(timetrim_st);
                        }
                        }
-               } while (*++(*argv));
+                       break;
+#endif /* sgi */
+
+               default:
+                       fprintf(stderr, USAGE);
+                       exit(1);
+                       break;
+               }
+       }
+       if (optind < argc) {
+               fprintf(stderr, USAGE);
+               exit(1);
        }
 
        }
 
-       if (trace == ON) {
-               fd = fopen(tracefile, "w");
-               setlinebuf(fd);
-               fprintf(fd, "Tracing started on: %s\n\n", 
-                                       date());
+       if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
+               perror("gethostname");
+               exit(1);
        }
        }
+       self.l_bak = &self;
+       self.l_fwd = &self;
+       self.h_bak = &self;
+       self.h_fwd = &self;
+       self.head = 1;
+       self.good = 1;
+
+       if (goodhosts != 0)             /* trust ourself */
+               add_good_host(hostname,1);
 
        srvp = getservbyname("timed", "udp");
        if (srvp == 0) {
 
        srvp = getservbyname("timed", "udp");
        if (srvp == 0) {
-               syslog(LOG_CRIT, "unknown service 'timed/udp'");
+               fprintf(stderr, "unknown service 'timed/udp'\n");
                exit(1);
        }
        port = srvp->s_port;
                exit(1);
        }
        port = srvp->s_port;
@@ -170,130 +283,147 @@ char **argv;
        server.sin_family = AF_INET;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
        server.sin_family = AF_INET;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
-               syslog(LOG_ERR, "socket: %m");
+               perror("socket");
                exit(1);
        }
                exit(1);
        }
-       if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, 
+       if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
                                                        sizeof(on)) < 0) {
                                                        sizeof(on)) < 0) {
-               syslog(LOG_ERR, "setsockopt: %m");
+               perror("setsockopt");
                exit(1);
        }
                exit(1);
        }
-       if (bind(sock, (struct sockaddr *)&server, sizeof(server))) {
+       if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
                if (errno == EADDRINUSE)
                if (errno == EADDRINUSE)
-                       syslog(LOG_ERR, "server already running");
+                       fprintf(stderr,"timed: time daemon already running\n");
                else
                else
-                       syslog(LOG_ERR, "bind: %m");
+                       perror("bind");
                exit(1);
        }
                exit(1);
        }
+#ifdef sgi
+       /*
+        * handle many slaves with our buffer
+        */
+       if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace,
+                        sizeof(bufspace))) {
+               perror("setsockopt");
+               exit(1);
+       }
+#endif /* sgi */
 
        /* choose a unique seed for random number generation */
 
        /* choose a unique seed for random number generation */
-       (void)gettimeofday(&time, (struct timezone *)0);
-       seed = time.tv_sec + time.tv_usec;
-       srandom(seed);
+       (void)gettimeofday(&ntime, 0);
+       srandom(ntime.tv_sec + ntime.tv_usec);
 
        sequence = random();     /* initial seq number */
 
 
        sequence = random();     /* initial seq number */
 
+#ifndef sgi
        /* rounds kernel variable time to multiple of 5 ms. */
        /* rounds kernel variable time to multiple of 5 ms. */
-       time.tv_sec = 0;
-       time.tv_usec = -((time.tv_usec/1000) % 5) * 1000;
-       (void)adjtime(&time, (struct timeval *)0);
-
-       id = getpid();
-
-       if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
-               syslog(LOG_ERR, "gethostname: %m");
-               exit(1);
-       }
-       hp[0].name = hostname;
-
-       if (nflag || iflag) {
-               struct netent *getnetent();
-               struct netent *n;
-               struct nets *np;
-               for ( np = nets ; np ; np = np->next) {
-                       n = getnetbyname(np->name);
-                       if (n == NULL) {
-                               syslog(LOG_ERR, "getnetbyname: unknown net %s",
-                                       np->name);
-                               exit(1);
-                       }
-                       np->net = n->n_net;
+       ntime.tv_sec = 0;
+       ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
+       (void)adjtime(&ntime, (struct timeval *)0);
+#endif /* sgi */
+
+       for (nt = nets; nt; nt = nt->next) {
+               nentp = getnetbyname(nt->name);
+               if (nentp == 0) {
+                       nt->net = inet_network(nt->name);
+                       if (nt->net != INADDR_NONE)
+                               nentp = getnetbyaddr(nt->net, AF_INET);
+               }
+               if (nentp != 0) {
+                       nt->net = nentp->n_net;
+               } else if (nt->net == INADDR_NONE) {
+                       fprintf(stderr, "timed: unknown net %s\n", nt->name);
+                       exit(1);
+               } else if (nt->net == INADDR_ANY) {
+                       fprintf(stderr, "timed: bad net %s\n", nt->name);
+                       exit(1);
+               } else {
+                       fprintf(stderr,
+                               "timed: warning: %s unknown in /etc/networks\n",
+                               nt->name);
                }
                }
+
+               if (0 == (nt->net & 0xff000000))
+                   nt->net <<= 8;
+               if (0 == (nt->net & 0xff000000))
+                   nt->net <<= 8;
+               if (0 == (nt->net & 0xff000000))
+                   nt->net <<= 8;
        }
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = buf;
        if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
        }
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = buf;
        if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
-               syslog(LOG_ERR, "get interface configuration: %m");
+               perror("timed: get interface configuration");
                exit(1);
        }
        ntp = NULL;
                exit(1);
        }
        ntp = NULL;
-#define max(a, b) (a > b ? a : b)
+#ifdef sgi
+#define size(p)        (sizeof(*ifr) - sizeof(ifr->ifr_name))  /* XXX hack. kludge */
+#else
 #define size(p)        max((p).sa_len, sizeof(p))
 #define size(p)        max((p).sa_len, sizeof(p))
+#endif
        cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
        for (cp = buf; cp < cplim;
                        cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
                ifr = (struct ifreq *)cp;
                if (ifr->ifr_addr.sa_family != AF_INET)
                        continue;
        cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
        for (cp = buf; cp < cplim;
                        cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
                ifr = (struct ifreq *)cp;
                if (ifr->ifr_addr.sa_family != AF_INET)
                        continue;
+               if (!ntp)
+                       ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
+               bzero(ntp,sizeof(*ntp));
+               ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+               ntp->status = NOMASTER;
                ifreq = *ifr;
                ifreq = *ifr;
-               if (ntp == NULL)
-                       ntp = (struct netinfo *)malloc(sizeof(struct netinfo));
-               ntp->my_addr = 
-                       ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
-               if (ioctl(sock, SIOCGIFFLAGS, 
-                                       (char *)&ifreq) < 0) {
-                       syslog(LOG_ERR, "get interface flags: %m");
+               ifreqf = *ifr;
+
+               if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
+                       perror("get interface flags");
                        continue;
                }
                        continue;
                }
-               if ((ifreq.ifr_flags & IFF_UP) == 0 ||
-                       ((ifreq.ifr_flags & IFF_BROADCAST) == 0 &&
-                       (ifreq.ifr_flags & IFF_POINTOPOINT) == 0)) {
+               if ((ifreqf.ifr_flags & IFF_UP) == 0)
+                       continue;
+               if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
+                   (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
                        continue;
                }
                        continue;
                }
-               if (ifreq.ifr_flags & IFF_BROADCAST)
-                       flag = 1;
-               else
-                       flag = 0;
-               if (ioctl(sock, SIOCGIFNETMASK, 
-                                       (char *)&ifreq) < 0) {
-                       syslog(LOG_ERR, "get netmask: %m");
+
+
+               if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+                       perror("get netmask");
                        continue;
                }
                ntp->mask = ((struct sockaddr_in *)
                        &ifreq.ifr_addr)->sin_addr.s_addr;
                        continue;
                }
                ntp->mask = ((struct sockaddr_in *)
                        &ifreq.ifr_addr)->sin_addr.s_addr;
-               if (flag) {
-                       if (ioctl(sock, SIOCGIFBRDADDR, 
-                                               (char *)&ifreq) < 0) {
-                               syslog(LOG_ERR, "get broadaddr: %m");
+
+               if (ifreqf.ifr_flags & IFF_BROADCAST) {
+                       if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+                               perror("get broadaddr");
                                continue;
                        }
                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
                                continue;
                        }
                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+                       /* What if the broadcast address is all ones?
+                        * So we cannot just mask ntp->dest_addr.  */
+                       ntp->net = ntp->my_addr;
+                       ntp->net.s_addr &= ntp->mask;
                } else {
                } else {
-                       if (ioctl(sock, SIOCGIFDSTADDR, 
+                       if (ioctl(sock, SIOCGIFDSTADDR,
                                                (char *)&ifreq) < 0) {
                                                (char *)&ifreq) < 0) {
-                               syslog(LOG_ERR, "get destaddr: %m");
+                               perror("get destaddr");
                                continue;
                        }
                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
                                continue;
                        }
                        ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
+                       ntp->net = ntp->dest_addr.sin_addr;
                }
                }
+
                ntp->dest_addr.sin_port = port;
                ntp->dest_addr.sin_port = port;
-               if (nflag || iflag) {
-                       u_long addr, mask;
-                       struct nets *n;
-
-                       addr = ntohl(ntp->dest_addr.sin_addr.s_addr);
-                       mask = ntohl(ntp->mask);
-                       while ((mask & 1) == 0) {
-                               addr >>= 1;
-                               mask >>= 1;
-                       }
-                       for (n = nets ; n ; n = n->next)
-                               if (addr == n->net)
-                                       break;
-                       if (nflag && !n || iflag && n)
-                               continue;
+
+               for (nt = nets; nt; nt = nt->next) {
+                       if (ntp->net.s_addr == nt->net)
+                               break;
                }
                }
-               ntp->net = ntp->mask & ntp->dest_addr.sin_addr.s_addr;
+               if (nflag && !nt || iflag && nt)
+                       continue;
+
                ntp->next = NULL;
                if (nettab == NULL) {
                        nettab = ntp;
                ntp->next = NULL;
                if (nettab == NULL) {
                        nettab = ntp;
@@ -306,21 +436,24 @@ char **argv;
        if (ntp)
                (void) free((char *)ntp);
        if (nettab == NULL) {
        if (ntp)
                (void) free((char *)ntp);
        if (nettab == NULL) {
-               syslog(LOG_ERR, "No network usable");
+               fprintf(stderr, "timed: no network usable\n");
                exit(1);
        }
 
                exit(1);
        }
 
-       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
-               lookformaster(ntp);
-       setstatus();
-       /*
-        * Take care of some basic initialization.
-        */
-       /* us. delay to be used in response to broadcast */
-       delay1 = casual((long)10000, 200000);   
+
+#ifdef sgi
+       (void)schedctl(RENICE,0,10);       /* run fast to get good time */
+
+       /* ticks to delay before responding to a broadcast */
+       delay1 = casual(0, CLK_TCK/10);
+#else
+
+       /* microseconds to delay before responding to a broadcast */
+       delay1 = casual(1, 100*1000);
+#endif /* sgi */
 
        /* election timer delay in secs. */
 
        /* election timer delay in secs. */
-       delay2 = casual((long)MINTOUT, (long)MAXTOUT);
+       delay2 = casual(MINTOUT, MAXTOUT);
 #ifndef DEBUG
        if (fork())
                exit(0);
 #ifndef DEBUG
        if (fork())
                exit(0);
@@ -338,187 +471,225 @@ char **argv;
 #endif
 
 
 #endif
 
 
-       if (Mflag) {
-               /*
-                * number (increased by 1) of slaves controlled by master: 
-                * used in master.c, candidate.c, networkdelta.c, and 
-                * correct.c 
-                */
-               slvcount = 1;
-               ret = setjmp(jmpenv);
 
 
+#ifdef sgi
+       (void)_daemonize(debug ? _DF_NOFORK|_DF_NOCHDIR : 0, sock, -1, -1);
+#else
+       if (!debug)
+               daemon(debug, 0);
+#endif /* sgi */
+
+       if (trace)
+               traceon();
+       openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+       /*
+        * keep returning here
+        */
+       ret = setjmp(jmpenv);
+       savefromnet = fromnet;
+       setstatus();
+
+       if (Mflag) {
                switch (ret) {
 
                switch (ret) {
 
-               case 0: 
-                       makeslave(firstslavenet());
-                       setstatus();
+               case 0:
+                       checkignorednets();
+                       pickslavenet(0);
                        break;
                        break;
-               case 1: 
+               case 1:
                        /* Just lost our master */
                        /* Just lost our master */
-                       setstatus();
-                       slavenet->status = election(slavenet);
-                       checkignorednets();
-                       setstatus();
-                       if (slavenet->status == MASTER)
-                               makeslave(firstslavenet());
-                       else
-                               makeslave(slavenet);
-                       setstatus();
+                       if (slavenet != 0)
+                               slavenet->status = election(slavenet);
+                       if (!slavenet || slavenet->status == MASTER) {
+                               checkignorednets();
+                               pickslavenet(0);
+                       } else {
+                               makeslave(slavenet);    /* prune extras */
+                       }
                        break;
                        break;
+
                case 2:
                        /* Just been told to quit */
                case 2:
                        /* Just been told to quit */
-                       fromnet->status = SLAVE;
-                       setstatus();
-                       savefromnet = fromnet;
-                       rmnetmachs(fromnet);
-                       checkignorednets();
-                       if (slavenet)
-                               makeslave(slavenet);
-                       else
-                               makeslave(savefromnet);
-                       setstatus();
                        justquit = 1;
                        justquit = 1;
-                       break;
-                       
-               default:
-                       /* this should not happen */
-                       syslog(LOG_ERR, "Attempt to enter invalid state");
+                       pickslavenet(savefromnet);
                        break;
                }
                        break;
                }
-                       
-               if (status & MASTER) {
-                       /* open raw socket used to measure time differences */
-                       if (sock_raw == -1) {
-                           sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
-                           if (sock_raw < 0)  {
-                                   syslog(LOG_ERR, "opening raw socket: %m");
-                                   exit (1);
-                           }
-                       }
-               } else {
+
+               setstatus();
+               if (!(status & MASTER) && sock_raw != -1) {
                        /* sock_raw is not being used now */
                        /* sock_raw is not being used now */
-                       if (sock_raw != -1) {
-                           (void)close(sock_raw);
-                           sock_raw = -1;
-                       }
+                       (void)close(sock_raw);
+                       sock_raw = -1;
                }
 
                }
 
-               if (status == MASTER) 
+               if (status == MASTER)
                        master();
                        master();
-               else 
+               else
                        slave();
                        slave();
+
        } else {
        } else {
-               /* if Mflag is not set timedaemon is forced to act as a slave */
-               status = SLAVE;
-               if (setjmp(jmpenv)) {
-                       setstatus();
-                       checkignorednets();
+               if (sock_raw != -1) {
+                       (void)close(sock_raw);
+                       sock_raw = -1;
                }
                }
-               makeslave(firstslavenet());
-               makeslave(firstslavenet());
-               for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+
+               if (ret) {
+                       /* we just lost our master or were told to quit */
+                       justquit = 1;
+               }
+               for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                        if (ntp->status == MASTER)
                        if (ntp->status == MASTER)
-                               ntp->status = IGNORE;
+                               rmnetmachs(ntp);
+                               ntp->status = NOMASTER;
+               }
+               checkignorednets();
+               pickslavenet(0);
                setstatus();
                setstatus();
+
                slave();
        }
                slave();
        }
+       /* NOTREACHED */
+#ifdef lint
+       return(0);
+#endif
 }
 
 /*
 }
 
 /*
- * Try to become master over ignored nets..
+ * suppress an upstart, untrustworthy, self-appointed master
  */
  */
-checkignorednets()
+void
+suppress(addr, name,net)
+       struct sockaddr_in *addr;
+       char *name;
+       struct netinfo *net;
 {
 {
-       register struct netinfo *ntp;
-       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
-               if (ntp->status == IGNORE)
-                       lookformaster(ntp);
+       struct sockaddr_in tgt;
+       char tname[MAXHOSTNAMELEN];
+       struct tsp msg;
+       static struct timeval wait;
+
+       if (trace)
+               fprintf(fd, "suppress: %s\n", name);
+       tgt = *addr;
+       (void)strcpy(tname, name);
+
+       while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
+               if (trace)
+                       fprintf(fd, "suppress:\tdiscarded packet from %s\n",
+                                   name);
+       }
+
+       syslog(LOG_NOTICE, "suppressing false master %s", tname);
+       msg.tsp_type = TSP_QUIT;
+       (void)strcpy(msg.tsp_name, hostname);
+       (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
 }
 
 }
 
+void
 lookformaster(ntp)
 lookformaster(ntp)
-       register struct netinfo *ntp;
+       struct netinfo *ntp;
 {
 {
-       struct tsp resp, conflict, *answer, *readmsg(), *acksend();
-       struct timeval time;
+       struct tsp resp, conflict, *answer;
+       struct timeval ntime;
        char mastername[MAXHOSTNAMELEN];
        struct sockaddr_in masteraddr;
 
        char mastername[MAXHOSTNAMELEN];
        struct sockaddr_in masteraddr;
 
+       get_goodgroup(0);
        ntp->status = SLAVE;
        ntp->status = SLAVE;
+
        /* look for master */
        resp.tsp_type = TSP_MASTERREQ;
        (void)strcpy(resp.tsp_name, hostname);
        /* look for master */
        resp.tsp_type = TSP_MASTERREQ;
        (void)strcpy(resp.tsp_name, hostname);
-       answer = acksend(&resp, &ntp->dest_addr, (char *)ANYADDR, 
-           TSP_MASTERACK, ntp);
-       if (answer == NULL) {
+       answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
+                        TSP_MASTERACK, ntp, 0);
+       if (answer != 0 && !good_host_name(answer->tsp_name)) {
+               suppress(&from, answer->tsp_name, ntp);
+               ntp->status = NOMASTER;
+               answer = 0;
+       }
+       if (answer == 0) {
                /*
                /*
-                * Various conditions can cause conflict: race between
+                * Various conditions can cause conflict: races between
                 * two just started timedaemons when no master is
                 * two just started timedaemons when no master is
-                * present, or timedaemon started during an election.
-                * Conservative approach is taken: give up and became a
-                * slave postponing election of a master until first
+                * present, or timedaemons started during an election.
+                * A conservative approach is taken.  Give up and became a
+                * slave, postponing election of a master until first
                 * timer expires.
                 */
                 * timer expires.
                 */
-               time.tv_sec = time.tv_usec = 0;
-               answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR,
-                   &time, ntp);
-               if (answer != NULL) {
-                       ntp->status = SLAVE;
+               ntime.tv_sec = ntime.tv_usec = 0;
+               answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
+               if (answer != 0) {
+                       if (!good_host_name(answer->tsp_name)) {
+                               suppress(&from, answer->tsp_name, ntp);
+                               ntp->status = NOMASTER;
+                       }
                        return;
                }
 
                        return;
                }
 
-               time.tv_sec = time.tv_usec = 0;
-               answer = readmsg(TSP_MASTERUP, (char *)ANYADDR,
-                   &time, ntp);
-               if (answer != NULL) {
-                       ntp->status = SLAVE;
+               ntime.tv_sec = ntime.tv_usec = 0;
+               answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
+               if (answer != 0) {
+                       if (!good_host_name(answer->tsp_name)) {
+                               suppress(&from, answer->tsp_name, ntp);
+                               ntp->status = NOMASTER;
+                       }
                        return;
                }
 
                        return;
                }
 
-               time.tv_sec = time.tv_usec = 0;
-               answer = readmsg(TSP_ELECTION, (char *)ANYADDR,
-                   &time, ntp);
-               if (answer != NULL) {
-                       ntp->status = SLAVE;
+               ntime.tv_sec = ntime.tv_usec = 0;
+               answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
+               if (answer != 0) {
+                       if (!good_host_name(answer->tsp_name)) {
+                               suppress(&from, answer->tsp_name, ntp);
+                               ntp->status = NOMASTER;
+                       }
                        return;
                }
                        return;
                }
-               ntp->status = MASTER;
-       } else {
-               (void)strcpy(mastername, answer->tsp_name);
-               masteraddr = from;
 
 
-               /*
-                * If network has been partitioned, there might be other
-                * masters; tell the one we have just acknowledged that 
-                * it has to gain control over the others. 
-                */
-               time.tv_sec = 0;
-               time.tv_usec = 300000;
-               answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time,
-                   ntp);
-               /*
-                * checking also not to send CONFLICT to ack'ed master
-                * due to duplicated MASTERACKs
-                */
-               if (answer != NULL && 
-                   strcmp(answer->tsp_name, mastername) != 0) {
-                       conflict.tsp_type = TSP_CONFLICT;
-                       (void)strcpy(conflict.tsp_name, hostname);
-                       if (acksend(&conflict, &masteraddr, mastername,
-                           TSP_ACK, (struct netinfo *)NULL) == NULL) {
-                               syslog(LOG_ERR, 
-                                   "error on sending TSP_CONFLICT");
-                               exit(1);
-                       }
+               if (Mflag)
+                       ntp->status = MASTER;
+               else
+                       ntp->status = NOMASTER;
+               return;
+       }
+
+       ntp->status = SLAVE;
+       (void)strcpy(mastername, answer->tsp_name);
+       masteraddr = from;
+
+       /*
+        * If network has been partitioned, there might be other
+        * masters; tell the one we have just acknowledged that
+        * it has to gain control over the others.
+        */
+       ntime.tv_sec = 0;
+       ntime.tv_usec = 300000;
+       answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
+       /*
+        * checking also not to send CONFLICT to ack'ed master
+        * due to duplicated MASTERACKs
+        */
+       if (answer != NULL &&
+           strcmp(answer->tsp_name, mastername) != 0) {
+               conflict.tsp_type = TSP_CONFLICT;
+               (void)strcpy(conflict.tsp_name, hostname);
+               if (!acksend(&conflict, &masteraddr, mastername,
+                            TSP_ACK, 0, 0)) {
+                       syslog(LOG_ERR,
+                              "error on sending TSP_CONFLICT");
                }
        }
 }
                }
        }
 }
+
 /*
  * based on the current network configuration, set the status, and count
  * networks;
  */
 /*
  * based on the current network configuration, set the status, and count
  * networks;
  */
+void
 setstatus()
 {
 setstatus()
 {
-       register struct netinfo *ntp;
+       struct netinfo *ntp;
 
        status = 0;
        nmasternets = nslavenets = nnets = nignorednets = 0;
 
        status = 0;
        nmasternets = nslavenets = nnets = nignorednets = 0;
@@ -526,31 +697,35 @@ setstatus()
                fprintf(fd, "Net status:\n");
        for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                switch ((int)ntp->status) {
                fprintf(fd, "Net status:\n");
        for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                switch ((int)ntp->status) {
-                 case MASTER:
+               case MASTER:
                        nmasternets++;
                        break;
                        nmasternets++;
                        break;
-                 case SLAVE:
+               case SLAVE:
                        nslavenets++;
                        break;
                        nslavenets++;
                        break;
-                 case IGNORE:
+               case NOMASTER:
+               case IGNORE:
                        nignorednets++;
                        break;
                }
                if (trace) {
                        nignorednets++;
                        break;
                }
                if (trace) {
-                       fprintf(fd, "\t%-16s",
-                           inet_ntoa(inet_makeaddr(ntp->net, 0)));
+                       fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
                        switch ((int)ntp->status) {
                        switch ((int)ntp->status) {
-                         case MASTER:
+                       case NOMASTER:
+                               fprintf(fd, "NOMASTER\n");
+                               break;
+                       case MASTER:
                                fprintf(fd, "MASTER\n");
                                break;
                                fprintf(fd, "MASTER\n");
                                break;
-                         case SLAVE:
+                       case SLAVE:
                                fprintf(fd, "SLAVE\n");
                                break;
                                fprintf(fd, "SLAVE\n");
                                break;
-                         case IGNORE:
+                       case IGNORE:
                                fprintf(fd, "IGNORE\n");
                                break;
                                fprintf(fd, "IGNORE\n");
                                break;
-                         default:
-                               fprintf(fd, "invalid state %d\n",(int)ntp->status);
+                       default:
+                               fprintf(fd, "invalid state %d\n",
+                                       (int)ntp->status);
                                break;
                        }
                }
                                break;
                        }
                }
@@ -560,57 +735,98 @@ setstatus()
        status &= ~IGNORE;
        if (trace)
                fprintf(fd,
        status &= ~IGNORE;
        if (trace)
                fprintf(fd,
-                     "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
-                     nnets, nmasternets, nslavenets, nignorednets);
+                       "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
+                       nnets, nmasternets, nslavenets, nignorednets, delay2);
 }
 
 }
 
+void
 makeslave(net)
        struct netinfo *net;
 {
        register struct netinfo *ntp;
 
 makeslave(net)
        struct netinfo *net;
 {
        register struct netinfo *ntp;
 
-       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
                if (ntp->status == SLAVE && ntp != net)
                        ntp->status = IGNORE;
                if (ntp->status == SLAVE && ntp != net)
                        ntp->status = IGNORE;
+       }
        slavenet = net;
 }
        slavenet = net;
 }
-       
-struct netinfo *
-firstslavenet()
+
+/*
+ * Try to become master over ignored nets..
+ */
+static void
+checkignorednets()
 {
        register struct netinfo *ntp;
 
 {
        register struct netinfo *ntp;
 
-       for (ntp = nettab; ntp != NULL; ntp = ntp->next)
-               if (ntp->status == SLAVE)
-                       return (ntp);
-       return ((struct netinfo *)0);
+       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+               if (!Mflag && ntp->status == SLAVE)
+                       break;
+
+               if (ntp->status == IGNORE || ntp->status == NOMASTER) {
+                       lookformaster(ntp);
+                       if (!Mflag && ntp->status == SLAVE)
+                               break;
+               }
+       }
 }
 
 /*
 }
 
 /*
- * `casual' returns a random number in the range [inf, sup]
+ * choose a good network on which to be a slave
+ *     The ignored networks must have already been checked.
+ *     Take a hint about for a good network.
  */
  */
+static void
+pickslavenet(ntp)
+       struct netinfo *ntp;
+{
+       if (slavenet != 0 && slavenet->status == SLAVE) {
+               makeslave(slavenet);            /* prune extras */
+               return;
+       }
+
+       if (ntp == 0 || ntp->status != SLAVE) {
+               for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+                       if (ntp->status == SLAVE)
+                               break;
+               }
+       }
+       makeslave(ntp);
+}
 
 
+/*
+ * returns a random number in the range [inf, sup]
+ */
 long
 casual(inf, sup)
 long
 casual(inf, sup)
-long inf;
-long sup;
+       long inf, sup;
 {
 {
-       float value;
+       double value;
 
 
-       value = (float)(random() & 0x7fffffff) / 0x7fffffff;
-       return(inf + (sup - inf) * value);
+       value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
+       return(inf + (sup - inf)*value);
 }
 
 char *
 date()
 {
 }
 
 char *
 date()
 {
-       char    *ctime();
+#ifdef sgi
+       struct  timeval tv;
+       static char tm[32];
+
+       (void)gettimeofday(&tv, (struct timezone *)0);
+       (void)cftime(tm, "%D %T", &tv.tv_sec);
+       return (tm);
+#else
        struct  timeval tv;
 
        (void)gettimeofday(&tv, (struct timezone *)0);
        return (ctime(&tv.tv_sec));
        struct  timeval tv;
 
        (void)gettimeofday(&tv, (struct timezone *)0);
        return (ctime(&tv.tv_sec));
+#endif /* sgi */
 }
 
 }
 
+void
 addnetname(name)
        char *name;
 {
 addnetname(name)
        char *name;
 {
@@ -619,10 +835,129 @@ addnetname(name)
        while (*netlist)
                netlist = &((*netlist)->next);
        *netlist = (struct nets *)malloc(sizeof **netlist);
        while (*netlist)
                netlist = &((*netlist)->next);
        *netlist = (struct nets *)malloc(sizeof **netlist);
-       if (*netlist == (struct nets *)0) {
-               syslog(LOG_ERR, "malloc failed");
+       if (*netlist == 0) {
+               fprintf(stderr,"malloc failed\n");
                exit(1);
        }
        bzero((char *)*netlist, sizeof(**netlist));
        (*netlist)->name = name;
 }
                exit(1);
        }
        bzero((char *)*netlist, sizeof(**netlist));
        (*netlist)->name = name;
 }
+
+/* note a host as trustworthy */
+static void
+add_good_host(name, perm)
+       char *name;
+       int perm;                       /* 1=not part of the netgroup */
+{
+       register struct goodhost *ghp;
+       register struct hostent *hentp;
+
+       ghp = (struct goodhost*)malloc(sizeof(*ghp));
+       if (!ghp) {
+               syslog(LOG_ERR, "malloc failed");
+               exit(1);
+       }
+
+       bzero((char*)ghp, sizeof(*ghp));
+       (void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
+       ghp->next = goodhosts;
+       ghp->perm = perm;
+       goodhosts = ghp;
+
+       hentp = gethostbyname(name);
+       if (0 == hentp && perm)
+               (void)fprintf(stderr, "unknown host %s\n", name);
+}
+
+
+/* update our image of the net-group of trustworthy hosts
+ */
+void
+get_goodgroup(force)
+       int force;
+{
+# define NG_DELAY (30*60*CLK_TCK)      /* 30 minutes */
+       static unsigned long last_update = -NG_DELAY;
+       unsigned long new_update;
+       struct hosttbl *htp;
+       struct goodhost *ghp, **ghpp;
+       char *mach, *usr, *dom;
+       struct tms tm;
+
+
+       /* if no netgroup, then we are finished */
+       if (goodgroup == 0 || !Mflag)
+               return;
+
+       /* Do not chatter with the netgroup master too often.
+        */
+       new_update = times(&tm);
+       if (new_update < last_update + NG_DELAY
+           && !force)
+               return;
+       last_update = new_update;
+
+       /* forget the old temporary entries */
+       ghpp = &goodhosts;
+       while (0 != (ghp = *ghpp)) {
+               if (!ghp->perm) {
+                       *ghpp = ghp->next;
+                       free((char*)ghp);
+               } else {
+                       ghpp = &ghp->next;
+               }
+       }
+
+#ifdef HAVENIS
+       /* quit now if we are not one of the trusted masters
+        */
+       if (!innetgr(goodgroup, &hostname[0], 0,0)) {
+               if (trace)
+                       (void)fprintf(fd, "get_goodgroup: %s not in %s\n",
+                                     &hostname[0], goodgroup);
+               return;
+       }
+       if (trace)
+               (void)fprintf(fd, "get_goodgroup: %s in %s\n",
+                                 &hostname[0], goodgroup);
+
+       /* mark the entire netgroup as trusted */
+       (void)setnetgrent(goodgroup);
+       while (getnetgrent(&mach,&usr,&dom)) {
+               if (0 != mach)
+                       add_good_host(mach,0);
+       }
+       (void)endnetgrent();
+
+       /* update list of slaves */
+       for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+               htp->good = good_host_name(&htp->name[0]);
+       }
+#endif /* HAVENIS */
+}
+
+
+/* see if a machine is trustworthy
+ */
+int                                    /* 1=trust hp to change our date */
+good_host_name(name)
+       char *name;
+{
+       register struct goodhost *ghp = goodhosts;
+       register char c;
+
+       if (!ghp || !Mflag)             /* trust everyone if no one named */
+               return 1;
+
+       c = *name;
+       do {
+               if (c == ghp->name[0]
+                   && !strcasecmp(name, ghp->name))
+                       return 1;       /* found him, so say so */
+       } while (0 != (ghp = ghp->next));
+
+       if (!strcasecmp(name,hostname)) /* trust ourself */
+               return 1;
+
+       return 0;                       /* did not find him */
+}