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