BSD 4_4 release
[unix-history] / usr / src / usr.sbin / timed / timed / timed.c
index 898e4b8..b8f7dc7 100644 (file)
-/*
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+/*-
+ * Copyright (c) 1985, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1983 Regents of the University of California.\n\
- All rights reserved.\n";
-#endif not lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
      The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
 
 #ifndef lint
 
 #ifndef lint
-static char sccsid[] = "@(#)timed.c    1.3 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)timed.c    8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#ifdef sgi
+#ident "$Revision: 1.25 $"
+#endif /* sgi */
 
 
-#include "globals.h"
 #define TSPTYPES
 #define TSPTYPES
-#include <protocols/timed.h>
+#include "globals.h"
 #include <net/if.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <setjmp.h>
 #include <net/if.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <setjmp.h>
-
-int id;
-int trace;
-int sock, sock_raw;
-int status;                            /* either MASTER or SLAVE */
-int backoff;
-int slvcount;                          /* no. of slaves controlled by master */
-int machup;
+#include "pathnames.h"
+#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;
 u_short sequence;                      /* sequence number */
 long delay1;
 long delay2;
 u_short sequence;                      /* sequence number */
 long delay1;
 long delay2;
-char hostname[MAXHOSTNAMELEN];
-struct in_addr broadcastaddr;          /* local net broadcast address */
-u_long netmask, mynet = 0;             /* my network number & netmask */
-struct sockaddr_in server;
-struct host hp[NHOSTS];
-char *fj;
-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;
 
-extern struct sockaddr_in from;
+struct netinfo *nettab = 0;
+struct netinfo *slavenet;
+int Mflag;
+int justquit = 0;
+int debug;
+
+static struct nets {
+       char    *name;
+       long    net;
+       struct nets *next;
+} *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
  *
- * For problems and suggestions, please send mail to gusella@BERKELEY
+ * 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 Mflag;
-       int nflag;
-       char mastername[MAXHOSTNAMELEN];
-       char *netname;
-       struct timeval time;
+       int nflag, iflag;
+       struct timeval ntime;
        struct servent *srvp;
        struct servent *srvp;
-       struct netent *getnetent();
-       struct netent *localnet;
-       struct sockaddr_in masteraddr;
-       struct tsp resp, conflict, *answer, *readmsg(), *acksend();
-       long casual();
-       char *malloc(), *strcpy();
-       char *date();
-       int n;
-       int n_addrlen;
-       char *n_addr;
-       char buf[BUFSIZ];
+       char buf[BUFSIZ], *cp, *cplim;
        struct ifconf ifc;
        struct ifconf ifc;
-       struct ifreq ifreq, *ifr;
-       struct sockaddr_in *sin;
-       
+       struct ifreq ifreq, ifreqf, *ifr;
+       register struct netinfo *ntp;
+       struct netinfo *ntip;
+       struct netinfo *savefromnet;
+       struct netent *nentp;
+       struct nets *nt;
+       struct sockaddr_in server;
+       u_short port;
+       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
 
 
-       Mflag = 0;
        on = 1;
        on = 1;
-       backoff = 1;
-       fj = "/usr/adm/timed.log";
-       trace = OFF;
        nflag = OFF;
        nflag = OFF;
-       openlog("timed", LOG_ODELAY, LOG_DAEMON);
+       iflag = OFF;
 
 
-       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++;
+               case 'n':
+                       if (iflag) {
+                               fprintf(stderr, IN_MSG);
+                               exit(1);
+                       } else {
                                nflag = ON;
                                nflag = ON;
-                               netname = *argv;
-                               while (*(++(*argv)+1)) ;
-                               break;
-                       default:
-                               fprintf(stderr, "timed: -%c: unknown option\n", 
-                                                       **argv);
-                               break;
+                               addnetname(optarg);
                        }
                        }
-               } while (*++(*argv));
-       }
+                       break;
 
 
-#ifndef DEBUG
-       if (fork())
-               exit(0);
-       { int s;
-         for (s = 0; s < 10; s++)
-               (void) close(s);
-         (void) open("/", 0);
-         (void) dup2(0, 1);
-         (void) dup2(0, 2);
-         s = open("/dev/tty", 2);
-         if (s >= 0) {
-               (void) ioctl(s, (int)TIOCNOTTY, (char *)0);
-               (void) close(s);
-         }
+               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;
+                               }
+                       } 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 {
+                                       if (0 > syssgi(SGI_SETTIMETRIM,
+                                                      trim)) {
+                                        perror("timed: syssgi(SETTIMETRIM)");
+                                       } else {
+                                               timetrim = trim;
+                                       }
+                                       if (i == 3) {
+                                               tot_adj = adj;
+                                               tot_ticks -= ticks;
+                                       }
+                               }
+                               (void)fclose(timetrim_st);
+                       }
+                       break;
+#endif /* sgi */
+
+               default:
+                       fprintf(stderr, USAGE);
+                       exit(1);
+                       break;
+               }
+       }
+       if (optind < argc) {
+               fprintf(stderr, USAGE);
+               exit(1);
        }
        }
