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