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