master.c revision 30830
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
35#if 0
36static char sccsid[] = "@(#)master.c	8.1 (Berkeley) 6/6/93";
37#endif
38static const char rcsid[] =
39	"$Id: master.c,v 1.3 1997/10/22 06:19:48 charnier Exp $";
40#endif /* not lint */
41
42#include "globals.h"
43#include <sys/file.h>
44#include <sys/types.h>
45#include <sys/times.h>
46#include <setjmp.h>
47#ifdef sgi
48#include <sys/schedctl.h>
49#endif /* sgi */
50#include <utmp.h>
51#include "pathnames.h"
52
53extern int measure_delta;
54extern jmp_buf jmpenv;
55extern int Mflag;
56extern int justquit;
57
58static int dictate;
59static int slvcount;			/* slaves listening to our clock */
60
61static void mchgdate __P((struct tsp *));
62
63#ifdef sgi
64extern void logwtmp __P((struct timeval *, struct timeval *));
65#else
66extern void logwtmp __P((char *, char *, char *));
67#endif /* sgi */
68
69/*
70 * The main function of `master' is to periodically compute the differences
71 * (deltas) between its clock and the clocks of the slaves, to compute the
72 * network average delta, and to send to the slaves the differences between
73 * their individual deltas and the network delta.
74 * While waiting, it receives messages from the slaves (i.e. requests for
75 * master's name, remote requests to set the network time, ...), and
76 * takes the appropriate action.
77 */
78int
79master()
80{
81	struct hosttbl *htp;
82	long pollingtime;
83#define POLLRATE 4
84	int polls;
85	struct timeval wait, ntime;
86	struct tsp *msg, *answer, to;
87	char newdate[32];
88	struct sockaddr_in taddr;
89	char tname[MAXHOSTNAMELEN];
90	struct netinfo *ntp;
91	int i;
92
93	syslog(LOG_NOTICE, "This machine is master");
94	if (trace)
95		fprintf(fd, "This machine is master\n");
96	for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
97		if (ntp->status == MASTER)
98			masterup(ntp);
99	}
100	(void)gettimeofday(&ntime, 0);
101	pollingtime = ntime.tv_sec+3;
102	if (justquit)
103		polls = 0;
104	else
105		polls = POLLRATE-1;
106
107/* Process all outstanding messages before spending the long time necessary
108 *	to update all timers.
109 */
110loop:
111	(void)gettimeofday(&ntime, 0);
112	wait.tv_sec = pollingtime - ntime.tv_sec;
113	if (wait.tv_sec < 0)
114		wait.tv_sec = 0;
115	wait.tv_usec = 0;
116	msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
117	if (!msg) {
118		(void)gettimeofday(&ntime, 0);
119		if (ntime.tv_sec >= pollingtime) {
120			pollingtime = ntime.tv_sec + SAMPLEINTVL;
121			get_goodgroup(0);
122
123/* If a bogus master told us to quit, we can have decided to ignore a
124 * network.  Therefore, periodically try to take over everything.
125 */
126			polls = (polls + 1) % POLLRATE;
127			if (0 == polls && nignorednets > 0) {
128				trace_msg("Looking for nets to re-master\n");
129				for (ntp = nettab; ntp; ntp = ntp->next) {
130					if (ntp->status == IGNORE
131					    || ntp->status == NOMASTER) {
132						lookformaster(ntp);
133						if (ntp->status == MASTER) {
134							masterup(ntp);
135							polls = POLLRATE-1;
136						}
137					}
138					if (ntp->status == MASTER
139					    && --ntp->quit_count < 0)
140						ntp->quit_count = 0;
141				}
142				if (polls != 0)
143					setstatus();
144			}
145
146			synch(0L);
147
148			for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
149				to.tsp_type = TSP_LOOP;
150				to.tsp_vers = TSPVERSION;
151				to.tsp_seq = sequence++;
152				to.tsp_hopcnt = MAX_HOPCNT;
153				(void)strcpy(to.tsp_name, hostname);
154				bytenetorder(&to);
155				if (sendto(sock, (char *)&to,
156					   sizeof(struct tsp), 0,
157					   (struct sockaddr*)&ntp->dest_addr,
158					   sizeof(ntp->dest_addr)) < 0) {
159				   trace_sendto_err(ntp->dest_addr.sin_addr);
160				}
161			}
162		}
163
164
165	} else {
166		switch (msg->tsp_type) {
167
168		case TSP_MASTERREQ:
169			break;
170
171		case TSP_SLAVEUP:
172			newslave(msg);
173			break;
174
175		case TSP_SETDATE:
176			/*
177			 * XXX check to see it is from ourself
178			 */
179#ifdef sgi
180			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
181#else
182			(void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
183#endif /* sgi */
184			if (!good_host_name(msg->tsp_name)) {
185				syslog(LOG_NOTICE,
186				       "attempted date change by %s to %s",
187				       msg->tsp_name, newdate);
188				spreadtime();
189				break;
190			}
191
192			mchgdate(msg);
193			(void)gettimeofday(&ntime, 0);
194			pollingtime = ntime.tv_sec + SAMPLEINTVL;
195			break;
196
197		case TSP_SETDATEREQ:
198			if (!fromnet || fromnet->status != MASTER)
199				break;
200#ifdef sgi
201			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
202#else
203			(void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
204#endif /* sgi */
205			htp = findhost(msg->tsp_name);
206			if (htp == 0) {
207				syslog(LOG_ERR,
208				       "attempted SET DATEREQ by uncontrolled %s to %s",
209				       msg->tsp_name, newdate);
210				break;
211			}
212			if (htp->seq == msg->tsp_seq)
213				break;
214			htp->seq = msg->tsp_seq;
215			if (!htp->good) {
216				syslog(LOG_NOTICE,
217				"attempted SET DATEREQ by untrusted %s to %s",
218				       msg->tsp_name, newdate);
219				spreadtime();
220				break;
221			}
222
223			mchgdate(msg);
224			(void)gettimeofday(&ntime, 0);
225			pollingtime = ntime.tv_sec + SAMPLEINTVL;
226			break;
227
228		case TSP_MSITE:
229			xmit(TSP_ACK, msg->tsp_seq, &from);
230			break;
231
232		case TSP_MSITEREQ:
233			break;
234
235		case TSP_TRACEON:
236			traceon();
237			break;
238
239		case TSP_TRACEOFF:
240			traceoff("Tracing ended at %s\n");
241			break;
242
243		case TSP_ELECTION:
244			if (!fromnet)
245				break;
246			if (fromnet->status == MASTER) {
247				pollingtime = 0;
248				(void)addmach(msg->tsp_name, &from,fromnet);
249			}
250			taddr = from;
251			(void)strcpy(tname, msg->tsp_name);
252			to.tsp_type = TSP_QUIT;
253			(void)strcpy(to.tsp_name, hostname);
254			answer = acksend(&to, &taddr, tname,
255					 TSP_ACK, 0, 1);
256			if (answer == NULL) {
257				syslog(LOG_ERR, "election error by %s",
258				       tname);
259			}
260			break;
261
262		case TSP_CONFLICT:
263			/*
264			 * After a network partition, there can be
265			 * more than one master: the first slave to
266			 * come up will notify here the situation.
267			 */
268			if (!fromnet || fromnet->status != MASTER)
269				break;
270			(void)strcpy(to.tsp_name, hostname);
271
272			/* The other master often gets into the same state,
273			 * with boring results if we stay at it forever.
274			 */
275			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
276			for (i = 0; i < 3; i++) {
277				to.tsp_type = TSP_RESOLVE;
278				(void)strcpy(to.tsp_name, hostname);
279				answer = acksend(&to, &ntp->dest_addr,
280						 ANYADDR, TSP_MASTERACK,
281						 ntp, 0);
282				if (!answer)
283					break;
284				htp = addmach(answer->tsp_name,&from,ntp);
285				to.tsp_type = TSP_QUIT;
286				msg = acksend(&to, &htp->addr, htp->name,
287					      TSP_ACK, 0, htp->noanswer);
288				if (msg == NULL) {
289					syslog(LOG_ERR,
290				    "no response from %s to CONFLICT-QUIT",
291					       htp->name);
292				}
293			}
294			masterup(ntp);
295			pollingtime = 0;
296			break;
297
298		case TSP_RESOLVE:
299			if (!fromnet || fromnet->status != MASTER)
300				break;
301			/*
302			 * do not want to call synch() while waiting
303			 * to be killed!
304			 */
305			(void)gettimeofday(&ntime, (struct timezone *)0);
306			pollingtime = ntime.tv_sec + SAMPLEINTVL;
307			break;
308
309		case TSP_QUIT:
310			doquit(msg);		/* become a slave */
311			break;
312
313		case TSP_LOOP:
314			if (!fromnet || fromnet->status != MASTER
315			    || !strcmp(msg->tsp_name, hostname))
316				break;
317			/*
318			 * We should not have received this from a net
319			 * we are master on.  There must be two masters.
320			 */
321			htp = addmach(msg->tsp_name, &from,fromnet);
322			to.tsp_type = TSP_QUIT;
323			(void)strcpy(to.tsp_name, hostname);
324			answer = acksend(&to, &htp->addr, htp->name,
325					 TSP_ACK, 0, 1);
326			if (!answer) {
327				syslog(LOG_WARNING,
328				"loop breakage: no reply from %s=%s to QUIT",
329				    htp->name, inet_ntoa(htp->addr.sin_addr));
330				(void)remmach(htp);
331			}
332
333		case TSP_TEST:
334			if (trace) {
335				fprintf(fd,
336		"\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
337		nnets, nmasternets, nslavenets, nignorednets);
338				setstatus();
339			}
340			pollingtime = 0;
341			polls = POLLRATE-1;
342			break;
343
344		default:
345			if (trace) {
346				fprintf(fd, "garbage message: ");
347				print(msg, &from);
348			}
349			break;
350		}
351	}
352	goto loop;
353}
354
355
356/*
357 * change the system date on the master
358 */
359static void
360mchgdate(msg)
361	struct tsp *msg;
362{
363	char tname[MAXHOSTNAMELEN];
364	char olddate[32];
365	struct timeval otime, ntime;
366
367	(void)strcpy(tname, msg->tsp_name);
368
369	xmit(TSP_DATEACK, msg->tsp_seq, &from);
370
371	(void)strcpy(olddate, date());
372
373	/* adjust time for residence on the queue */
374	(void)gettimeofday(&otime, 0);
375	adj_msg_time(msg,&otime);
376
377	timevalsub(&ntime, &msg->tsp_time, &otime);
378	if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
379		/*
380		 * do not change the clock if we can adjust it
381		 */
382		dictate = 3;
383		synch(tvtomsround(ntime));
384	} else {
385#ifdef sgi
386		if (0 > settimeofday(&msg->tsp_time, 0)) {
387			syslog(LOG_ERR, "settimeofday(): %m");
388		}
389		logwtmp(&otime, &msg->tsp_time);
390#else
391		logwtmp("|", "date", "");
392		(void)settimeofday(&msg->tsp_time, 0);
393		logwtmp("{", "date", "");
394#endif /* sgi */
395		spreadtime();
396	}
397
398	syslog(LOG_NOTICE, "date changed by %s from %s",
399	       tname, olddate);
400}
401
402
403/*
404 * synchronize all of the slaves
405 */
406void
407synch(mydelta)
408	long mydelta;
409{
410	struct hosttbl *htp;
411	int measure_status;
412	struct timeval check, stop, wait;
413#ifdef sgi
414	int pri;
415#endif /* sgi */
416
417	if (slvcount > 0) {
418		if (trace)
419			fprintf(fd, "measurements starting at %s\n", date());
420		(void)gettimeofday(&check, 0);
421#ifdef sgi
422		/* run fast to get good time */
423		pri = schedctl(NDPRI,0,NDPHIMIN);
424		if (pri < 0)
425			syslog(LOG_ERR, "schedctl(): %m");
426#endif /* sgi */
427		for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
428			if (htp->noanswer != 0) {
429				measure_status = measure(500, 100,
430							 htp->name,
431							 &htp->addr,0);
432			} else {
433				measure_status = measure(3000, 100,
434							 htp->name,
435							 &htp->addr,0);
436			}
437			if (measure_status != GOOD) {
438				/* The slave did not respond.  We have
439				 * just wasted lots of time on it.
440				 */
441				htp->delta = HOSTDOWN;
442				if (++htp->noanswer >= LOSTHOST) {
443					if (trace) {
444						fprintf(fd,
445					"purging %s for not answering ICMP\n",
446							htp->name);
447						(void)fflush(fd);
448					}
449					htp = remmach(htp);
450				}
451			} else {
452				htp->delta = measure_delta;
453			}
454			(void)gettimeofday(&stop, 0);
455			timevalsub(&stop, &stop, &check);
456			if (stop.tv_sec >= 1) {
457				if (trace)
458					(void)fflush(fd);
459				/*
460				 * ack messages periodically
461				 */
462				wait.tv_sec = 0;
463				wait.tv_usec = 0;
464				if (0 != readmsg(TSP_TRACEON,ANYADDR,
465						 &wait,0))
466					traceon();
467				(void)gettimeofday(&check, 0);
468			}
469		}
470#ifdef sgi
471		if (pri >= 0)
472			(void)schedctl(NDPRI,0,pri);
473#endif /* sgi */
474		if (trace)
475			fprintf(fd, "measurements finished at %s\n", date());
476	}
477	if (!(status & SLAVE)) {
478		if (!dictate) {
479			mydelta = networkdelta();
480		} else {
481			dictate--;
482		}
483	}
484	if (trace && (mydelta != 0 || (status & SLAVE)))
485		fprintf(fd,"local correction of %ld ms.\n", mydelta);
486	correct(mydelta);
487}
488
489/*
490 * sends the time to each slave after the master
491 * has received the command to set the network time
492 */
493void
494spreadtime()
495{
496	struct hosttbl *htp;
497	struct tsp to;
498	struct tsp *answer;
499
500/* Do not listen to the consensus after forcing the time.  This is because
501 *	the consensus takes a while to reach the time we are dictating.
502 */
503	dictate = 2;
504	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
505		to.tsp_type = TSP_SETTIME;
506		(void)strcpy(to.tsp_name, hostname);
507		(void)gettimeofday(&to.tsp_time, 0);
508		answer = acksend(&to, &htp->addr, htp->name,
509				 TSP_ACK, 0, htp->noanswer);
510		if (answer == 0) {
511			/* We client does not respond, then we have
512			 * just wasted lots of time on it.
513			 */
514			syslog(LOG_WARNING,
515			       "no reply to SETTIME from %s", htp->name);
516			if (++htp->noanswer >= LOSTHOST) {
517				if (trace) {
518					fprintf(fd,
519					     "purging %s for not answering",
520						htp->name);
521					(void)fflush(fd);
522				}
523				htp = remmach(htp);
524			}
525		}
526	}
527}
528
529void
530prthp(delta)
531	clock_t delta;
532{
533	static time_t next_time;
534	time_t this_time;
535	struct tms tm;
536	struct hosttbl *htp;
537	int length, l;
538	int i;
539
540	if (!fd)			/* quit if tracing already off */
541		return;
542
543	this_time = times(&tm);
544	if (this_time + delta < next_time)
545		return;
546	next_time = this_time + CLK_TCK;
547
548	fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
549	htp = self.l_fwd;
550	length = 1;
551	for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
552		l = strlen(htp->name) + 1;
553		if (length+l >= 80) {
554			fprintf(fd, "\n");
555			length = 0;
556		}
557		length += l;
558		fprintf(fd, " %s", htp->name);
559	}
560	fprintf(fd, "\n");
561}
562
563
564static struct hosttbl *newhost_hash;
565static struct hosttbl *lasthfree = &hosttbl[0];
566
567
568struct hosttbl *			/* answer or 0 */
569findhost(name)
570	char *name;
571{
572	int i, j;
573	struct hosttbl *htp;
574	char *p;
575
576	j= 0;
577	for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
578		j = (j << 2) ^ *p;
579	newhost_hash = &hosttbl[j % NHOSTS];
580
581	htp = newhost_hash;
582	if (htp->name[0] == '\0')
583		return(0);
584	do {
585		if (!strcmp(name, htp->name))
586			return(htp);
587		htp = htp->h_fwd;
588	} while (htp != newhost_hash);
589	return(0);
590}
591
592/*
593 * add a host to the list of controlled machines if not already there
594 */
595struct hosttbl *
596addmach(name, addr, ntp)
597	char *name;
598	struct sockaddr_in *addr;
599	struct netinfo *ntp;
600{
601	struct hosttbl *ret, *p, *b, *f;
602
603	ret = findhost(name);
604	if (ret == 0) {
605		if (slvcount >= NHOSTS) {
606			if (trace) {
607				fprintf(fd, "no more slots in host table\n");
608				prthp(CLK_TCK);
609			}
610			syslog(LOG_ERR, "no more slots in host table");
611			Mflag = 0;
612			longjmp(jmpenv, 2); /* give up and be a slave */
613		}
614
615		/* if our home hash slot is occupied, find a free entry
616		 * in the hash table
617		 */
618		if (newhost_hash->name[0] != '\0') {
619			do {
620				ret = lasthfree;
621				if (++lasthfree > &hosttbl[NHOSTS])
622					lasthfree = &hosttbl[1];
623			} while (ret->name[0] != '\0');
624
625			if (!newhost_hash->head) {
626				/* Move an interloper using our home.  Use
627				 * scratch pointers in case the new head is
628				 * pointing to itself.
629				 */
630				f = newhost_hash->h_fwd;
631				b = newhost_hash->h_bak;
632				f->h_bak = ret;
633				b->h_fwd = ret;
634				f = newhost_hash->l_fwd;
635				b = newhost_hash->l_bak;
636				f->l_bak = ret;
637				b->l_fwd = ret;
638				bcopy(newhost_hash,ret,sizeof(*ret));
639				ret = newhost_hash;
640				ret->head = 1;
641				ret->h_fwd = ret;
642				ret->h_bak = ret;
643			} else {
644				/* link to an existing chain in our home
645				 */
646				ret->head = 0;
647				p = newhost_hash->h_bak;
648				ret->h_fwd = newhost_hash;
649				ret->h_bak = p;
650				p->h_fwd = ret;
651				newhost_hash->h_bak = ret;
652			}
653		} else {
654			ret = newhost_hash;
655			ret->head = 1;
656			ret->h_fwd = ret;
657			ret->h_bak = ret;
658		}
659		ret->addr = *addr;
660		ret->ntp = ntp;
661		(void)strncpy(ret->name, name, sizeof(ret->name));
662		ret->good = good_host_name(name);
663		ret->l_fwd = &self;
664		ret->l_bak = self.l_bak;
665		self.l_bak->l_fwd = ret;
666		self.l_bak = ret;
667		slvcount++;
668
669		ret->noanswer = 0;
670		ret->need_set = 1;
671
672	} else {
673		ret->noanswer = (ret->noanswer != 0);
674	}
675
676	/* need to clear sequence number anyhow */
677	ret->seq = 0;
678	return(ret);
679}
680
681/*
682 * remove the machine with the given index in the host table.
683 */
684struct hosttbl *
685remmach(htp)
686	struct hosttbl *htp;
687{
688	struct hosttbl *lprv, *hnxt, *f, *b;
689
690	if (trace)
691		fprintf(fd, "remove %s\n", htp->name);
692
693	/* get out of the lists */
694	htp->l_fwd->l_bak = lprv = htp->l_bak;
695	htp->l_bak->l_fwd = htp->l_fwd;
696	htp->h_fwd->h_bak = htp->h_bak;
697	htp->h_bak->h_fwd = hnxt = htp->h_fwd;
698
699	/* If we are in the home slot, pull up the chain */
700	if (htp->head && hnxt != htp) {
701		if (lprv == hnxt)
702			lprv = htp;
703
704		/* Use scratch pointers in case the new head is pointing to
705		 * itself.
706		 */
707		f = hnxt->h_fwd;
708		b = hnxt->h_bak;
709		f->h_bak = htp;
710		b->h_fwd = htp;
711		f = hnxt->l_fwd;
712		b = hnxt->l_bak;
713		f->l_bak = htp;
714		b->l_fwd = htp;
715		hnxt->head = 1;
716		bcopy(hnxt, htp, sizeof(*htp));
717		lasthfree = hnxt;
718	} else {
719		lasthfree = htp;
720	}
721
722	lasthfree->name[0] = '\0';
723	lasthfree->h_fwd = 0;
724	lasthfree->l_fwd = 0;
725	slvcount--;
726
727	return lprv;
728}
729
730
731/*
732 * Remove all the machines from the host table that exist on the given
733 * network.  This is called when a master transitions to a slave on a
734 * given network.
735 */
736void
737rmnetmachs(ntp)
738	struct netinfo *ntp;
739{
740	struct hosttbl *htp;
741
742	if (trace)
743		prthp(CLK_TCK);
744	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
745		if (ntp == htp->ntp)
746			htp = remmach(htp);
747	}
748	if (trace)
749		prthp(CLK_TCK);
750}
751
752void
753masterup(net)
754	struct netinfo *net;
755{
756	xmit(TSP_MASTERUP, 0, &net->dest_addr);
757
758	/*
759	 * Do not tell new slaves our time for a while.  This ensures
760	 * we do not tell them to start using our time, before we have
761	 * found a good master.
762	 */
763	(void)gettimeofday(&net->slvwait, 0);
764}
765
766void
767newslave(msg)
768	struct tsp *msg;
769{
770	struct hosttbl *htp;
771	struct tsp *answer, to;
772	struct timeval now;
773
774	if (!fromnet || fromnet->status != MASTER)
775		return;
776
777	htp = addmach(msg->tsp_name, &from,fromnet);
778	htp->seq = msg->tsp_seq;
779	if (trace)
780		prthp(0);
781
782	/*
783	 * If we are stable, send our time to the slave.
784	 * Do not go crazy if the date has been changed.
785	 */
786	(void)gettimeofday(&now, 0);
787	if (now.tv_sec >= fromnet->slvwait.tv_sec+3
788	    || now.tv_sec < fromnet->slvwait.tv_sec) {
789		to.tsp_type = TSP_SETTIME;
790		(void)strcpy(to.tsp_name, hostname);
791		(void)gettimeofday(&to.tsp_time, 0);
792		answer = acksend(&to, &htp->addr,
793				 htp->name, TSP_ACK,
794				 0, htp->noanswer);
795		if (answer) {
796			htp->need_set = 0;
797		} else {
798			syslog(LOG_WARNING,
799			       "no reply to initial SETTIME from %s",
800			       htp->name);
801			htp->noanswer = LOSTHOST;
802		}
803	}
804}
805
806
807/*
808 * react to a TSP_QUIT:
809 */
810void
811doquit(msg)
812	struct tsp *msg;
813{
814	if (fromnet->status == MASTER) {
815		if (!good_host_name(msg->tsp_name)) {
816			if (fromnet->quit_count <= 0) {
817				syslog(LOG_NOTICE,"untrusted %s told us QUIT",
818				       msg->tsp_name);
819				suppress(&from, msg->tsp_name, fromnet);
820				fromnet->quit_count = 1;
821				return;
822			}
823			syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
824			       msg->tsp_name);
825			fromnet->quit_count = 2;
826			fromnet->status = NOMASTER;
827		} else {
828			fromnet->status = SLAVE;
829		}
830		rmnetmachs(fromnet);
831		longjmp(jmpenv, 2);		/* give up and be a slave */
832
833	} else {
834		if (!good_host_name(msg->tsp_name)) {
835			syslog(LOG_NOTICE, "untrusted %s told us QUIT",
836			       msg->tsp_name);
837			fromnet->quit_count = 2;
838		}
839	}
840}
841
842void
843traceon()
844{
845	if (!fd) {
846		fd = fopen(_PATH_TIMEDLOG, "w");
847		if (!fd) {
848			trace = 0;
849			return;
850		}
851		fprintf(fd,"Tracing started at %s\n", date());
852	}
853	trace = 1;
854	get_goodgroup(1);
855	setstatus();
856	prthp(CLK_TCK);
857}
858
859
860void
861traceoff(msg)
862	char *msg;
863{
864	get_goodgroup(1);
865	setstatus();
866	prthp(CLK_TCK);
867	if (trace) {
868		fprintf(fd, msg, date());
869		(void)fclose(fd);
870		fd = 0;
871	}
872#ifdef GPROF
873	moncontrol(0);
874	_mcleanup();
875	moncontrol(1);
876#endif
877	trace = OFF;
878}
879
880
881#ifdef sgi
882void
883logwtmp(otime, ntime)
884	struct timeval *otime, *ntime;
885{
886	static struct utmp wtmp[2] = {
887		{"","",OTIME_MSG,0,OLD_TIME,0,0,0},
888		{"","",NTIME_MSG,0,NEW_TIME,0,0,0}
889	};
890	static char *wtmpfile = WTMP_FILE;
891	int f;
892
893	wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000;
894	wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000;
895	if (wtmp[0].ut_time == wtmp[1].ut_time)
896		return;
897
898	setutent();
899	(void)pututline(&wtmp[0]);
900	(void)pututline(&wtmp[1]);
901	endutent();
902	if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
903		(void) write(f, (char *)wtmp, sizeof(wtmp));
904		(void) close(f);
905	}
906}
907#endif /* sgi */
908