Merge branch 'vendor/GCC47'
[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                 }
416                 checkignorednets();
417                 pickslavenet(0);
418                 setstatus();
419
420                 slave();
421         }
422         /* NOTREACHED */
423         return(0);
424 }
425
426 static void
427 usage(void)
428 {
429
430 #ifdef HAVENIS
431         fprintf(stderr, 
432 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
433 #else
434         fprintf(stderr,
435 "usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
436 #endif /* HAVENIS */
437         exit(1);
438 }
439
440 /*
441  * suppress an upstart, untrustworthy, self-appointed master
442  */
443 void
444 suppress(struct sockaddr_in *addr, char *name, struct netinfo *net)
445 {
446         struct sockaddr_in tgt;
447         char tname[MAXHOSTNAMELEN];
448         struct tsp msg;
449         static struct timeval wait;
450
451         if (trace)
452                 fprintf(fd, "suppress: %s\n", name);
453         tgt = *addr;
454         strlcpy(tname, name, sizeof(tname));
455
456         while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
457                 if (trace)
458                         fprintf(fd, "suppress:\tdiscarded packet from %s\n",
459                                     name);
460         }
461
462         syslog(LOG_NOTICE, "suppressing false master %s", tname);
463         msg.tsp_type = TSP_QUIT;
464         strlcpy(msg.tsp_name, hostname, sizeof(msg.tsp_name));
465         acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
466 }
467
468 void
469 lookformaster(struct netinfo *ntp)
470 {
471         struct tsp resp, conflict, *answer;
472         struct timeval ntime;
473         char mastername[MAXHOSTNAMELEN];
474         struct sockaddr_in masteraddr;
475
476         get_goodgroup(0);
477         ntp->status = SLAVE;
478
479         /* look for master */
480         resp.tsp_type = TSP_MASTERREQ;
481         strlcpy(resp.tsp_name, hostname, sizeof(resp.tsp_name));
482         answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
483                          TSP_MASTERACK, ntp, 0);
484         if (answer != NULL && !good_host_name(answer->tsp_name)) {
485                 suppress(&from, answer->tsp_name, ntp);
486                 ntp->status = NOMASTER;
487                 answer = NULL;
488         }
489         if (answer == NULL) {
490                 /*
491                  * Various conditions can cause conflict: races between
492                  * two just started timedaemons when no master is
493                  * present, or timedaemons started during an election.
494                  * A conservative approach is taken.  Give up and became a
495                  * slave, postponing election of a master until first
496                  * timer expires.
497                  */
498                 ntime.tv_sec = ntime.tv_usec = 0;
499                 answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
500                 if (answer != NULL) {
501                         if (!good_host_name(answer->tsp_name)) {
502                                 suppress(&from, answer->tsp_name, ntp);
503                                 ntp->status = NOMASTER;
504                         }
505                         return;
506                 }
507
508                 ntime.tv_sec = ntime.tv_usec = 0;
509                 answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
510                 if (answer != NULL) {
511                         if (!good_host_name(answer->tsp_name)) {
512                                 suppress(&from, answer->tsp_name, ntp);
513                                 ntp->status = NOMASTER;
514                         }
515                         return;
516                 }
517
518                 ntime.tv_sec = ntime.tv_usec = 0;
519                 answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
520                 if (answer != NULL) {
521                         if (!good_host_name(answer->tsp_name)) {
522                                 suppress(&from, answer->tsp_name, ntp);
523                                 ntp->status = NOMASTER;
524                         }
525                         return;
526                 }
527
528                 if (Mflag)
529                         ntp->status = MASTER;
530                 else
531                         ntp->status = NOMASTER;
532                 return;
533         }
534
535         ntp->status = SLAVE;
536         strlcpy(mastername, answer->tsp_name, sizeof(mastername));
537         masteraddr = from;
538
539         /*
540          * If network has been partitioned, there might be other
541          * masters; tell the one we have just acknowledged that
542          * it has to gain control over the others.
543          */
544         ntime.tv_sec = 0;
545         ntime.tv_usec = 300000;
546         answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
547         /*
548          * checking also not to send CONFLICT to ack'ed master
549          * due to duplicated MASTERACKs
550          */
551         if (answer != NULL &&
552             strcmp(answer->tsp_name, mastername) != 0) {
553                 conflict.tsp_type = TSP_CONFLICT;
554                 strlcpy(conflict.tsp_name, hostname, sizeof(conflict.tsp_name));
555                 if (!acksend(&conflict, &masteraddr, mastername,
556                              TSP_ACK, 0, 0)) {
557                         syslog(LOG_ERR,
558                                "error on sending TSP_CONFLICT");
559                 }
560         }
561 }
562
563 /*
564  * based on the current network configuration, set the status, and count
565  * networks;
566  */
567 void
568 setstatus(void)
569 {
570         struct netinfo *ntp;
571
572         status = 0;
573         nmasternets = nslavenets = nnets = nignorednets = 0;
574         if (trace)
575                 fprintf(fd, "Net status:\n");
576         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
577                 switch ((int)ntp->status) {
578                 case MASTER:
579                         nmasternets++;
580                         break;
581                 case SLAVE:
582                         nslavenets++;
583                         break;
584                 case NOMASTER:
585                 case IGNORE:
586                         nignorednets++;
587                         break;
588                 }
589                 if (trace) {
590                         fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
591                         switch ((int)ntp->status) {
592                         case NOMASTER:
593                                 fprintf(fd, "NOMASTER\n");
594                                 break;
595                         case MASTER:
596                                 fprintf(fd, "MASTER\n");
597                                 break;
598                         case SLAVE:
599                                 fprintf(fd, "SLAVE\n");
600                                 break;
601                         case IGNORE:
602                                 fprintf(fd, "IGNORE\n");
603                                 break;
604                         default:
605                                 fprintf(fd, "invalid state %d\n",
606                                         (int)ntp->status);
607                                 break;
608                         }
609                 }
610                 nnets++;
611                 status |= ntp->status;
612         }
613         status &= ~IGNORE;
614         if (trace)
615                 fprintf(fd,
616                     "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
617                     nnets, nmasternets, nslavenets, nignorednets, delay2);
618 }
619
620 void
621 makeslave(struct netinfo *net)
622 {
623         struct netinfo *ntp;
624
625         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
626                 if (ntp->status == SLAVE && ntp != net)
627                         ntp->status = IGNORE;
628         }
629         slavenet = net;
630 }
631
632 /*
633  * Try to become master over ignored nets..
634  */
635 static void
636 checkignorednets(void)
637 {
638         struct netinfo *ntp;
639
640         for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
641                 if (!Mflag && ntp->status == SLAVE)
642                         break;
643
644                 if (ntp->status == IGNORE || ntp->status == NOMASTER) {
645                         lookformaster(ntp);
646                         if (!Mflag && ntp->status == SLAVE)
647                                 break;
648                 }
649         }
650 }
651
652 /*
653  * choose a good network on which to be a slave
654  *      The ignored networks must have already been checked.
655  *      Take a hint about for a good network.
656  */
657 static void
658 pickslavenet(struct netinfo *ntp)
659 {
660
661         if (slavenet != NULL && slavenet->status == SLAVE) {
662                 makeslave(slavenet);            /* prune extras */
663                 return;
664         }
665
666         if (ntp == NULL || ntp->status != SLAVE) {
667                 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
668                         if (ntp->status == SLAVE)
669                                 break;
670                 }
671         }
672         makeslave(ntp);
673 }
674
675 /*
676  * returns a random number in the range [inf, sup]
677  */
678 long
679 casual(long inf, long sup)
680 {
681         double value;
682
683         value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
684         return(inf + (sup - inf)*value);
685 }
686
687 char *
688 date(void)
689 {
690         struct  timeval tv;
691         time_t  tv_sec;
692
693         gettimeofday(&tv, NULL);
694         tv_sec = tv.tv_sec;
695         return (ctime(&tv_sec));
696 }
697
698 void
699 addnetname(char *name)
700 {
701         struct nets **netlist = &nets;
702
703         while (*netlist)
704                 netlist = &((*netlist)->next);
705         *netlist = (struct nets *)malloc(sizeof **netlist);
706         if (*netlist == NULL)
707                 errx(1, "malloc failed");
708         bzero((char *)*netlist, sizeof(**netlist));
709         (*netlist)->name = name;
710 }
711
712 /* note a host as trustworthy */
713 static void
714 add_good_host(char *name,
715               int perm)         /* 1=not part of the netgroup */
716 {
717         struct goodhost *ghp;
718         struct hostent *hentp;
719
720         ghp = (struct goodhost*)malloc(sizeof(*ghp));
721         if (!ghp) {
722                 syslog(LOG_ERR, "malloc failed");
723                 exit(1);
724         }
725
726         bzero((char*)ghp, sizeof(*ghp));
727         strlcpy(&ghp->name[0], name, sizeof(ghp->name));
728         ghp->next = goodhosts;
729         ghp->perm = perm;
730         goodhosts = ghp;
731
732         hentp = gethostbyname(name);
733         if (NULL == hentp && perm)
734                 warnx("unknown host %s", name);
735 }
736
737
738 /* update our image of the net-group of trustworthy hosts
739  */
740 void
741 get_goodgroup(int force)
742 {
743 # define NG_DELAY (30*60*CLK_TCK)       /* 30 minutes */
744         static unsigned long last_update = -NG_DELAY;
745         unsigned long new_update;
746         struct goodhost *ghp, **ghpp;
747 #ifdef HAVENIS
748         struct hosttbl *htp;
749         char *mach, *usr, *dom;
750 #endif /* HAVENIS */
751         struct tms tm;
752
753
754         /* if no netgroup, then we are finished */
755         if (goodgroup == NULL || !Mflag)
756                 return;
757
758         /* Do not chatter with the netgroup master too often.
759          */
760         new_update = times(&tm);
761         if (new_update < last_update + NG_DELAY
762             && !force)
763                 return;
764         last_update = new_update;
765
766         /* forget the old temporary entries */
767         ghpp = &goodhosts;
768         while (NULL != (ghp = *ghpp)) {
769                 if (!ghp->perm) {
770                         *ghpp = ghp->next;
771                         free((char*)ghp);
772                 } else {
773                         ghpp = &ghp->next;
774                 }
775         }
776
777 #ifdef HAVENIS
778         /* quit now if we are not one of the trusted masters
779          */
780         if (!innetgr(goodgroup, &hostname[0], 0,0)) {
781                 if (trace)
782                         fprintf(fd, "get_goodgroup: %s not in %s\n",
783                                       &hostname[0], goodgroup);
784                 return;
785         }
786         if (trace)
787                 fprintf(fd, "get_goodgroup: %s in %s\n",
788                                   &hostname[0], goodgroup);
789
790         /* mark the entire netgroup as trusted */
791         setnetgrent(goodgroup);
792         while (getnetgrent(&mach,&usr,&dom)) {
793                 if (NULL != mach)
794                         add_good_host(mach,0);
795         }
796         endnetgrent();
797
798         /* update list of slaves */
799         for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
800                 htp->good = good_host_name(&htp->name[0]);
801         }
802 #endif /* HAVENIS */
803 }
804
805
806 /* see if a machine is trustworthy
807  */
808 int                                     /* 1=trust hp to change our date */
809 good_host_name(char *name)
810 {
811         struct goodhost *ghp = goodhosts;
812         char c;
813
814         if (!ghp || !Mflag)             /* trust everyone if no one named */
815                 return 1;
816
817         c = *name;
818         do {
819                 if (c == ghp->name[0]
820                     && !strcasecmp(name, ghp->name))
821                         return 1;       /* found him, so say so */
822         } while (NULL != (ghp = ghp->next));
823
824         if (!strcasecmp(name,hostname)) /* trust ourself */
825                 return 1;
826
827         return 0;                       /* did not find him */
828 }