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