-#endif
 
 
-       if (trace == ON) {
-               fd = fopen(fj, "w");
-               fprintf(fd, "Tracing started on: %s\n\n", 
-                                       date());
-               (void)fflush(fd);
+       /* If we care about which machine is the master, then we must
+        *      be willing to be a master
+        */
+       if (0 != goodgroup || 0 != goodhosts)
+               Mflag = 1;
+
+       if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
+               perror("gethostname");
+               exit(1);
        }
        }
-       openlog("timed", LOG_ODELAY|LOG_CONS, LOG_DAEMON);
+       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);
        }
                exit(1);
        }
+       port = srvp->s_port;
        server.sin_port = srvp->s_port;
        server.sin_family = AF_INET;
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
        server.sin_port = srvp->s_port;
        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, &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);
+       }
+#ifdef sgi
+       /*
+        * handle many slaves with our buffer
+        */
+       if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace,
+                        sizeof(bufspace))) {
+               perror("setsockopt");
                exit(1);
        }
                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 = casual((long)1, (long)MAXSEQ);     /* 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) {
-               localnet = getnetbyname(netname);
-               if (localnet == NULL) {
-                       syslog(LOG_ERR, "getnetbyname: unknown net %s",
-                               netname);
+       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);
                        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;
        }
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = buf;
-       if (ioctl(sock, (int)SIOCGIFCONF, (char *)&ifc) < 0) {
-               syslog(LOG_ERR, "get interface configuration: %m");
+       if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+               perror("timed: get interface configuration");
                exit(1);
        }
                exit(1);
        }
-       ifr = ifc.ifc_req;
-       for (n = ifc.ifc_len/sizeof(struct ifreq); n > 0; n--, ifr++) {
+       ntp = NULL;
+#ifdef sgi
+#define size(p)        (sizeof(*ifr) - sizeof(ifr->ifr_name))  /* XXX hack. kludge */
+#else
+#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;
+               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 (ioctl(sock, (int)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) {
+               if ((ifreqf.ifr_flags & IFF_UP) == 0)
                        continue;
                        continue;
-               }
-               if (ioctl(sock, (int)SIOCGIFNETMASK, 
-                                       (char *)&ifreq) < 0) {
-                       syslog(LOG_ERR, "get broadaddr: %m");
+               if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
+                   (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
                        continue;
                }
                        continue;
                }
-               netmask = ((struct sockaddr_in *)
-                       &ifreq.ifr_addr)->sin_addr.s_addr;
-               if (ioctl(sock, (int)SIOCGIFBRDADDR, 
-                                       (char *)&ifreq) < 0) {
-                       syslog(LOG_ERR, "get broadaddr: %m");
+
+
+               if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+                       perror("get netmask");
                        continue;
                }
                        continue;
                }
-               n_addrlen = sizeof(ifr->ifr_addr);
-               n_addr = (char *)malloc((unsigned)n_addrlen);
-               bcopy((char *)&ifreq.ifr_broadaddr, n_addr, n_addrlen);
-               sin = (struct sockaddr_in *)n_addr;
-               broadcastaddr = sin->sin_addr;
-               if (nflag) {
-                       u_long addr, mask;
-
-                       addr = ntohl(broadcastaddr.s_addr);
-                       mask = ntohl(netmask);
-                       while ((mask & 1) == 0) {
-                               addr >>= 1;
-                               mask >>= 1;
+               ntp->mask = ((struct sockaddr_in *)
+                       &ifreq.ifr_addr)->sin_addr.s_addr;
+
+               if (ifreqf.ifr_flags & IFF_BROADCAST) {
+                       if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+                               perror("get broadaddr");
+                               continue;
                        }
                        }
-                       if (addr != localnet->n_net)
+                       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 {
+                       if (ioctl(sock, SIOCGIFDSTADDR,
+                                               (char *)&ifreq) < 0) {
+                               perror("get destaddr");
                                continue;
                                continue;
+                       }
+                       ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
+                       ntp->net = ntp->dest_addr.sin_addr;
+               }
+
+               ntp->dest_addr.sin_port = port;
+
+               for (nt = nets; nt; nt = nt->next) {
+                       if (ntp->net.s_addr == nt->net)
+                               break;
+               }
+               if (nflag && !nt || iflag && nt)
+                       continue;
+
+               ntp->next = NULL;
+               if (nettab == NULL) {
+                       nettab = ntp;
+               } else {
+                       ntip->next = ntp;
                }
                }
