Generally use NULL instead of explicitly casting 0 to some pointer type.
[dragonfly.git] / usr.sbin / timed / timed / timed.c
1 /*-
2  * Copyright (c) 1985, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1985, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)timed.c  8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.sbin/timed/timed/timed.c,v 1.9 1999/08/28 01:20:19 peter Exp $
36  * $DragonFly: src/usr.sbin/timed/timed/timed.c,v 1.8 2004/09/05 02:20:15 dillon Exp $
37  */
38
39 #define TSPTYPES
40 #include "globals.h"
41 #include <net/if.h>
42 #include <sys/file.h>
43 #include <sys/ioctl.h>
44 #include <setjmp.h>
45 #include "pathnames.h"
46 #include <math.h>
47 #include <sys/types.h>
48 #include <sys/times.h>
49
50 int trace = 0;
51 int sock, sock_raw = -1;
52 int status = 0;
53 u_short sequence;                       /* sequence number */
54 long delay1;
55 long delay2;
56
57 int nslavenets;                         /* nets were I could be a slave */
58 int nmasternets;                        /* nets were I could be a master */
59 int nignorednets;                       /* ignored nets */
60 int nnets;                              /* nets I am connected to */
61
62 FILE *fd;                               /* trace file FD */
63
64 jmp_buf jmpenv;
65
66 struct netinfo *nettab = 0;
67 struct netinfo *slavenet;
68 int Mflag;
69 int justquit = 0;
70 int debug;
71
72 static struct nets {
73         char    *name;
74         long    net;
75         struct nets *next;
76 } *nets = 0;
77
78 struct hosttbl hosttbl[NHOSTS+1];       /* known hosts */
79
80 static struct goodhost {                /* hosts that we trust */
81         char    name[MAXHOSTNAMELEN];
82         struct goodhost *next;
83         char    perm;
84 } *goodhosts;
85
86 static char *goodgroup;                 /* net group of trusted hosts */
87 static void checkignorednets(void);
88 static void pickslavenet(struct netinfo *);
89 static void add_good_host(char *, int);
90 static void usage(void);
91
92 /*
93  * The timedaemons synchronize the clocks of hosts in a local area network.
94  * One daemon runs as master, all the others as slaves. The master
95  * performs the task of computing clock differences and sends correction
96  * values to the slaves.
97  * Slaves start an election to choose a new master when the latter disappears
98  * because of a machine crash, network partition, or when killed.
99  * A resolution protocol is used to kill all but one of the masters
100  * that happen to exist in segments of a partitioned network when the
101  * network partition is fixed.
102  *
103  * Authors: Riccardo Gusella & Stefano Zatti
104  *
105  * overhauled at Silicon Graphics
106  */
107 int
108 main(int argc, char *argv[])
109 {
110         int on;
111         int ret;
112         int nflag, iflag;
113         struct timeval ntime;
114         struct servent *srvp;
115         char buf[BUFSIZ], *cp, *cplim;
116         struct ifconf ifc;
117         struct ifreq ifreq, ifreqf, *ifr;
118         struct netinfo *ntp;
119         struct netinfo *ntip;
120         struct netinfo *savefromnet;
121         struct netent *nentp;
122         struct nets *nt;
123         struct sockaddr_in server;
124         u_short port;
125         char c;
126
127         on = 1;
128         nflag = OFF;
129         iflag = OFF;
130         ntip = NULL;
131
132         opterr = 0;
133         while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
134                 switch (c) {
135                 case 'M':
136                         Mflag = 1;
137                         break;
138
139                 case 't':
140                         trace = 1;
141                         break;
142
143                 case 'n':
144                         if (iflag) {
145                                 errx(1, "-i and -n make no sense together");
146                         } else {
147                                 nflag = ON;
148                                 addnetname(optarg);
149                         }
150                         break;
151
152                 case 'i':
153                         if (nflag) {
154                                 errx(1, "-i and -n make no sense together");
155                         } else {
156                                 iflag = ON;
157                                 addnetname(optarg);
158                         }
159                         break;
160
161                 case 'F':
162                         add_good_host(optarg,1);
163                         while (optind < argc && argv[optind][0] != '-')
164                                 add_good_host(argv[optind++], 1);
165                         break;
166
167                 case 'd':
168                         debug = 1;
169                         break;
170                 case 'G':
171                         if (goodgroup != 0)
172                                 errx(1, "only one net group");
173                         goodgroup = optarg;
174                         break;
175                 default:
176                         usage();
177                         break;
178                 }
179         }
180         if (optind < argc)
181                 usage();
182
183         /* If we care about which machine is the master, then we must
184          *      be willing to be a master
185          */
186         if (0 != goodgroup || 0 != goodhosts)
187                 Mflag = 1;
188
189         if (gethostname(hostname, sizeof(hostname) - 1) < 0)
190                 err(1, "gethostname");
191         self.l_bak = &self;
192         self.l_fwd = &self;
193         self.h_bak = &self;
194         self.h_fwd = &self;
195         self.head = 1;
196         self.good = 1;
197
198         if (goodhosts != 0)             /* trust ourself */
199                 add_good_host(hostname,1);
200
201         srvp = getservbyname("timed", "udp");
202         if (srvp == 0)
203                 errx(1, "unknown service 'timed/udp'");
204         port = srvp->s_port;
205         bzero(&server, sizeof(struct sockaddr_in));
206         server.sin_port = srvp->s_port;
207         server.sin_family = AF_INET;
208         sock = socket(AF_INET, SOCK_DGRAM, 0);
209         if (sock < 0)
210                 err(1, "socket");
211         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
212                                                         sizeof(on)) < 0)
213                 err(1, "setsockopt");
214         if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
215                 if (errno == EADDRINUSE)
216                         warnx("time daemon already running");
217                 else
218                         warn("bind");
219                 exit(1);
220         }
221
222         /* choose a unique seed for random number generation */
223         gettimeofday(&ntime, 0);
224         srandom(ntime.tv_sec + ntime.tv_usec);
225
226         sequence = random();     /* initial seq number */
227
228         /* rounds kernel variable time to multiple of 5 ms. */
229         ntime.tv_sec = 0;
230         ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
231         adjtime(&ntime, NULL);
232
233         for (nt = nets; nt; nt = nt->next) {
234                 nentp = getnetbyname(nt->name);
235                 if (nentp == 0) {
236                         nt->net = inet_network(nt->name);
237                         if (nt->net != (long)INADDR_NONE)
238                                 nentp = getnetbyaddr(nt->net, AF_INET);
239                 }
240                 if (nentp != 0) {
241                         nt->net = nentp->n_net;
242                 } else if (nt->net == (long)INADDR_NONE) {
243                         errx(1, "unknown net %s", nt->name);
244                 } else if (nt->net == (long)INADDR_ANY) {
245                         errx(1, "bad net %s", nt->name);
246                 } else {
247                         warnx("warning: %s unknown in /etc/networks",
248                                 nt->name);
249                 }
250
251                 if (0 == (nt->net & 0xff000000))
252                     nt->net <<= 8;
253                 if (0 == (nt->net & 0xff000000))
254                     nt->net <<= 8;
255                 if (0 == (nt->net & 0xff000000))
256                     nt->net <<= 8;
257         }
258         ifc.ifc_len = sizeof(buf);
259         ifc.ifc_buf = buf;
260         if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
261                 err(1, "get interface configuration");
262         ntp = NULL;
263
264 #define size(p) max((p).sa_len, sizeof(p))
265         cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
266         for (cp = buf; cp < cplim;
267                         cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
268                 ifr = (struct ifreq *)cp;
269                 if (ifr->ifr_addr.sa_family != AF_INET)
270                         continue;
271                 if (!ntp)
272                         ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
273                 bzero(ntp,sizeof(*ntp));
274                 ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
275                 ntp->status = NOMASTER;
276                 ifreq = *ifr;
277                 ifreqf = *ifr;
278
279                 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
280                         warn("get interface flags");
281                         continue;
282                 }
283                 if ((ifreqf.ifr_flags & IFF_UP) == 0)
284                         continue;
285                 if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
286                     (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
287                         continue;
288                 }
289
290
291                 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
292                         warn("get netmask");
293                         continue;
294                 }
295                 ntp->mask = ((struct sockaddr_in *)
296                         &ifreq.ifr_addr)->sin_addr.s_addr;
297
298                 if (ifreqf.ifr_flags & IFF_BROADCAST) {
299                         if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
300                                 warn("get broadaddr");
301                                 continue;
302                         }
303                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
304                         /* What if the broadcast address is all ones?
305                          * So we cannot just mask ntp->dest_addr.  */
306                         ntp->net = ntp->my_addr;
307                         ntp->net.s_addr &= ntp->mask;
308                 } else {
309                         if (ioctl(sock, SIOCGIFDSTADDR,
310                                                 (char *)&ifreq) < 0) {
311                                 warn("get destaddr");
312                                 continue;
313                         }
314                         ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
315                         ntp->net = ntp->dest_addr.sin_addr;
316                 }
317
318                 ntp->dest_addr.sin_port = port;
319
320                 for (nt = nets; nt; nt = nt->next) {
321                         if (ntp->net.s_addr == htonl(nt->net))
322                                 break;
323                 }
324                 if ((nflag && !nt) || (iflag && nt))
325                         continue;
326
327                 ntp->next = NULL;
328                 if (nettab == NULL) {
329                         nettab = ntp;
330                 } else {
331                         ntip->next = ntp;
332                 }
333                 ntip = ntp;
334                 ntp = NULL;
335         }
336         if (ntp)
337                 free((char *)ntp);
338         if (nettab == NULL)
339                 errx(1, "no network usable");
340
341
342         /* microseconds to delay before responding to a broadcast */
343         delay1 = casual(1, 100*1000);
344
345         /* election timer delay in secs. */
346         delay2 = casual(MINTOUT, MAXTOUT);
347
348
349         if (!debug)
350                 daemon(debug, 0);
351
352         if (trace)
353                 traceon();
354         openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
355
356         /*
357          * keep returning here
358          */
359         ret = setjmp(jmpenv);
360         savefromnet = fromnet;
361         setstatus();
362
363         if (Mflag) {
364                 switch (ret) {
365
366                 case 0:
367                         checkignorednets();
368                         pickslavenet(0);
369                         break;
370                 case 1:
371                         /* Just lost our master */
372                         if (slavenet != 0)
373                                 slavenet->status = election(slavenet);
374                         if (!slavenet || slavenet->status == MASTER) {
375                                 checkignorednets();
376                                 pickslavenet(0);
377                         } else {
378                                 makeslave(slavenet);    /* prune extras */
379                         }
380                         break;
381
382                 case 2:
383                         /* Just been told to quit */
384                         justquit = 1;
385                         pickslavenet(savefromnet);
386                         break;
387                 }
388
389                 setstatus();
390                 if (!(status & MASTER) && sock_raw != -1) {
391                         /* sock_raw is not being used now */
392                         close(sock_raw);
393                         sock_raw = -1;
394                 }
395
396                 if (status == MASTER)
397                         master();
398                 else
399                         slave();
400
401         } else {
402                 if (sock_raw != -1) {
403                         close(sock_raw);
404                         sock_raw = -1;
405                 }
406
407                 if (ret) {
408                         /* we just lost our master or were told to quit */
409                         justquit = 1;
410                 }
411                 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
412                         if (ntp->status == MASTER)
413                                 rmnetmachs(ntp);
414                                 ntp->status = NOMASTER;
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 != 0 && !good_host_name(answer->tsp_name)) {
485                 suppress(&from, answer->tsp_name, ntp);
486                 ntp->status = NOMASTER;
487                 answer = 0;
488         }
489         if (answer == 0) {
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 != 0) {
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 != 0) {
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 != 0) {
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 != 0 && slavenet->status == SLAVE) {
662                 makeslave(slavenet);            /* prune extras */
663                 return;
664         }
665
666         if (ntp == 0 || ntp->status != SLAVE) {
667                 for (ntp = nettab; ntp != 0; 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 == 0)
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 (0 == 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 == 0 || !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 (0 != (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 (0 != 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 (0 != (ghp = ghp->next));
823
824         if (!strcasecmp(name,hostname)) /* trust ourself */
825                 return 1;
826
827         return 0;                       /* did not find him */
828 }