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