-               mynet = netmask & broadcastaddr.s_addr;
-               break;
+               ntip = ntp;
+               ntp = NULL;
        }
        }
-       if (!mynet) {
-               syslog(LOG_ERR, "No network usable");
+       if (ntp)
+               (void) free((char *)ntp);
+       if (nettab == NULL) {
+               fprintf(stderr, "timed: no network usable\n");
                exit(1);
        }
 
                exit(1);
        }
 
-       /* 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);
 
 
-       /* look for master */
-       resp.tsp_type = TSP_MASTERREQ;
-       (void)strcpy(resp.tsp_name, hostname);
-       answer = acksend(&resp, (char *)ANYADDR, TSP_MASTERACK);
-       if (answer == NULL) {
-               status = MASTER;
-       } else {
-               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. 
-                */
-               time.tv_sec = 0;
-               time.tv_usec = 300000;
-               answer = readmsg(TSP_MASTERACK, (char *)ANYADDR, &time);
-               /*
-                * 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);
-                       server = masteraddr;
-                       if (acksend(&conflict, (char *)mastername, 
-                                                       TSP_ACK) == NULL) {
-                               syslog(LOG_ERR, "error on sending TSP_CONFLICT");
-                               exit(1);
+#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) {
+
+               case 0:
+                       checkignorednets();
+                       pickslavenet(0);
+                       break;
+               case 1:
+                       /* Just lost our master */
+                       if (slavenet != 0)
+                               slavenet->status = election(slavenet);
+                       if (!slavenet || slavenet->status == MASTER) {
+                               checkignorednets();
+                               pickslavenet(0);
+                       } else {
+                               makeslave(slavenet);    /* prune extras */
                        }
                        }
+                       break;
+
+               case 2:
+                       /* Just been told to quit */
+                       justquit = 1;
+                       pickslavenet(savefromnet);
+                       break;
                }
                }
-       }
-       if (Mflag) {
-               /* open raw socket used to measure time differences */
-               sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
-               if (sock_raw < 0)  {
-                       syslog(LOG_ERR, "opening raw socket: %m");
-                       exit (1);
+
+               setstatus();
+               if (!(status & MASTER) && sock_raw != -1) {
+                       /* sock_raw is not being used now */
+                       (void)close(sock_raw);
+                       sock_raw = -1;
                }
 
                }
 
-               /*
-                * number (increased by 1) of slaves controlled by master: 
-                * used in master.c, candidate.c, networkdelta.c, and 
-                * correct.c 
-                */
-               slvcount = 1;
+               if (status == MASTER)
+                       master();
+               else
+                       slave();
+
+       } else {
+               if (sock_raw != -1) {
+                       (void)close(sock_raw);
+                       sock_raw = -1;
+               }
+
+               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)
+                               rmnetmachs(ntp);
+                               ntp->status = NOMASTER;
+               }
+               checkignorednets();
+               pickslavenet(0);
+               setstatus();
+
+               slave();
+       }
+       /* NOTREACHED */
+#ifdef lint
+       return(0);
+#endif
+}
+
+/*
+ * suppress an upstart, untrustworthy, self-appointed master
+ */
+void
+suppress(addr, name,net)
+       struct sockaddr_in *addr;
+       char *name;
+       struct netinfo *net;
+{
+       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)
+       struct netinfo *ntp;
+{
+       struct tsp resp, conflict, *answer;
+       struct timeval ntime;
+       char mastername[MAXHOSTNAMELEN];
+       struct sockaddr_in masteraddr;
+
+       get_goodgroup(0);
+       ntp->status = SLAVE;
 
 
+       /* look for master */
+       resp.tsp_type = TSP_MASTERREQ;
+       (void)strcpy(resp.tsp_name, hostname);
+       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
-                * 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
+                * Various conditions can cause conflict: races between
+                * two just started timedaemons when no master is
+                * 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.
                 */
-               if (status == MASTER) {
-                       time.tv_sec = time.tv_usec = 0;
-                       answer = readmsg(TSP_MASTERREQ, (char *)ANYADDR, &time);
-                       if (answer != NULL) {
-                               status = SLAVE;
-                               goto startd;
+               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;
                        }
                        }
-       
-                       time.tv_sec = time.tv_usec = 0;
-                       answer = readmsg(TSP_MASTERUP, (char *)ANYADDR, &time);
-                       if (answer != NULL) {
-                               status = SLAVE;
-                               goto startd;
+                       return;
+               }
+
+               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;
                        }
                        }
