xref: /netbsd-src/usr.sbin/timed/timed/timed.c (revision 33b82b03392e2106db8728db8eea7f5301157325)
1 /*	$NetBSD: timed.c,v 1.31 2024/11/03 10:43:27 rillig Exp $	*/
2 
3 /*-
4  * Copyright (c) 1985, 1993 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1985, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)timed.c	8.2 (Berkeley) 3/26/95";
41 #else
42 __RCSID("$NetBSD: timed.c,v 1.31 2024/11/03 10:43:27 rillig Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #define TSPTYPES
47 #include "globals.h"
48 #include <net/if.h>
49 #include <sys/file.h>
50 #include <sys/ioctl.h>
51 #include <setjmp.h>
52 #include "pathnames.h"
53 #include <math.h>
54 #include <sys/types.h>
55 #include <sys/times.h>
56 #include <util.h>
57 #include <ifaddrs.h>
58 #include <err.h>
59 
60 #ifdef HAVENIS
61 #include <netgroup.h>
62 #endif
63 
64 int trace = 0;
65 int sock, sock_raw = -1;
66 int status = 0;
67 u_short sequence;			/* sequence number */
68 long delay1;
69 long delay2;
70 
71 int nslavenets;				/* nets where I could be a slave */
72 int nmasternets;			/* nets where I could be a master */
73 int nignorednets;			/* ignored nets */
74 int nnets;				/* nets I am connected to */
75 
76 FILE *fd;				/* trace file FD */
77 
78 jmp_buf jmpenv;
79 
80 struct netinfo *nettab = 0;
81 struct netinfo *slavenet;
82 int Mflag;
83 int justquit = 0;
84 int debug;
85 
86 static struct nets {
87 	char	 *name;
88 	in_addr_t net;
89 	struct nets *next;
90 } *nets = 0;
91 
92 struct hosttbl hosttbl[NHOSTS+1];	/* known hosts */
93 
94 static struct goodhost {		/* hosts that we trust */
95 	char	name[MAXHOSTNAMELEN+1];
96 	struct goodhost *next;
97 	char	perm;
98 } *goodhosts;
99 
100 static char *goodgroup;			/* net group of trusted hosts */
101 static void checkignorednets(void);
102 static void pickslavenet(struct netinfo *);
103 static void add_good_host(const char*,char);
104 
105 
106 /*
107  * The timedaemons synchronize the clocks of hosts in a local area network.
108  * One daemon runs as master, all the others as slaves. The master
109  * performs the task of computing clock differences and sends correction
110  * values to the slaves.
111  * Slaves start an election to choose a new master when the latter disappears
112  * because of a machine crash, network partition, or when killed.
113  * A resolution protocol is used to kill all but one of the masters
114  * that happen to exist in segments of a partitioned network when the
115  * network partition is fixed.
116  *
117  * Authors: Riccardo Gusella & Stefano Zatti
118  */
119 
120 int
121 main(int argc, char *argv[])
122 {
123 	int on;
124 	int ret;
125 	int nflag, iflag;
126 	struct timeval ntime;
127 	struct servent *srvp;
128 	struct netinfo *ntp;
129 	struct netinfo *ntip;
130 	struct netinfo *savefromnet;
131 	struct netent *nentp;
132 	struct nets *nt;
133 	struct sockaddr_in server;
134 	uint16_t port;
135 	int c;
136 	struct ifaddrs *ifap, *ifa;
137 
138 #define	IN_MSG "-i and -n make no sense together\n"
139 #ifdef HAVENIS
140 #define USAGE "[-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
141 #else
142 #define USAGE "[-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
143 #endif /* HAVENIS */
144 
145 	ntip = NULL;
146 
147 	on = 1;
148 	nflag = OFF;
149 	iflag = OFF;
150 
151 	opterr = 0;
152 	while ((c = getopt(argc, argv, "Mtdn:i:F:G:")) != -1) {
153 		switch (c) {
154 		case 'M':
155 			Mflag = 1;
156 			break;
157 
158 		case 't':
159 			trace = 1;
160 			break;
161 
162 		case 'n':
163 			if (iflag)
164 				errx(EXIT_FAILURE, "%s", IN_MSG);
165 			nflag = ON;
166 			addnetname(optarg);
167 			break;
168 
169 		case 'i':
170 			if (nflag)
171 				errx(EXIT_FAILURE, "%s", IN_MSG);
172 			iflag = ON;
173 			addnetname(optarg);
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(EXIT_FAILURE, "timed: only one net group");
188 			goodgroup = optarg;
189 			break;
190 		default:
191 			errx(EXIT_FAILURE, "%s", USAGE);
192 			break;
193 		}
194 	}
195 	if (optind < argc)
196 		errx(EXIT_FAILURE, "%s", USAGE);
197 
198 	/* If we care about which machine is the master, then we must
199 	 *	be willing to be a master
200 	 */
201 	if (0 != goodgroup || 0 != goodhosts)
202 		Mflag = 1;
203 
204 	if (gethostname(hostname, sizeof(hostname)) < 0)
205 		err(EXIT_FAILURE, "gethostname");
206 
207 	hostname[sizeof(hostname) - 1] = '\0';
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 == NULL)
220 		errx(EXIT_FAILURE, "unknown service 'timed/udp'");
221 
222 	port = srvp->s_port;
223 	(void)memset(&server, 0, sizeof(server));
224 	server.sin_port = srvp->s_port;
225 	server.sin_family = AF_INET;
226 	sock = socket(AF_INET, SOCK_DGRAM, 0);
227 	if (sock < 0)
228 		err(EXIT_FAILURE, "socket");
229 
230 	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
231 		err(EXIT_FAILURE, "setsockopt");
232 
233 	if (bind(sock, (struct sockaddr*)(void *)&server, sizeof(server))) {
234 		if (errno == EADDRINUSE)
235 			errx(EXIT_FAILURE, "time daemon already running");
236 		else
237 			err(EXIT_FAILURE, "bind");
238 	}
239 
240 	/* initial seq number */
241 	sequence = (u_short)arc4random_uniform(UINT16_MAX);
242 
243 	/* rounds kernel variable time to multiple of 5 ms. */
244 	ntime.tv_sec = 0;
245 	ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
246 	(void)adjtime(&ntime, (struct timeval *)0);
247 
248 	for (nt = nets; nt; nt = nt->next) {
249 		nentp = getnetbyname(nt->name);
250 		if (nentp == 0) {
251 			nt->net = inet_network(nt->name);
252 			if (nt->net != INADDR_NONE)
253 				nentp = getnetbyaddr(nt->net, AF_INET);
254 		}
255 		if (nentp != 0)
256 			nt->net = nentp->n_net;
257 		else if (nt->net == INADDR_NONE)
258 			errx(EXIT_FAILURE, "unknown net %s", nt->name);
259 		else if (nt->net == INADDR_ANY)
260 			errx(EXIT_FAILURE, "bad net %s", nt->name);
261 		else
262 			warnx("warning: %s unknown in /etc/networks",
263 				nt->name);
264 
265 		if (0 == (nt->net & 0xff000000))
266 		    nt->net <<= 8;
267 		if (0 == (nt->net & 0xff000000))
268 		    nt->net <<= 8;
269 		if (0 == (nt->net & 0xff000000))
270 		    nt->net <<= 8;
271 	}
272 	if (getifaddrs(&ifap) != 0)
273 		err(EXIT_FAILURE, "get interface configuration");
274 
275 	ntp = NULL;
276 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
277 		if (ifa->ifa_addr->sa_family != AF_INET)
278 			continue;
279 		if (!ntp)
280 			ntp = malloc(sizeof(struct netinfo));
281 		(void)memset(ntp, 0, sizeof(*ntp));
282 		ntp->my_addr=((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr;
283 		ntp->status = NOMASTER;
284 
285 		if ((ifa->ifa_flags & IFF_UP) == 0)
286 			continue;
287 		if ((ifa->ifa_flags & IFF_BROADCAST) == 0 &&
288 		    (ifa->ifa_flags & IFF_POINTOPOINT) == 0) {
289 			continue;
290 		}
291 
292 		ntp->mask = ((struct sockaddr_in *)(void *)
293 		    ifa->ifa_netmask)->sin_addr.s_addr;
294 
295 		if (ifa->ifa_flags & IFF_BROADCAST) {
296 			ntp->dest_addr = *(struct sockaddr_in *)(void *)ifa->ifa_broadaddr;
297 			/* What if the broadcast address is all ones?
298 			 * So we cannot just mask ntp->dest_addr.  */
299 			ntp->net = ntp->my_addr;
300 			ntp->net.s_addr &= ntp->mask;
301 		} else {
302 			ntp->dest_addr = *(struct sockaddr_in *)(void *)ifa->ifa_dstaddr;
303 			ntp->net = ntp->dest_addr.sin_addr;
304 		}
305 
306 		ntp->dest_addr.sin_port = port;
307 
308 		for (nt = nets; nt; nt = nt->next) {
309 			if (ntohl(ntp->net.s_addr) == nt->net)
310 				break;
311 		}
312 		if ((nflag && !nt) || (iflag && nt))
313 			continue;
314 
315 		ntp->next = NULL;
316 		if (nettab == NULL) {
317 			nettab = ntp;
318 		} else {
319 			ntip->next = ntp;
320 		}
321 		ntip = ntp;
322 		ntp = NULL;
323 	}
324 	freeifaddrs(ifap);
325 	if (ntp)
326 		(void) free(ntp);
327 	if (nettab == NULL)
328 		errx(EXIT_FAILURE, "no network usable");
329 
330 
331 	/* microseconds to delay before responding to a broadcast */
332 	delay1 = 1L + arc4random_uniform((100 * 1000L) - 1L);
333 
334 	/* election timer delay in secs. */
335 	delay2 = MINTOUT + arc4random_uniform(MAXTOUT - MINTOUT);
336 
337 	if (!debug) {
338 		daemon(debug, 0);
339 		pidfile(NULL);
340 	}
341 
342 	if (trace)
343 		traceon();
344 	openlog("timed", LOG_PID, LOG_DAEMON);
345 
346 	/*
347 	 * keep returning here
348 	 */
349 	ret = setjmp(jmpenv);
350 	savefromnet = fromnet;
351 	setstatus();
352 
353 	if (Mflag) {
354 		switch (ret) {
355 
356 		case 0:
357 			checkignorednets();
358 			pickslavenet(0);
359 			break;
360 		case 1:
361 			/* Just lost our master */
362 			if (slavenet != 0)
363 				slavenet->status = election(slavenet);
364 			if (!slavenet || slavenet->status == MASTER) {
365 				checkignorednets();
366 				pickslavenet(0);
367 			} else {
368 				makeslave(slavenet);	/* prune extras */
369 			}
370 			break;
371 
372 		case 2:
373 			/* Just been told to quit */
374 			justquit = 1;
375 			pickslavenet(savefromnet);
376 			break;
377 		}
378 
379 		setstatus();
380 		if (!(status & MASTER) && sock_raw != -1) {
381 			/* sock_raw is not being used now */
382 			(void)close(sock_raw);
383 			sock_raw = -1;
384 		}
385 
386 		if (status == MASTER)
387 			master();
388 		else
389 			slave();
390 
391 	} else {
392 		if (sock_raw != -1) {
393 			(void)close(sock_raw);
394 			sock_raw = -1;
395 		}
396 
397 		if (ret) {
398 			/* we just lost our master or were told to quit */
399 			justquit = 1;
400 		}
401 		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
402 			if (ntp->status == MASTER) {
403 				rmnetmachs(ntp);
404 				ntp->status = NOMASTER;
405 			}
406 		}
407 		checkignorednets();
408 		pickslavenet(0);
409 		setstatus();
410 
411 		slave();
412 	}
413 	/* NOTREACHED */
414 	return(0);
415 }
416 
417 
418 /* suppress an upstart, untrustworthy, self-appointed master
419  */
420 void
421 suppress(struct sockaddr_in *addr, char *name, struct netinfo *net)
422 {
423 	struct sockaddr_in tgt;
424 	char tname[MAXHOSTNAMELEN];
425 	struct tsp msg;
426 	static struct timeval wait;
427 
428 	if (trace)
429 		fprintf(fd, "suppress: %s\n", name);
430 	tgt = *addr;
431 	(void)strlcpy(tname, name, sizeof(tname));
432 
433 	while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
434 		if (trace)
435 			fprintf(fd, "suppress:\tdiscarded packet from %s\n",
436 				    name);
437 	}
438 
439 	syslog(LOG_NOTICE, "suppressing false master %s", tname);
440 	msg.tsp_type = TSP_QUIT;
441 	set_tsp_name(&msg, hostname);
442 	(void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
443 }
444 
445 void
446 lookformaster(struct netinfo *ntp)
447 {
448 	struct tsp resp, conflict, *answer;
449 	struct timeval ntime;
450 	char mastername[MAXHOSTNAMELEN];
451 	struct sockaddr_in masteraddr;
452 
453 	get_goodgroup(0);
454 	ntp->status = SLAVE;
455 
456 	/* look for master */
457 	resp.tsp_type = TSP_MASTERREQ;
458 	set_tsp_name(&resp, hostname);
459 	answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
460 			 TSP_MASTERACK, ntp, 0);
461 	if (answer != 0 && !good_host_name(answer->tsp_name)) {
462 		suppress(&from, answer->tsp_name, ntp);
463 		ntp->status = NOMASTER;
464 		answer = 0;
465 	}
466 	if (answer == 0) {
467 		/*
468 		 * Various conditions can cause conflict: races between
469 		 * two just started timedaemons when no master is
470 		 * present, or timedaemons started during an election.
471 		 * A conservative approach is taken.  Give up and become a
472 		 * slave, postponing election of a master until first
473 		 * timer expires.
474 		 */
475 		ntime.tv_sec = ntime.tv_usec = 0;
476 		answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
477 		if (answer != 0) {
478 			if (!good_host_name(answer->tsp_name)) {
479 				suppress(&from, answer->tsp_name, ntp);
480 				ntp->status = NOMASTER;
481 			}
482 			return;
483 		}
484 
485 		ntime.tv_sec = ntime.tv_usec = 0;
486 		answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
487 		if (answer != 0) {
488 			if (!good_host_name(answer->tsp_name)) {
489 				suppress(&from, answer->tsp_name, ntp);
490 				ntp->status = NOMASTER;
491 			}
492 			return;
493 		}
494 
495 		ntime.tv_sec = ntime.tv_usec = 0;
496 		answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
497 		if (answer != 0) {
498 			if (!good_host_name(answer->tsp_name)) {
499 				suppress(&from, answer->tsp_name, ntp);
500 				ntp->status = NOMASTER;
501 			}
502 			return;
503 		}
504 
505 		if (Mflag)
506 			ntp->status = MASTER;
507 		else
508 			ntp->status = NOMASTER;
509 		return;
510 	}
511 
512 	ntp->status = SLAVE;
513 	get_tsp_name(answer, mastername, sizeof(mastername));
514 	masteraddr = from;
515 
516 	/*
517 	 * If network has been partitioned, there might be other
518 	 * masters; tell the one we have just acknowledged that
519 	 * it has to gain control over the others.
520 	 */
521 	ntime.tv_sec = 0;
522 	ntime.tv_usec = 300000;
523 	answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
524 	/*
525 	 * checking also not to send CONFLICT to ack'ed master
526 	 * due to duplicated MASTERACKs
527 	 */
528 	if (answer != NULL &&
529 	    strcmp(answer->tsp_name, mastername) != 0) {
530 		conflict.tsp_type = TSP_CONFLICT;
531 		set_tsp_name(&conflict, hostname);
532 		if (!acksend(&conflict, &masteraddr, mastername,
533 			     TSP_ACK, 0, 0)) {
534 			syslog(LOG_ERR,
535 			       "error on sending TSP_CONFLICT");
536 		}
537 	}
538 }
539 
540 /*
541  * based on the current network configuration, set the status, and count
542  * networks;
543  */
544 void
545 setstatus(void)
546 {
547 	struct netinfo *ntp;
548 
549 	status = 0;
550 	nmasternets = nslavenets = nnets = nignorednets = 0;
551 	if (trace)
552 		fprintf(fd, "Net status:\n");
553 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
554 		switch ((int)ntp->status) {
555 		case MASTER:
556 			nmasternets++;
557 			break;
558 		case SLAVE:
559 			nslavenets++;
560 			break;
561 		case NOMASTER:
562 		case IGNORE:
563 			nignorednets++;
564 			break;
565 		}
566 		if (trace) {
567 			fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
568 			switch ((int)ntp->status) {
569 			case NOMASTER:
570 				fprintf(fd, "NOMASTER\n");
571 				break;
572 			case MASTER:
573 				fprintf(fd, "MASTER\n");
574 				break;
575 			case SLAVE:
576 				fprintf(fd, "SLAVE\n");
577 				break;
578 			case IGNORE:
579 				fprintf(fd, "IGNORE\n");
580 				break;
581 			default:
582 				fprintf(fd, "invalid state %d\n",
583 					(int)ntp->status);
584 				break;
585 			}
586 		}
587 		nnets++;
588 		status |= ntp->status;
589 	}
590 	status &= ~IGNORE;
591 	if (trace)
592 		fprintf(fd,
593 		    "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
594 		    nnets, nmasternets, nslavenets, nignorednets, (long)delay2);
595 }
596 
597 void
598 makeslave(struct netinfo *net)
599 {
600 	struct netinfo *ntp;
601 
602 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
603 		if (ntp->status == SLAVE && ntp != net)
604 			ntp->status = IGNORE;
605 	}
606 	slavenet = net;
607 }
608 
609 /*
610  * Try to become master over ignored nets..
611  */
612 static void
613 checkignorednets(void)
614 {
615 	struct netinfo *ntp;
616 
617 	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
618 		if (!Mflag && ntp->status == SLAVE)
619 			break;
620 
621 		if (ntp->status == IGNORE || ntp->status == NOMASTER) {
622 			lookformaster(ntp);
623 			if (!Mflag && ntp->status == SLAVE)
624 				break;
625 		}
626 	}
627 }
628 
629 /*
630  * choose a good network on which to be a slave
631  *	The ignored networks must have already been checked.
632  *	Take a hint about for a good network.
633  */
634 static void
635 pickslavenet(struct netinfo *ntp)
636 {
637 	if (slavenet != 0 && slavenet->status == SLAVE) {
638 		makeslave(slavenet);		/* prune extras */
639 		return;
640 	}
641 
642 	if (ntp == 0 || ntp->status != SLAVE) {
643 		for (ntp = nettab; ntp != 0; ntp = ntp->next) {
644 			if (ntp->status == SLAVE)
645 				break;
646 		}
647 	}
648 	makeslave(ntp);
649 }
650 
651 char *
652 date(void)
653 {
654 	struct	timeval tv;
655 	time_t t;
656 
657 	(void)gettimeofday(&tv, (struct timezone *)0);
658 	t = tv.tv_sec;
659 	return (ctime(&t));
660 }
661 
662 void
663 addnetname(char *name)
664 {
665 	struct nets **netlist = &nets;
666 
667 	while (*netlist)
668 		netlist = &((*netlist)->next);
669 	*netlist = calloc(1, sizeof **netlist);
670 	if (*netlist == NULL)
671 		err(EXIT_FAILURE, "malloc failed");
672 	(*netlist)->name = name;
673 }
674 
675 /* note a host as trustworthy */
676 static void
677 add_good_host(const char* name,
678 	      char perm)		/* 1=not part of the netgroup */
679 {
680 	struct goodhost *ghp;
681 	struct hostent *hentp;
682 
683 	ghp = calloc(1, sizeof(*ghp));
684 	if (!ghp) {
685 		syslog(LOG_ERR, "malloc failed");
686 		exit(EXIT_FAILURE);
687 	}
688 
689 	(void)strncpy(&ghp->name[0], name, sizeof(ghp->name) - 1);
690 	ghp->name[sizeof(ghp->name) - 1] = 0;
691 	ghp->next = goodhosts;
692 	ghp->perm = perm;
693 	goodhosts = ghp;
694 
695 	hentp = gethostbyname(name);
696 	if (NULL == hentp && perm)
697 		warnx("unknown host %s", name);
698 }
699 
700 
701 /* update our image of the net-group of trustworthy hosts
702  */
703 void
704 get_goodgroup(int force)
705 {
706 # define NG_DELAY (30*60*CLK_TCK)	/* 30 minutes */
707 	static unsigned long last_update;
708 	static int firsttime;
709 	unsigned long new_update;
710 	struct goodhost *ghp, **ghpp;
711 #ifdef HAVENIS
712 	struct hosttbl *htp;
713 	const char *mach, *usr, *dom;
714 #endif
715 	struct tms tm;
716 
717 	if (firsttime == 0) {
718 		last_update = -NG_DELAY;
719 		firsttime++;
720 	}
721 
722 	/* if no netgroup, then we are finished */
723 	if (goodgroup == 0 || !Mflag)
724 		return;
725 
726 	/* Do not chatter with the netgroup master too often.
727 	 */
728 	new_update = times(&tm);
729 	if (new_update < last_update + NG_DELAY
730 	    && !force)
731 		return;
732 	last_update = new_update;
733 
734 	/* forget the old temporary entries */
735 	ghpp = &goodhosts;
736 	while (0 != (ghp = *ghpp)) {
737 		if (!ghp->perm) {
738 			*ghpp = ghp->next;
739 			free(ghp);
740 		} else {
741 			ghpp = &ghp->next;
742 		}
743 	}
744 
745 #ifdef HAVENIS
746 	/* quit now if we are not one of the trusted masters
747 	 */
748 	if (!innetgr(goodgroup, &hostname[0], 0,0)) {
749 		if (trace)
750 			(void)fprintf(fd, "get_goodgroup: %s not in %s\n",
751 				      &hostname[0], goodgroup);
752 		return;
753 	}
754 	if (trace)
755 		(void)fprintf(fd, "get_goodgroup: %s in %s\n",
756 				  &hostname[0], goodgroup);
757 
758 	/* mark the entire netgroup as trusted */
759 	(void)setnetgrent(goodgroup);
760 	while (getnetgrent(&mach,&usr,&dom)) {
761 		if (0 != mach)
762 			add_good_host(mach,0);
763 	}
764 	(void)endnetgrent();
765 
766 	/* update list of slaves */
767 	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
768 		htp->good = good_host_name(&htp->name[0]);
769 	}
770 #endif /* HAVENIS */
771 }
772 
773 
774 /* see if a machine is trustworthy
775  */
776 int					/* 1=trust hp to change our date */
777 good_host_name(char *name)
778 {
779 	struct goodhost *ghp = goodhosts;
780 	char c;
781 
782 	if (!ghp || !Mflag)		/* trust everyone if no one named */
783 		return 1;
784 
785 	c = *name;
786 	do {
787 		if (c == ghp->name[0]
788 		    && !strcasecmp(name, ghp->name))
789 			return 1;	/* found him, so say so */
790 	} while (0 != (ghp = ghp->next));
791 
792 	if (!strcasecmp(name,hostname))	/* trust ourself */
793 		return 1;
794 
795 	return 0;			/* did not find him */
796 }
797