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