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