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