Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.sbin / timed / timed / timed.c
1 /*-
2  * Copyright (c) 1985, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1985, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)timed.c  8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.sbin/timed/timed/timed.c,v 1.9 1999/08/28 01:20:19 peter Exp $
36  * $DragonFly: src/usr.sbin/timed/timed/timed.c,v 1.2 2003/06/17 04:30:03 dillon Exp $
37  */
38
39 #define TSPTYPES
40 #include "globals.h"
41 #include <net/if.h>
42 #include <sys/file.h>
43 #include <sys/ioctl.h>
44 #include <setjmp.h>
45 #include "pathnames.h"
46 #include <math.h>
47 #include <sys/types.h>
48 #include <sys/times.h>
49 #ifdef sgi
50 #include <unistd.h>
51 #include <sys/syssgi.h>
52 #include <sys/schedctl.h>
53 #endif /* sgi */
54
55 int trace = 0;
56 int sock, sock_raw = -1;
57 int status = 0;
58 u_short sequence;                       /* sequence number */
59 long delay1;
60 long delay2;
61
62 int nslavenets;                         /* nets were I could be a slave */
63 int nmasternets;                        /* nets were I could be a master */
64 int nignorednets;                       /* ignored nets */
65 int nnets;                              /* nets I am connected to */
66
67 FILE *fd;                               /* trace file FD */
68
69 jmp_buf jmpenv;
70
71 struct netinfo *nettab = 0;
72 struct netinfo *slavenet;
73 int Mflag;
74 int justquit = 0;
75 int debug;
76
77 static struct nets {
78         char    *name;
79         long    net;
80         struct nets *next;
81 } *nets = 0;
82
83 struct hosttbl hosttbl[NHOSTS+1];       /* known hosts */
84
85 static struct goodhost {                /* hosts that we trust */
86         char    name[MAXHOSTNAMELEN];
87         struct goodhost *next;
88         char    perm;
89 } *goodhosts;
90
91 static char *goodgroup;                 /* net group of trusted hosts */
92 static void checkignorednets __P((void));
93 static void pickslavenet __P((struct netinfo *));
94 static void add_good_host __P((char *, int));
95
96 #ifdef sgi
97 char *timetrim_fn;
98 char *timetrim_wpat = "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
99 char *timetrim_rpat = "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
100 long timetrim;
101 double tot_adj, hr_adj;                 /* totals in nsec */
102 double tot_ticks, hr_ticks;
103
104 int bufspace = 60*1024;
105 #endif
106
107 static void usage __P((void));
108
109 /*
110  * The timedaemons synchronize the clocks of hosts in a local area network.
111  * One daemon runs as master, all the others as slaves. The master
112  * performs the task of computing clock differences and sends correction
113  * values to the slaves.
114  * Slaves start an election to choose a new master when the latter disappears
115  * because of a machine crash, network partition, or when killed.
116  * A resolution protocol is used to kill all but one of the masters
117  * that happen to exist in segments of a partitioned network when the
118  * network partition is fixed.
119  *
120  * Authors: Riccardo Gusella & Stefano Zatti
121  *
122  * overhauled at Silicon Graphics
123  */
124 int
125 main(argc, argv)
126         int argc;
127         char *argv[];
128 {
129         int on;
130         int ret;
131         int nflag, iflag;
132         struct timeval ntime;
133         struct servent *srvp;
134         char buf[BUFSIZ], *cp, *cplim;
135         struct ifconf ifc;
136         struct ifreq ifreq, ifreqf, *ifr;
137         register struct netinfo *ntp;
138         struct netinfo *ntip;
139         struct netinfo *savefromnet;
140         struct netent *nentp;
141         struct nets *nt;
142         struct sockaddr_in server;
143         u_short port;
144         char c;
145 #ifdef sgi
146         FILE *timetrim_st;
147 #endif
148
149 #ifdef sgi
150         struct tms tms;
151 #endif /* sgi */
152
153 #ifdef lint
154         ntip = NULL;
155 #endif
156
157         on = 1;
158         nflag = OFF;
159         iflag = OFF;
160
161 #ifdef sgi
162         if (0 > syssgi(SGI_GETTIMETRIM, &timetrim)) {
163                 warn("syssgi(GETTIMETRIM)");
164                 timetrim = 0;
165         }
166         tot_ticks = hr_ticks = times(&tms);
167 #endif /* sgi */
168
169         opterr = 0;
170         while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
171                 switch (c) {
172                 case 'M':
173                         Mflag = 1;
174                         break;
175
176                 case 't':
177                         trace = 1;
178                         break;
179
180                 case 'n':
181                         if (iflag) {
182                                 errx(1, "-i and -n make no sense together");
183                         } else {
184                                 nflag = ON;
185                                 addnetname(optarg);
186                         }
187                         break;
188
189                 case 'i':
190                         if (nflag) {
191                                 errx(1, "-i and -n make no sense together");
192                         } else {
193                                 iflag = ON;
194                                 addnetname(optarg);
195                         }
196                         break;
197
198                 case 'F':
199                         add_good_host(optarg,1);
200                         while (optind < argc && argv[optind][0] != '-')
201                                 add_good_host(argv[optind++], 1);
202                         break;
203
204                 case 'd':
205                         debug = 1;
206                         break;
207                 case 'G':
208                         if (goodgroup != 0)
209                                 errx(1, "only one net group");
210                         goodgroup = optarg;
211                         break;
212 #ifdef sgi
213                 case 'P':
214                         timetrim_fn = optarg;
215                         timetrim_st = fopen(timetrim_fn, "r+");
216                         if (0 == timetrim_st) {
217                                 if (errno != ENOENT) {
218                                         warn("%s", timetrim_fn);
219                                         timetrim_fn = 0;
220                                 }
221                         } else {
222                                 int i;
223                                 long trim;
224                                 double adj, ticks;
225
226                                 i = fscanf(timetrim_st, timetrim_rpat,
227                                            &trim, &adj, &ticks);
228                                 if (i < 1
229                                     || trim > MAX_TRIM
230                                     || trim < -MAX_TRIM
231                                     || i == 2
232                                     || (i == 3
233                                         && trim != rint(adj*CLK_TCK/ticks))) {
234                                         if (trace && i != EOF)
235                                                 warn(
236                                                 "unrecognized contents in %s",
237                                                               timetrim_fn);
238                                 } else {
239                                         if (0 > syssgi(SGI_SETTIMETRIM,
240                                                        trim)) {
241                                          warn("syssgi(SETTIMETRIM)");
242                                         } else {
243                                                 timetrim = trim;
244                                         }
245                                         if (i == 3) {
246                                                 tot_adj = adj;
247                                                 tot_ticks -= ticks;
248                                         }
249                                 }
250                                 (void)fclose(timetrim_st);
251                         }
252                         break;
253 #endif /* sgi */
254
255                 default:
256                         usage();
257                         break;
258                 }
259         }
260         if (optind < argc)
261                 usage();
262
263         /* If we care about which machine is the master, then we must
264          *      be willing to be a master
265          */
266         if (0 != goodgroup || 0 != goodhosts)
267                 Mflag = 1;
268
269         if (gethostname(hostname, sizeof(hostname) - 1) < 0)
270                 err(1, "gethostname");
271         self.l_bak = &self;
272         self.l_fwd = &self;
273         self.h_bak = &self;
274         self.h_fwd = &self;
275         self.head = 1;
276         self.good = 1;
277
278         if (goodhosts != 0)             /* trust ourself */
279                 add_good_host(hostname,1);
280
281         srvp = getservbyname("timed", "udp");
282         if (srvp == 0)
283                 errx(1, "unknown service 'timed/udp'");
284         port = srvp->s_port;
285         bzero(&server, sizeof(struct sockaddr_in));
286         server.sin_port = srvp->s_port;
287         server.sin_family = AF_INET;
288         sock = socket(AF_INET, SOCK_DGRAM, 0);
289         if (sock < 0)
290                 err(1, "socket");
291         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
292                                                         sizeof(on)) < 0)
293                 err(1, "setsockopt");
294         if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
295                 if (errno == EADDRINUSE)
296                         warnx("time daemon already running");
297                 else
298                         warn("bind");
299                 exit(1);
300         }
301 #ifdef sgi
302         /*
303          * handle many slaves with our buffer
304          */
305         if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace,
306                          sizeof(bufspace)))
307                 err(1, "setsockopt");
308 #endif /* sgi */
309
310         /* choose a unique seed for random number generation */
311         (void)gettimeofday(&ntime, 0);
312         srandom(ntime.tv_sec + ntime.tv_usec);
313
314         sequence = random();     /* initial seq number */
315
316 #ifndef sgi
317         /* rounds kernel variable time to multiple of 5 ms. */
318         ntime.tv_sec = 0;
319         ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
320         (void)adjtime(&ntime, (struct timeval *)0);
321 #endif /* sgi */
322
323         for (nt = nets; nt; nt = nt->next) {
324                 nentp = getnetbyname(nt->name);
325                 if (nentp == 0) {
326                         nt->net = inet_network(nt->name);
327                         if (nt->net != INADDR_NONE)
328                                 nentp = getnetbyaddr(nt->net, AF_INET);
329                 }
330                 if (nentp != 0) {
331                         nt->net = nentp->n_net;
332                 } else if (nt->net == INADDR_NONE) {
333                         errx(1, "unknown net %s", nt->name);
334                 } else if (nt->net == INADDR_ANY) {
335                         errx(1, "bad net %s", nt->name);
336                 } else {
337                         warnx("warning: %s unknown in /etc/networks",
338                                 nt->name);
339                 }
340
341                 if (0 == (nt->net & 0xff000000))
342                     nt->net <<= 8;
343                 if (0 == (nt->net & 0xff000000))
344                     nt->net <<= 8;
345                 if (0 == (nt->net & 0xff000000))
346                     nt->net <<= 8;
347         }
348         ifc.ifc_len = sizeof(buf);
349         ifc.ifc_buf = buf;
350         if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
351                 err(1, "get interface configuration");
352         ntp = NULL;
353 #ifdef sgi
354 #define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name))  /* XXX hack. kludge */
355 #else
356 #define size(p) max((p).sa_len, sizeof(p))
357 #endif
358         cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
359         for (cp = buf; cp < cplim;
360                         cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
361                 ifr = (struct ifreq *)cp;
362                 if (ifr->ifr_addr.sa_family != AF_INET)
363                         continue;
364                 if (!ntp)
365                         ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
366                 bzero(ntp,sizeof(*ntp));
367                 ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
368                 ntp->status = NOMASTER;
369                 ifreq = *ifr;
370                 ifreqf = *ifr;
371
372                 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
373                         warn("get interface flags");
374                         continue;
375                 }
376                 if ((ifreqf.ifr_flags & IFF_UP) == 0)
377                         continue;
378                 if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
379                     (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
380                         continue;
381                 }
382
383
384                 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
385                         warn("get netmask");
386                         continue;
387                 }
388                 ntp->mask = ((struct sockaddr_in *)
389                         &ifreq.ifr_addr)->sin_addr.s_addr;
390
391                 if (ifreqf.ifr_flags & IFF_BROADCAST) {
392                         if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
393                                 warn("get broadaddr");
394                                 continue;
395                         }
396                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
397                         /* What if the broadcast address is all ones?
398                          * So we cannot just mask ntp->dest_addr.  */
399                         ntp->net = ntp->my_addr;
400                         ntp->net.s_addr &= ntp->mask;
401                 } else {
402                         if (ioctl(sock, SIOCGIFDSTADDR,
403                                                 (char *)&ifreq) < 0) {
404                                 warn("get destaddr");
405                                 continue;
406                         }
407                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
408                         ntp->net = ntp->dest_addr.sin_addr;
409                 }
410
411                 ntp->dest_addr.sin_port = port;
412
413                 for (nt = nets; nt; nt = nt->next) {
414                         if (ntp->net.s_addr == htonl(nt->net))
415                                 break;
416                 }
417                 if ((nflag && !nt) || (iflag && nt))
418                         continue;
419
420                 ntp->next = NULL;
421                 if (nettab == NULL) {
422                         nettab = ntp;
423                 } else {
424                         ntip->next = ntp;
425                 }
426                 ntip = ntp;
427                 ntp = NULL;
428         }
429         if (ntp)
430                 (void) free((char *)ntp);
431         if (nettab == NULL)
432                 errx(1, "no network usable");
433
434
435 #ifdef sgi
436         (void)schedctl(RENICE,0,10);       /* run fast to get good time */
437
438         /* ticks to delay before responding to a broadcast */
439         delay1 = casual(0, CLK_TCK/10);
440 #else
441
442         /* microseconds to delay before responding to a broadcast */
443         delay1 = casual(1, 100*1000);
444 #endif /* sgi */
445
446         /* election timer delay in secs. */
447         delay2 = casual(MINTOUT, MAXTOUT);
448
449
450 #ifdef sgi
451         (void)_daemonize(debug ? _DF_NOFORK|_DF_NOCHDIR : 0, sock, -1, -1);
452 #else
453         if (!debug)
454                 daemon(debug, 0);
455 #endif /* sgi */
456
457         if (trace)
458                 traceon();
459         openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
460
461         /*
462          * keep returning here
463          */
464         ret = setjmp(jmpenv);
465         savefromnet = fromnet;
466         setstatus();
467
468         if (Mflag) {
469                 switch (ret) {
470
471                 case 0:
472                         checkignorednets();
473                         pickslavenet(0);
474                         break;
475                 case 1:
476                         /* Just lost our master */
477                         if (slavenet != 0)
478                                 slavenet->status = election(slavenet);
479                         if (!slavenet || slavenet->status == MASTER) {
480                                 checkignorednets();
481                                 pickslavenet(0);
482                         } else {
483                                 makeslave(slavenet);    /* prune extras */
484                         }
485                         break;
486
487                 case 2:
488                         /* Just been told to quit */
489                         justquit = 1;
490                         pickslavenet(savefromnet);
491                         break;
492                 }
493
494                 setstatus();
495                 if (!(status & MASTER) && sock_raw != -1) {
496                         /* sock_raw is not being used now */
497                         (void)close(sock_raw);
498                         sock_raw = -1;
499                 }
500
501                 if (status == MASTER)
502                         master();
503                 else
504                         slave();
505
506         } else {
507                 if (sock_raw != -1) {
508                         (void)close(sock_raw);
509                         sock_raw = -1;
510                 }
511
512                 if (ret) {
513                         /* we just lost our master or were told to quit */
514                         justquit = 1;
515                 }
516                 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
517                         if (ntp->status == MASTER)
518                                 rmnetmachs(ntp);
519                                 ntp->status = NOMASTER;
520                 }
521                 checkignorednets();
522                 pickslavenet(0);
523                 setstatus();
524
525                 slave();
526         }
527         /* NOTREACHED */
528         return(0);
529 }
530
531 static void
532 usage()
533 {
534 #ifdef sgi
535         fprintf(stderr, "%s\n%s\n",
536 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]",
537 "             [-G netgp] [-P trimfile]");
538 #else
539 #ifdef HAVENIS
540         fprintf(stderr, 
541 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
542 #else
543         fprintf(stderr,
544 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
545 #endif /* HAVENIS */
546 #endif /* sgi */
547         exit(1);
548 }
549
550 /*
551  * suppress an upstart, untrustworthy, self-appointed master
552  */
553 void
554 suppress(addr, name,net)
555         struct sockaddr_in *addr;
556         char *name;
557         struct netinfo *net;
558 {
559         struct sockaddr_in tgt;
560         char tname[MAXHOSTNAMELEN];
561         struct tsp msg;
562         static struct timeval wait;
563
564         if (trace)
565                 fprintf(fd, "suppress: %s\n", name);
566         tgt = *addr;
567         (void)strcpy(tname, name);
568
569         while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
570                 if (trace)
571                         fprintf(fd, "suppress:\tdiscarded packet from %s\n",
572                                     name);
573         }
574
575         syslog(LOG_NOTICE, "suppressing false master %s", tname);
576         msg.tsp_type = TSP_QUIT;
577         (void)strcpy(msg.tsp_name, hostname);
578         (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
579 }
580
581 void
582 lookformaster(ntp)
583         struct netinfo *ntp;
584 {
585         struct tsp resp, conflict, *answer;
586         struct timeval ntime;
587         char mastername[MAXHOSTNAMELEN];
588         struct sockaddr_in masteraddr;
589
590         get_goodgroup(0);
591         ntp->status = SLAVE;
592
593         /* look for master */
594         resp.tsp_type = TSP_MASTERREQ;
595         (void)strcpy(resp.tsp_name, hostname);
596         answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
597                          TSP_MASTERACK, ntp, 0);
598         if (answer != 0 && !good_host_name(answer->tsp_name)) {
599                 suppress(&from, answer->tsp_name, ntp);
600                 ntp->status = NOMASTER;
601                 answer = 0;
602         }
603         if (answer == 0) {
604                 /*
605                  * Various conditions can cause conflict: races between
606                  * two just started timedaemons when no master is
607                  * present, or timedaemons started during an election.
608                  * A conservative approach is taken.  Give up and became a
609                  * slave, postponing election of a master until first
610                  * timer expires.
611                  */
612                 ntime.tv_sec = ntime.tv_usec = 0;
613                 answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
614                 if (answer != 0) {
615                         if (!good_host_name(answer->tsp_name)) {
616                                 suppress(&from, answer->tsp_name, ntp);
617                                 ntp->status = NOMASTER;
618                         }
619                         return;
620                 }
621
622                 ntime.tv_sec = ntime.tv_usec = 0;
623                 answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
624                 if (answer != 0) {
625                         if (!good_host_name(answer->tsp_name)) {
626                                 suppress(&from, answer->tsp_name, ntp);
627                                 ntp->status = NOMASTER;
628                         }
629                         return;
630                 }
631
632                 ntime.tv_sec = ntime.tv_usec = 0;
633                 answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
634                 if (answer != 0) {
635                         if (!good_host_name(answer->tsp_name)) {
636                                 suppress(&from, answer->tsp_name, ntp);
637                                 ntp->status = NOMASTER;
638                         }
639                         return;
640                 }
641
642                 if (Mflag)
643                         ntp->status = MASTER;
644                 else
645                         ntp->status = NOMASTER;
646                 return;
647         }
648
649         ntp->status = SLAVE;
650         (void)strcpy(mastername, answer->tsp_name);
651         masteraddr = from;
652
653         /*
654          * If network has been partitioned, there might be other
655          * masters; tell the one we have just acknowledged that
656          * it has to gain control over the others.
657          */
658         ntime.tv_sec = 0;
659         ntime.tv_usec = 300000;
660         answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
661         /*
662          * checking also not to send CONFLICT to ack'ed master
663          * due to duplicated MASTERACKs
664          */
665         if (answer != NULL &&
666             strcmp(answer->tsp_name, mastername) != 0) {
667                 conflict.tsp_type = TSP_CONFLICT;
668                 (void)strcpy(conflict.tsp_name, hostname);
669                 if (!acksend(&conflict, &masteraddr, mastername,
670                              TSP_ACK, 0, 0)) {
671                         syslog(LOG_ERR,
672                                "error on sending TSP_CONFLICT");
673                 }
674         }
675 }
676
677 /*
678  * based on the current network configuration, set the status, and count
679  * networks;
680  */
681 void
682 setstatus()
683 {
684         struct netinfo *ntp;
685
686         status = 0;
687         nmasternets = nslavenets = nnets = nignorednets = 0;
688         if (trace)
689                 fprintf(fd, "Net status:\n");
690         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
691                 switch ((int)ntp->status) {
692                 case MASTER:
693                         nmasternets++;
694                         break;
695                 case SLAVE:
696                         nslavenets++;
697                         break;
698                 case NOMASTER:
699                 case IGNORE:
700                         nignorednets++;
701                         break;
702                 }
703                 if (trace) {
704                         fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
705                         switch ((int)ntp->status) {
706                         case NOMASTER:
707                                 fprintf(fd, "NOMASTER\n");
708                                 break;
709                         case MASTER:
710                                 fprintf(fd, "MASTER\n");
711                                 break;
712                         case SLAVE:
713                                 fprintf(fd, "SLAVE\n");
714                                 break;
715                         case IGNORE:
716                                 fprintf(fd, "IGNORE\n");
717                                 break;
718                         default:
719                                 fprintf(fd, "invalid state %d\n",
720                                         (int)ntp->status);
721                                 break;
722                         }
723                 }
724                 nnets++;
725                 status |= ntp->status;
726         }
727         status &= ~IGNORE;
728         if (trace)
729                 fprintf(fd,
730                     "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
731                     nnets, nmasternets, nslavenets, nignorednets, delay2);
732 }
733
734 void
735 makeslave(net)
736         struct netinfo *net;
737 {
738         register struct netinfo *ntp;
739
740         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
741                 if (ntp->status == SLAVE && ntp != net)
742                         ntp->status = IGNORE;
743         }
744         slavenet = net;
745 }
746
747 /*
748  * Try to become master over ignored nets..
749  */
750 static void
751 checkignorednets()
752 {
753         register struct netinfo *ntp;
754
755         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
756                 if (!Mflag && ntp->status == SLAVE)
757                         break;
758
759                 if (ntp->status == IGNORE || ntp->status == NOMASTER) {
760                         lookformaster(ntp);
761                         if (!Mflag && ntp->status == SLAVE)
762                                 break;
763                 }
764         }
765 }
766
767 /*
768  * choose a good network on which to be a slave
769  *      The ignored networks must have already been checked.
770  *      Take a hint about for a good network.
771  */
772 static void
773 pickslavenet(ntp)
774         struct netinfo *ntp;
775 {
776         if (slavenet != 0 && slavenet->status == SLAVE) {
777                 makeslave(slavenet);            /* prune extras */
778                 return;
779         }
780
781         if (ntp == 0 || ntp->status != SLAVE) {
782                 for (ntp = nettab; ntp != 0; ntp = ntp->next) {
783                         if (ntp->status == SLAVE)
784                                 break;
785                 }
786         }
787         makeslave(ntp);
788 }
789
790 /*
791  * returns a random number in the range [inf, sup]
792  */
793 long
794 casual(inf, sup)
795         long inf, sup;
796 {
797         double value;
798
799         value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
800         return(inf + (sup - inf)*value);
801 }
802
803 char *
804 date()
805 {
806 #ifdef sgi
807         struct  timeval tv;
808         static char tm[32];
809
810         (void)gettimeofday(&tv, (struct timezone *)0);
811         (void)cftime(tm, "%D %T", &tv.tv_sec);
812         return (tm);
813 #else
814         struct  timeval tv;
815         time_t  tv_sec;
816
817         (void)gettimeofday(&tv, (struct timezone *)0);
818         tv_sec = tv.tv_sec;
819         return (ctime(&tv_sec));
820 #endif /* sgi */
821 }
822
823 void
824 addnetname(name)
825         char *name;
826 {
827         register struct nets **netlist = &nets;
828
829         while (*netlist)
830                 netlist = &((*netlist)->next);
831         *netlist = (struct nets *)malloc(sizeof **netlist);
832         if (*netlist == 0)
833                 errx(1, "malloc failed");
834         bzero((char *)*netlist, sizeof(**netlist));
835         (*netlist)->name = name;
836 }
837
838 /* note a host as trustworthy */
839 static void
840 add_good_host(name, perm)
841         char *name;
842         int perm;                       /* 1=not part of the netgroup */
843 {
844         register struct goodhost *ghp;
845         register struct hostent *hentp;
846
847         ghp = (struct goodhost*)malloc(sizeof(*ghp));
848         if (!ghp) {
849                 syslog(LOG_ERR, "malloc failed");
850                 exit(1);
851         }
852
853         bzero((char*)ghp, sizeof(*ghp));
854         (void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
855         ghp->next = goodhosts;
856         ghp->perm = perm;
857         goodhosts = ghp;
858
859         hentp = gethostbyname(name);
860         if (0 == hentp && perm)
861                 warnx("unknown host %s", name);
862 }
863
864
865 /* update our image of the net-group of trustworthy hosts
866  */
867 void
868 get_goodgroup(force)
869         int force;
870 {
871 # define NG_DELAY (30*60*CLK_TCK)       /* 30 minutes */
872         static unsigned long last_update = -NG_DELAY;
873         unsigned long new_update;
874         struct goodhost *ghp, **ghpp;
875 #ifdef HAVENIS
876         struct hosttbl *htp;
877         char *mach, *usr, *dom;
878 #endif /* HAVENIS */
879         struct tms tm;
880
881
882         /* if no netgroup, then we are finished */
883         if (goodgroup == 0 || !Mflag)
884                 return;
885
886         /* Do not chatter with the netgroup master too often.
887          */
888         new_update = times(&tm);
889         if (new_update < last_update + NG_DELAY
890             && !force)
891                 return;
892         last_update = new_update;
893
894         /* forget the old temporary entries */
895         ghpp = &goodhosts;
896         while (0 != (ghp = *ghpp)) {
897                 if (!ghp->perm) {
898                         *ghpp = ghp->next;
899                         free((char*)ghp);
900                 } else {
901                         ghpp = &ghp->next;
902                 }
903         }
904
905 #ifdef HAVENIS
906         /* quit now if we are not one of the trusted masters
907          */
908         if (!innetgr(goodgroup, &hostname[0], 0,0)) {
909                 if (trace)
910                         (void)fprintf(fd, "get_goodgroup: %s not in %s\n",
911                                       &hostname[0], goodgroup);
912                 return;
913         }
914         if (trace)
915                 (void)fprintf(fd, "get_goodgroup: %s in %s\n",
916                                   &hostname[0], goodgroup);
917
918         /* mark the entire netgroup as trusted */
919         (void)setnetgrent(goodgroup);
920         while (getnetgrent(&mach,&usr,&dom)) {
921                 if (0 != mach)
922                         add_good_host(mach,0);
923         }
924         (void)endnetgrent();
925
926         /* update list of slaves */
927         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
928                 htp->good = good_host_name(&htp->name[0]);
929         }
930 #endif /* HAVENIS */
931 }
932
933
934 /* see if a machine is trustworthy
935  */
936 int                                     /* 1=trust hp to change our date */
937 good_host_name(name)
938         char *name;
939 {
940         register struct goodhost *ghp = goodhosts;
941         register char c;
942
943         if (!ghp || !Mflag)             /* trust everyone if no one named */
944                 return 1;
945
946         c = *name;
947         do {
948                 if (c == ghp->name[0]
949                     && !strcasecmp(name, ghp->name))
950                         return 1;       /* found him, so say so */
951         } while (0 != (ghp = ghp->next));
952
953         if (!strcasecmp(name,hostname)) /* trust ourself */
954                 return 1;
955
956         return 0;                       /* did not find him */
957 }