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