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