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