-       
-                       time.tv_sec = time.tv_usec = 0;
-                       answer = readmsg(TSP_ELECTION, (char *)ANYADDR, &time);
-                       if (answer != NULL) 
-                               status = SLAVE;
+                       return;
                }
                }
-startd:
-               ret = setjmp(jmpenv);
-               switch (ret) {
 
 
-               case 0: 
-                       break;
-               case 1: 
-                       /* from slave */
-                       status = election();
+               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;
+               }
+
+               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;
+ */
+void
+setstatus()
+{
+       struct netinfo *ntp;
+
+       status = 0;
+       nmasternets = nslavenets = nnets = nignorednets = 0;
+       if (trace)
+               fprintf(fd, "Net status:\n");
+       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+               switch ((int)ntp->status) {
+               case MASTER:
+                       nmasternets++;
                        break;
                        break;
-               case 2:
-                       /* from master */
-                       status = SLAVE;
+               case SLAVE:
+                       nslavenets++;
                        break;
                        break;
-               default:
-                       /* this should not happen */
-                       syslog(LOG_ERR, "Attempt to enter invalid state");
+               case NOMASTER:
+               case IGNORE:
+                       nignorednets++;
                        break;
                }
                        break;
                }
-                       
-               if (status == MASTER) 
-                       master();
-               else 
-                       slave();
-       } else {
-               status = SLAVE;
-               /* if Mflag is not set timedaemon is forced to act as a slave */
-               if (setjmp(jmpenv)) {
-                       resp.tsp_type = TSP_SLAVEUP;
-                       (void)strcpy(resp.tsp_name, hostname);
-                       broadcast(&resp);
+               if (trace) {
+                       fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
+                       switch ((int)ntp->status) {
+                       case NOMASTER:
+                               fprintf(fd, "NOMASTER\n");
+                               break;
+                       case MASTER:
+                               fprintf(fd, "MASTER\n");
+                               break;
+                       case SLAVE:
+                               fprintf(fd, "SLAVE\n");
+                               break;
+                       case IGNORE:
+                               fprintf(fd, "IGNORE\n");
+                               break;
+                       default:
+                               fprintf(fd, "invalid state %d\n",
+                                       (int)ntp->status);
+                               break;
+                       }
                }
                }
-               slave();
+               nnets++;
+               status |= ntp->status;
        }
        }
+       status &= ~IGNORE;
+       if (trace)
+               fprintf(fd,
+                       "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
+                       nnets, nmasternets, nslavenets, nignorednets, delay2);
 }
 
 }
 
-/* 
- * `casual' returns a random number in the range [inf, sup]
+void
+makeslave(net)
+       struct netinfo *net;
+{
+       register struct netinfo *ntp;
+
+       for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+               if (ntp->status == SLAVE && ntp != net)
+                       ntp->status = IGNORE;
+       }
+       slavenet = net;
+}
+
+/*
+ * Try to become master over ignored nets..
  */
  */
+static void
+checkignorednets()
+{
+       register struct netinfo *ntp;
+
+       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;
+               }
+       }
+}
 
 
-long casual(inf, sup)
-long inf;
-long 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;
 {
 {
-       float value;
-       long random();
+       if (slavenet != 0 && slavenet->status == SLAVE) {
+               makeslave(slavenet);            /* prune extras */
+               return;
+       }
 
 
-       value = (float)(random() & 0x7fffffff) / 0x7fffffff;
-       return(inf + (sup - inf) * value);
+       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 inf, sup;
+{
+       double value;
+
+       value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
+       return(inf + (sup - inf)*value);
 }
 
 char *
 date()
 {
 }
 
 char *
 date()
 {
-       char    *ret;
-       char    *asctime();
+#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;
        struct  timeval tv;
-       struct  tm *localtime();
-       struct  tm *tp;
 
        (void)gettimeofday(&tv, (struct timezone *)0);
 
        (void)gettimeofday(&tv, (struct timezone *)0);
-       tp = localtime((time_t *)&tv.tv_sec);
-       ret = asctime(tp);
-       ret[19] = '\0';
-       return(ret);
+       return (ctime(&tv.tv_sec));
+#endif /* sgi */
+}
+
+void
+addnetname(name)
+       char *name;
+{
+       register struct nets **netlist = &nets;
+
+       while (*netlist)
+               netlist = &((*netlist)->next);
+       *netlist = (struct nets *)malloc(sizeof **netlist);
+       if (*netlist == 0) {
+               fprintf(stderr,"malloc failed\n");
+               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 */
 }
 }