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