3a61c864a488b28eefad54f8f9123b83841aabc4
[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  */
37
38 #define TSPTYPES
39 #include "globals.h"
40 #include <net/if.h>
41 #include <sys/file.h>
42 #include <sys/ioctl.h>
43 #include <setjmp.h>
44 #include "pathnames.h"
45 #include <math.h>
46 #include <sys/types.h>
47 #include <sys/times.h>
48
49 int trace = 0;
50 int sock, sock_raw = -1;
51 int status = 0;
52 u_short sequence;                       /* sequence number */
53 long delay1;
54 long delay2;
55
56 int nslavenets;                         /* nets were I could be a slave */
57 int nmasternets;                        /* nets were I could be a master */
58 int nignorednets;                       /* ignored nets */
59 int nnets;                              /* nets I am connected to */
60
61 FILE *fd;                               /* trace file FD */
62
63 jmp_buf jmpenv;
64
65 struct netinfo *nettab = NULL;
66 struct netinfo *slavenet;
67 int Mflag;
68 int justquit = 0;
69 int debug;
70
71 static struct nets {
72         char    *name;
73         long    net;
74         struct nets *next;
75 } *nets = NULL;
76
77 struct hosttbl hosttbl[NHOSTS+1];       /* known hosts */
78
79 static struct goodhost {                /* hosts that we trust */
80         char    name[MAXHOSTNAMELEN];
81         struct goodhost *next;
82         char    perm;
83 } *goodhosts;
84
85 static char *goodgroup;                 /* net group of trusted hosts */
86 static void checkignorednets(void);
87 static void pickslavenet(struct netinfo *);
88 static void add_good_host(char *, int);
89 static void usage(void);
90
91 /*
92  * The timedaemons synchronize the clocks of hosts in a local area network.
93  * One daemon runs as master, all the others as slaves. The master
94  * performs the task of computing clock differences and sends correction
95  * values to the slaves.
96  * Slaves start an election to choose a new master when the latter disappears
97  * because of a machine crash, network partition, or when killed.
98  * A resolution protocol is used to kill all but one of the masters
99  * that happen to exist in segments of a partitioned network when the
100  * network partition is fixed.
101  *
102  * Authors: Riccardo Gusella & Stefano Zatti
103  *
104  * overhauled at Silicon Graphics
105  */
106 int
107 main(int argc, char *argv[])
108 {
109         int on;
110         int ret;
111         int nflag, iflag;
112         struct timeval ntime;
113         struct servent *srvp;
114         char buf[BUFSIZ], *cp, *cplim;
115         struct ifconf ifc;
116         struct ifreq ifreq, ifreqf, *ifr;
117         struct netinfo *ntp;
118         struct netinfo *ntip;
119         struct netinfo *savefromnet;
120         struct netent *nentp;
121         struct nets *nt;
122         struct sockaddr_in server;
123         u_short port;
124         char c;
125
126         on = 1;
127         nflag = OFF;
128         iflag = OFF;
129         ntip = NULL;
130
131         opterr = 0;
132         while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
133                 switch (c) {
134                 case 'M':
135                         Mflag = 1;
136                         break;
137
138                 case 't':
139                         trace = 1;
140                         break;
141
142                 case 'n':
143                         if (iflag) {
144                                 errx(1, "-i and -n make no sense together");
145                         } else {
146                                 nflag = ON;
147                                 addnetname(optarg);
148                         }
149                         break;
150
151                 case 'i':
152                         if (nflag) {
153                                 errx(1, "-i and -n make no sense together");
154                         } else {
155                                 iflag = ON;
156                                 addnetname(optarg);
157                         }
158                         break;
159
160                 case 'F':
161                         add_good_host(optarg,1);
162                         while (optind < argc && argv[optind][0] != '-')
163                                 add_good_host(argv[optind++], 1);
164                         break;
165
166                 case 'd':
167                         debug = 1;
168                         break;
169                 case 'G':
170                         if (goodgroup != NULL)
171                                 errx(1, "only one net group");
172                         goodgroup = optarg;
173                         break;
174                 default:
175                         usage();
176                         break;
177                 }
178         }
179         if (optind < argc)
180                 usage();
181
182         /* If we care about which machine is the master, then we must
183          *      be willing to be a master
184          */
185         if (NULL != goodgroup || NULL != goodhosts)
186                 Mflag = 1;
187
188         if (gethostname(hostname, sizeof(hostname) - 1) < 0)
189                 err(1, "gethostname");
190         self.l_bak = &self;
191         self.l_fwd = &self;
192         self.h_bak = &self;
193         self.h_fwd = &self;
194         self.head = 1;
195         self.good = 1;
196
197         if (goodhosts != NULL)          /* trust ourself */
198                 add_good_host(hostname,1);
199
200         srvp = getservbyname("timed", "udp");
201         if (srvp == NULL)
202                 errx(1, "unknown service 'timed/udp'");
203         port = srvp->s_port;
204         bzero(&server, sizeof(struct sockaddr_in));
205         server.sin_port = srvp->s_port;
206         server.sin_family = AF_INET;
207         sock = socket(AF_INET, SOCK_DGRAM, 0);
208         if (sock < 0)
209                 err(1, "socket");
210         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
211                                                         sizeof(on)) < 0)
212                 err(1, "setsockopt");
213         if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
214                 if (errno == EADDRINUSE)
215                         warnx("time daemon already running");
216                 else
217                         warn("bind");
218                 exit(1);
219         }
220
221         /* choose a unique seed for random number generation */
222         gettimeofday(&ntime, 0);
223         srandom(ntime.tv_sec + ntime.tv_usec);
224
225         sequence = random();     /* initial seq number */
226
227         /* rounds kernel variable time to multiple of 5 ms. */
228         ntime.tv_sec = 0;
229         ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
230         adjtime(&ntime, NULL);
231
232         for (nt = nets; nt; nt = nt->next) {
233                 nentp = getnetbyname(nt->name);
234                 if (nentp == NULL) {
235                         nt->net = inet_network(nt->name);
236                         if (nt->net != (long)INADDR_NONE)
237                                 nentp = getnetbyaddr(nt->net, AF_INET);
238                 }
239                 if (nentp != NULL) {
240                         nt->net = nentp->n_net;
241                 } else if (nt->net == (long)INADDR_NONE) {
242                         errx(1, "unknown net %s", nt->name);
243                 } else if (nt->net == (long)INADDR_ANY) {
244                         errx(1, "bad net %s", nt->name);
245                 } else {
246                         warnx("warning: %s unknown in /etc/networks",
247                                 nt->name);
248                 }
249
250                 if (0 == (nt->net & 0xff000000))
251                     nt->net <<= 8;
252                 if (0 == (nt->net & 0xff000000))
253                     nt->net <<= 8;
254                 if (0 == (nt->net & 0xff000000))
255                     nt->net <<= 8;
256         }
257         ifc.ifc_len = sizeof(buf);
258         ifc.ifc_buf = buf;
259         if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
260                 err(1, "get interface configuration");
261         ntp = NULL;
262
263 #define size(p) max((p).sa_len, sizeof(p))
264         cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
265         for (cp = buf; cp < cplim;
266                         cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
267                 ifr = (struct ifreq *)cp;
268                 if (ifr->ifr_addr.sa_family != AF_INET)
269                         continue;
270                 if (!ntp)
271                         ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
272                 bzero(ntp,sizeof(*ntp));
273                 ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
274                 ntp->status = NOMASTER;
275                 ifreq = *ifr;
276                 ifreqf = *ifr;
277
278                 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
279                         warn("get interface flags");
280                         continue;
281                 }
282                 if ((ifreqf.ifr_flags & IFF_UP) == 0)
283                         continue;
284                 if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
285                     (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
286                         continue;
287                 }
288
289
290                 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
291                         warn("get netmask");
292                         continue;
293                 }
294                 ntp->mask = ((struct sockaddr_in *)
295                         &ifreq.ifr_addr)->sin_addr.s_addr;
296
297                 if (ifreqf.ifr_flags & IFF_BROADCAST) {
298                         if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
299                                 warn("get broadaddr");
300                                 continue;
301                         }
302                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
303                         /* What if the broadcast address is all ones?
304                          * So we cannot just mask ntp->dest_addr.  */
305                         ntp->net = ntp->my_addr;
306                         ntp->net.s_addr &= ntp->mask;
307                 } else {
308                         if (ioctl(sock, SIOCGIFDSTADDR,
309                                                 (char *)&ifreq) < 0) {
310                                 warn("get destaddr");
311                                 continue;
312                         }
313                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
314                         ntp->net = ntp->dest_addr.sin_addr;
315                 }
316
317                 ntp->dest_addr.sin_port = port;
318
319                 for (nt = nets; nt; nt = nt->next) {
320                         if (ntp->net.s_addr == htonl(nt->net))
321                                 break;
322                 }
323                 if ((nflag && !nt) || (iflag && nt))
324                         continue;
325
326                 ntp->next = NULL;
327                 if (nettab == NULL) {
328                         nettab = ntp;
329                 } else {
330                         ntip->next = ntp;
331                 }
332                 ntip = ntp;
333                 ntp = NULL;
334         }
335         if (ntp)
336                 free((char *)ntp);
337         if (nettab == NULL)
338                 errx(1, "no network usable");
339
340
341         /* microseconds to delay before responding to a broadcast */
342         delay1 = casual(1, 100*1000);
343
344         /* election timer delay in secs. */
345         delay2 = casual(MINTOUT, MAXTOUT);
346
347
348         if (!debug)
349                 daemon(debug, 0);
350
351         if (trace)
352                 traceon();
353         openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
354
355         /*
356          * keep returning here
357          */
358         ret = setjmp(jmpenv);
359         savefromnet = fromnet;
360         setstatus();
361
362         if (Mflag) {
363                 switch (ret) {
364
365                 case 0:
366                         checkignorednets();
367                         pickslavenet(0);
368                         break;
369                 case 1:
370                         /* Just lost our master */
371                         if (slavenet != NULL)
372                                 slavenet->status = election(slavenet);
373                         if (!slavenet || slavenet->status == MASTER) {
374                                 checkignorednets();
375                                 pickslavenet(0);
376                         } else {
377                                 makeslave(slavenet);    /* prune extras */
378                         }
379                         break;
380
381                 case 2:
382                         /* Just been told to quit */
383                         justquit = 1;
384                         pickslavenet(savefromnet);
385                         break;
386                 }
387
388                 setstatus();
389                 if (!(status & MASTER) && sock_raw != -1) {
390                         /* sock_raw is not being used now */
391                         close(sock_raw);
392                         sock_raw = -1;
393                 }
394
395                 if (status == MASTER)
396                         master();
397                 else
398                         slave();
399
400         } else {
401                 if (sock_raw != -1) {
402                         close(sock_raw);
403                         sock_raw = -1;
404                 }
405
406                 if (ret) {
407                         /* we just lost our master or were told to quit */
408                         justquit = 1;
409                 }
410                 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
411                         if (ntp->status == MASTER)
412                                 rmnetmachs(ntp);
413                                 ntp->status = NOMASTER;
414                 }
415                 checkignorednets();
416                 pickslavenet(0);
417                 setstatus();
418
419                 slave();
420         }
421         /* NOTREACHED */
422         return(0);
423 }
424
425 static void
426 usage(void)
427 {
428
429 #ifdef HAVENIS
430         fprintf(stderr, 
431 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
432 #else
433         fprintf(stderr,
434 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
435 #endif /* HAVENIS */
436         exit(1);
437 }
438
439 /*
440  * suppress an upstart, untrustworthy, self-appointed master
441  */
442 void
443 suppress(struct sockaddr_in *addr, char *name, struct netinfo *net)
444 {
445         struct sockaddr_in tgt;
446         char tname[MAXHOSTNAMELEN];
447         struct tsp msg;
448         static struct timeval wait;
449
450         if (trace)
451                 fprintf(fd, "suppress: %s\n", name);
452         tgt = *addr;
453         strlcpy(tname, name, sizeof(tname));
454
455         while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
456                 if (trace)
457                         fprintf(fd, "suppress:\tdiscarded packet from %s\n",
458                                     name);
459         }
460
461         syslog(LOG_NOTICE, "suppressing false master %s", tname);
462         msg.tsp_type = TSP_QUIT;
463         strlcpy(msg.tsp_name, hostname, sizeof(msg.tsp_name));
464         acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
465 }
466
467 void
468 lookformaster(struct netinfo *ntp)
469 {
470         struct tsp resp, conflict, *answer;
471         struct timeval ntime;
472         char mastername[MAXHOSTNAMELEN];
473         struct sockaddr_in masteraddr;
474
475         get_goodgroup(0);
476         ntp->status = SLAVE;
477
478         /* look for master */
479         resp.tsp_type = TSP_MASTERREQ;
480         strlcpy(resp.tsp_name, hostname, sizeof(resp.tsp_name));
481         answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
482                          TSP_MASTERACK, ntp, 0);
483         if (answer != NULL && !good_host_name(answer->tsp_name)) {
484                 suppress(&from, answer->tsp_name, ntp);
485                 ntp->status = NOMASTER;
486                 answer = NULL;
487         }
488         if (answer == NULL) {
489                 /*
490                  * Various conditions can cause conflict: races between
491                  * two just started timedaemons when no master is
492                  * present, or timedaemons started during an election.
493                  * A conservative approach is taken.  Give up and became a
494                  * slave, postponing election of a master until first
495                  * timer expires.
496                  */
497                 ntime.tv_sec = ntime.tv_usec = 0;
498                 answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
499                 if (answer != NULL) {
500                         if (!good_host_name(answer->tsp_name)) {
501                                 suppress(&from, answer->tsp_name, ntp);
502                                 ntp->status = NOMASTER;
503                         }
504                         return;
505                 }
506
507                 ntime.tv_sec = ntime.tv_usec = 0;
508                 answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
509                 if (answer != NULL) {
510                         if (!good_host_name(answer->tsp_name)) {
511                                 suppress(&from, answer->tsp_name, ntp);
512                                 ntp->status = NOMASTER;
513                         }
514                         return;
515                 }
516
517                 ntime.tv_sec = ntime.tv_usec = 0;
518                 answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
519                 if (answer != NULL) {
520                         if (!good_host_name(answer->tsp_name)) {
521                                 suppress(&from, answer->tsp_name, ntp);
522                                 ntp->status = NOMASTER;
523                         }
524                         return;
525                 }
526
527                 if (Mflag)
528                         ntp->status = MASTER;
529                 else
530                         ntp->status = NOMASTER;
531                 return;
532         }
533
534         ntp->status = SLAVE;
535         strlcpy(mastername, answer->tsp_name, sizeof(mastername));
536         masteraddr = from;
537
538         /*
539          * If network has been partitioned, there might be other
540          * masters; tell the one we have just acknowledged that
541          * it has to gain control over the others.
542          */
543         ntime.tv_sec = 0;
544         ntime.tv_usec = 300000;
545         answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
546         /*
547          * checking also not to send CONFLICT to ack'ed master
548          * due to duplicated MASTERACKs
549          */
550         if (answer != NULL &&
551             strcmp(answer->tsp_name, mastername) != 0) {
552                 conflict.tsp_type = TSP_CONFLICT;
553                 strlcpy(conflict.tsp_name, hostname, sizeof(conflict.tsp_name));
554                 if (!acksend(&conflict, &masteraddr, mastername,
555                              TSP_ACK, 0, 0)) {
556                         syslog(LOG_ERR,
557                                "error on sending TSP_CONFLICT");
558                 }
559         }
560 }
561
562 /*
563  * based on the current network configuration, set the status, and count
564  * networks;
565  */
566 void
567 setstatus(void)
568 {
569         struct netinfo *ntp;
570
571         status = 0;
572         nmasternets = nslavenets = nnets = nignorednets = 0;
573         if (trace)
574                 fprintf(fd, "Net status:\n");
575         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
576                 switch ((int)ntp->status) {
577                 case MASTER:
578                         nmasternets++;
579                         break;
580                 case SLAVE:
581                         nslavenets++;
582                         break;
583                 case NOMASTER:
584                 case IGNORE:
585                         nignorednets++;
586                         break;
587                 }
588                 if (trace) {
589                         fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
590                         switch ((int)ntp->status) {
591                         case NOMASTER:
592                                 fprintf(fd, "NOMASTER\n");
593                                 break;
594                         case MASTER:
595                                 fprintf(fd, "MASTER\n");
596                                 break;
597                         case SLAVE:
598                                 fprintf(fd, "SLAVE\n");
599                                 break;
600                         case IGNORE:
601                                 fprintf(fd, "IGNORE\n");
602                                 break;
603                         default:
604                                 fprintf(fd, "invalid state %d\n",
605                                         (int)ntp->status);
606                                 break;
607                         }
608                 }
609                 nnets++;
610                 status |= ntp->status;
611         }
612         status &= ~IGNORE;
613         if (trace)
614                 fprintf(fd,
615                     "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
616                     nnets, nmasternets, nslavenets, nignorednets, delay2);
617 }
618
619 void
620 makeslave(struct netinfo *net)
621 {
622         struct netinfo *ntp;
623
624         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
625                 if (ntp->status == SLAVE && ntp != net)
626                         ntp->status = IGNORE;
627         }
628         slavenet = net;
629 }
630
631 /*
632  * Try to become master over ignored nets..
633  */
634 static void
635 checkignorednets(void)
636 {
637         struct netinfo *ntp;
638
639         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
640                 if (!Mflag && ntp->status == SLAVE)
641                         break;
642
643                 if (ntp->status == IGNORE || ntp->status == NOMASTER) {
644                         lookformaster(ntp);
645                         if (!Mflag && ntp->status == SLAVE)
646                                 break;
647                 }
648         }
649 }
650
651 /*
652  * choose a good network on which to be a slave
653  *      The ignored networks must have already been checked.
654  *      Take a hint about for a good network.
655  */
656 static void
657 pickslavenet(struct netinfo *ntp)
658 {
659
660         if (slavenet != NULL && slavenet->status == SLAVE) {
661                 makeslave(slavenet);            /* prune extras */
662                 return;
663         }
664
665         if (ntp == NULL || ntp->status != SLAVE) {
666                 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
667                         if (ntp->status == SLAVE)
668                                 break;
669                 }
670         }
671         makeslave(ntp);
672 }
673
674 /*
675  * returns a random number in the range [inf, sup]
676  */
677 long
678 casual(long inf, long sup)
679 {
680         double value;
681
682         value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
683         return(inf + (sup - inf)*value);
684 }
685
686 char *
687 date(void)
688 {
689         struct  timeval tv;
690         time_t  tv_sec;
691
692         gettimeofday(&tv, NULL);
693         tv_sec = tv.tv_sec;
694         return (ctime(&tv_sec));
695 }
696
697 void
698 addnetname(char *name)
699 {
700         struct nets **netlist = &nets;
701
702         while (*netlist)
703                 netlist = &((*netlist)->next);
704         *netlist = (struct nets *)malloc(sizeof **netlist);
705         if (*netlist == NULL)
706                 errx(1, "malloc failed");
707         bzero((char *)*netlist, sizeof(**netlist));
708         (*netlist)->name = name;
709 }
710
711 /* note a host as trustworthy */
712 static void
713 add_good_host(char *name,
714               int perm)         /* 1=not part of the netgroup */
715 {
716         struct goodhost *ghp;
717         struct hostent *hentp;
718
719         ghp = (struct goodhost*)malloc(sizeof(*ghp));
720         if (!ghp) {
721                 syslog(LOG_ERR, "malloc failed");
722                 exit(1);
723         }
724
725         bzero((char*)ghp, sizeof(*ghp));
726         strlcpy(&ghp->name[0], name, sizeof(ghp->name));
727         ghp->next = goodhosts;
728         ghp->perm = perm;
729         goodhosts = ghp;
730
731         hentp = gethostbyname(name);
732         if (NULL == hentp && perm)
733                 warnx("unknown host %s", name);
734 }
735
736
737 /* update our image of the net-group of trustworthy hosts
738  */
739 void
740 get_goodgroup(int force)
741 {
742 # define NG_DELAY (30*60*CLK_TCK)       /* 30 minutes */
743         static unsigned long last_update = -NG_DELAY;
744         unsigned long new_update;
745         struct goodhost *ghp, **ghpp;
746 #ifdef HAVENIS
747         struct hosttbl *htp;
748         char *mach, *usr, *dom;
749 #endif /* HAVENIS */
750         struct tms tm;
751
752
753         /* if no netgroup, then we are finished */
754         if (goodgroup == NULL || !Mflag)
755                 return;
756
757         /* Do not chatter with the netgroup master too often.
758          */
759         new_update = times(&tm);
760         if (new_update < last_update + NG_DELAY
761             && !force)
762                 return;
763         last_update = new_update;
764
765         /* forget the old temporary entries */
766         ghpp = &goodhosts;
767         while (NULL != (ghp = *ghpp)) {
768                 if (!ghp->perm) {
769                         *ghpp = ghp->next;
770                         free((char*)ghp);
771                 } else {
772                         ghpp = &ghp->next;
773                 }
774         }
775
776 #ifdef HAVENIS
777         /* quit now if we are not one of the trusted masters
778          */
779         if (!innetgr(goodgroup, &hostname[0], 0,0)) {
780                 if (trace)
781                         fprintf(fd, "get_goodgroup: %s not in %s\n",
782                                       &hostname[0], goodgroup);
783                 return;
784         }
785         if (trace)
786                 fprintf(fd, "get_goodgroup: %s in %s\n",
787                                   &hostname[0], goodgroup);
788
789         /* mark the entire netgroup as trusted */
790         setnetgrent(goodgroup);
791         while (getnetgrent(&mach,&usr,&dom)) {
792                 if (NULL != mach)
793                         add_good_host(mach,0);
794         }
795         endnetgrent();
796
797         /* update list of slaves */
798         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
799                 htp->good = good_host_name(&htp->name[0]);
800         }
801 #endif /* HAVENIS */
802 }
803
804
805 /* see if a machine is trustworthy
806  */
807 int                                     /* 1=trust hp to change our date */
808 good_host_name(char *name)
809 {
810         struct goodhost *ghp = goodhosts;
811         char c;
812
813         if (!ghp || !Mflag)             /* trust everyone if no one named */
814                 return 1;
815
816         c = *name;
817         do {
818                 if (c == ghp->name[0]
819                     && !strcasecmp(name, ghp->name))
820                         return 1;       /* found him, so say so */
821         } while (NULL != (ghp = ghp->next));
822
823         if (!strcasecmp(name,hostname)) /* trust ourself */
824                 return 1;
825
826         return 0;                       /* did not find him */
827 }