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