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