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