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