xref: /netbsd/usr.sbin/timed/timed/master.c (revision c4a72b64)
1 /*	$NetBSD: master.c,v 1.11 2002/08/02 02:13:46 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1985, 1993 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)master.c	8.1 (Berkeley) 6/6/93";
40 #else
41 __RCSID("$NetBSD: master.c,v 1.11 2002/08/02 02:13:46 christos Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include "globals.h"
46 #include <sys/file.h>
47 #include <sys/types.h>
48 #include <sys/times.h>
49 #include <setjmp.h>
50 
51 #include "pathnames.h"
52 
53 extern int measure_delta;
54 extern jmp_buf jmpenv;
55 extern int Mflag;
56 extern int justquit;
57 
58 static int dictate;
59 static int slvcount;			/* slaves listening to our clock */
60 
61 static void mchgdate(struct tsp*);
62 
63 extern	void	logwtmp(char *, char *, char *);
64 
65 
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  */
76 int
77 master(void)
78 {
79 	struct hosttbl *htp;
80 	long pollingtime;
81 #define POLLRATE 4
82 	int polls;
83 	struct timeval wait, ntime;
84 	time_t tmpt;
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  */
109 loop:
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 			tmpt = msg->tsp_time.tv_sec;
179 			(void)strcpy(newdate, ctime(&tmpt));
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 			tmpt = msg->tsp_time.tv_sec;
197 			(void)strcpy(newdate, ctime(&tmpt));
198 
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 }
348 
349 
350 /*
351  * change the system date on the master
352  */
353 static void
354 mchgdate(struct tsp *msg)
355 {
356 	char tname[MAXHOSTNAMELEN];
357 	char olddate[32];
358 	struct timeval otime, ntime, tmptv;
359 
360 	(void)strcpy(tname, msg->tsp_name);
361 
362 	xmit(TSP_DATEACK, msg->tsp_seq, &from);
363 
364 	(void)strcpy(olddate, date());
365 
366 	/* adjust time for residence on the queue */
367 	(void)gettimeofday(&otime, 0);
368 	adj_msg_time(msg,&otime);
369 
370 	timersub(&msg->tsp_time, &otime, &ntime);
371 	if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
372 		/*
373 		 * do not change the clock if we can adjust it
374 		 */
375 		dictate = 3;
376 		synch(tvtomsround(ntime));
377 	} else {
378 		logwtmp("|", "date", "");
379 		tmptv.tv_sec = msg->tsp_time.tv_sec;
380 		tmptv.tv_usec = msg->tsp_time.tv_usec;
381 		(void)settimeofday(&tmptv, 0);
382 		logwtmp("}", "date", "");
383 		spreadtime();
384 	}
385 
386 	syslog(LOG_NOTICE, "date changed by %s from %s",
387 	       tname, olddate);
388 }
389 
390 
391 /*
392  * synchronize all of the slaves
393  */
394 void
395 synch(long mydelta)
396 {
397 	struct hosttbl *htp;
398 	int measure_status;
399 	struct timeval check, stop, wait;
400 
401 	if (slvcount > 0) {
402 		if (trace)
403 			fprintf(fd, "measurements starting at %s\n", date());
404 		(void)gettimeofday(&check, 0);
405 		for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
406 			if (htp->noanswer != 0) {
407 				measure_status = measure(500, 100,
408 							 htp->name,
409 							 &htp->addr,0);
410 			} else {
411 				measure_status = measure(3000, 100,
412 							 htp->name,
413 							 &htp->addr,0);
414 			}
415 			if (measure_status != GOOD) {
416 				/* The slave did not respond.  We have
417 				 * just wasted lots of time on it.
418 				 */
419 				htp->delta = HOSTDOWN;
420 				if (++htp->noanswer >= LOSTHOST) {
421 					if (trace) {
422 						fprintf(fd,
423 					"purging %s for not answering ICMP\n",
424 							htp->name);
425 						(void)fflush(fd);
426 					}
427 					htp = remmach(htp);
428 				}
429 			} else {
430 				htp->delta = measure_delta;
431 			}
432 			(void)gettimeofday(&stop, 0);
433 			timersub(&stop, &check, &stop);
434 			if (stop.tv_sec >= 1) {
435 				if (trace)
436 					(void)fflush(fd);
437 				/*
438 				 * ack messages periodically
439 				 */
440 				wait.tv_sec = 0;
441 				wait.tv_usec = 0;
442 				if (0 != readmsg(TSP_TRACEON,ANYADDR,
443 						 &wait,0))
444 					traceon();
445 				(void)gettimeofday(&check, 0);
446 			}
447 		}
448 		if (trace)
449 			fprintf(fd, "measurements finished at %s\n", date());
450 	}
451 	if (!(status & SLAVE)) {
452 		if (!dictate) {
453 			mydelta = networkdelta();
454 		} else {
455 			dictate--;
456 		}
457 	}
458 	if (trace && (mydelta != 0 || (status & SLAVE)))
459 		fprintf(fd,"local correction of %ld ms.\n", mydelta);
460 	correct(mydelta);
461 }
462 
463 /*
464  * sends the time to each slave after the master
465  * has received the command to set the network time
466  */
467 void
468 spreadtime(void)
469 {
470 	struct hosttbl *htp;
471 	struct tsp to;
472 	struct tsp *answer;
473 	struct timeval tmptv;
474 
475 /* Do not listen to the consensus after forcing the time.  This is because
476  *	the consensus takes a while to reach the time we are dictating.
477  */
478 	dictate = 2;
479 	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
480 		to.tsp_type = TSP_SETTIME;
481 		(void)strcpy(to.tsp_name, hostname);
482 		(void)gettimeofday(&tmptv, 0);
483 		to.tsp_time.tv_sec = tmptv.tv_sec;
484 		to.tsp_time.tv_usec = tmptv.tv_usec;
485 		answer = acksend(&to, &htp->addr, htp->name,
486 				 TSP_ACK, 0, htp->noanswer);
487 		if (answer == 0) {
488 			/* We client does not respond, then we have
489 			 * just wasted lots of time on it.
490 			 */
491 			syslog(LOG_WARNING,
492 			       "no reply to SETTIME from %s", htp->name);
493 			if (++htp->noanswer >= LOSTHOST) {
494 				if (trace) {
495 					fprintf(fd,
496 					     "purging %s for not answering",
497 						htp->name);
498 					(void)fflush(fd);
499 				}
500 				htp = remmach(htp);
501 			}
502 		}
503 	}
504 }
505 
506 
507 void
508 prthp(clock_t delta)
509 {
510 	static time_t next_time;
511 	time_t this_time;
512 	struct tms tm;
513 	struct hosttbl *htp;
514 	int length, l;
515 	int i;
516 
517 	if (!fd)			/* quit if tracing already off */
518 		return;
519 
520 	this_time = times(&tm);
521 	if (this_time + delta < next_time)
522 		return;
523 	next_time = this_time + CLK_TCK;
524 
525 	fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
526 	htp = self.l_fwd;
527 	length = 1;
528 	for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
529 		l = strlen(htp->name) + 1;
530 		if (length+l >= 80) {
531 			fprintf(fd, "\n");
532 			length = 0;
533 		}
534 		length += l;
535 		fprintf(fd, " %s", htp->name);
536 	}
537 	fprintf(fd, "\n");
538 }
539 
540 
541 static struct hosttbl *newhost_hash;
542 static struct hosttbl *lasthfree = &hosttbl[0];
543 
544 
545 struct hosttbl *			/* answer or 0 */
546 findhost(char *name)
547 {
548 	int i, j;
549 	struct hosttbl *htp;
550 	char *p;
551 
552 	j= 0;
553 	for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
554 		j = (j << 2) ^ *p;
555 	newhost_hash = &hosttbl[j % NHOSTS];
556 
557 	htp = newhost_hash;
558 	if (htp->name[0] == '\0')
559 		return(0);
560 	do {
561 		if (!strcmp(name, htp->name))
562 			return(htp);
563 		htp = htp->h_fwd;
564 	} while (htp != newhost_hash);
565 	return(0);
566 }
567 
568 /*
569  * add a host to the list of controlled machines if not already there
570  */
571 struct hosttbl *
572 addmach(char *name, struct sockaddr_in *addr, struct netinfo *ntp)
573 {
574 	struct hosttbl *ret, *p, *b, *f;
575 
576 	ret = findhost(name);
577 	if (ret == 0) {
578 		if (slvcount >= NHOSTS) {
579 			if (trace) {
580 				fprintf(fd, "no more slots in host table\n");
581 				prthp(CLK_TCK);
582 			}
583 			syslog(LOG_ERR, "no more slots in host table");
584 			Mflag = 0;
585 			longjmp(jmpenv, 2); /* give up and be a slave */
586 		}
587 
588 		/* if our home hash slot is occupied, find a free entry
589 		 * in the hash table
590 		 */
591 		if (newhost_hash->name[0] != '\0') {
592 			do {
593 				ret = lasthfree;
594 				if (++lasthfree > &hosttbl[NHOSTS])
595 					lasthfree = &hosttbl[1];
596 			} while (ret->name[0] != '\0');
597 
598 			if (!newhost_hash->head) {
599 				/* Move an interloper using our home.  Use
600 				 * scratch pointers in case the new head is
601 				 * pointing to itself.
602 				 */
603 				f = newhost_hash->h_fwd;
604 				b = newhost_hash->h_bak;
605 				f->h_bak = ret;
606 				b->h_fwd = ret;
607 				f = newhost_hash->l_fwd;
608 				b = newhost_hash->l_bak;
609 				f->l_bak = ret;
610 				b->l_fwd = ret;
611 				bcopy(newhost_hash,ret,sizeof(*ret));
612 				ret = newhost_hash;
613 				ret->head = 1;
614 				ret->h_fwd = ret;
615 				ret->h_bak = ret;
616 			} else {
617 				/* link to an existing chain in our home
618 				 */
619 				ret->head = 0;
620 				p = newhost_hash->h_bak;
621 				ret->h_fwd = newhost_hash;
622 				ret->h_bak = p;
623 				p->h_fwd = ret;
624 				newhost_hash->h_bak = ret;
625 			}
626 		} else {
627 			ret = newhost_hash;
628 			ret->head = 1;
629 			ret->h_fwd = ret;
630 			ret->h_bak = ret;
631 		}
632 		ret->addr = *addr;
633 		ret->ntp = ntp;
634 		(void)strncpy(ret->name, name, sizeof(ret->name));
635 		ret->good = good_host_name(name);
636 		ret->l_fwd = &self;
637 		ret->l_bak = self.l_bak;
638 		self.l_bak->l_fwd = ret;
639 		self.l_bak = ret;
640 		slvcount++;
641 
642 		ret->noanswer = 0;
643 		ret->need_set = 1;
644 
645 	} else {
646 		ret->noanswer = (ret->noanswer != 0);
647 	}
648 
649 	/* need to clear sequence number anyhow */
650 	ret->seq = 0;
651 	return(ret);
652 }
653 
654 /*
655  * remove the machine with the given index in the host table.
656  */
657 struct hosttbl *
658 remmach(struct hosttbl *htp)
659 {
660 	struct hosttbl *lprv, *hnxt, *f, *b;
661 
662 	if (trace)
663 		fprintf(fd, "remove %s\n", htp->name);
664 
665 	/* get out of the lists */
666 	htp->l_fwd->l_bak = lprv = htp->l_bak;
667 	htp->l_bak->l_fwd = htp->l_fwd;
668 	htp->h_fwd->h_bak = htp->h_bak;
669 	htp->h_bak->h_fwd = hnxt = htp->h_fwd;
670 
671 	/* If we are in the home slot, pull up the chain */
672 	if (htp->head && hnxt != htp) {
673 		if (lprv == hnxt)
674 			lprv = htp;
675 
676 		/* Use scratch pointers in case the new head is pointing to
677 		 * itself.
678 		 */
679 		f = hnxt->h_fwd;
680 		b = hnxt->h_bak;
681 		f->h_bak = htp;
682 		b->h_fwd = htp;
683 		f = hnxt->l_fwd;
684 		b = hnxt->l_bak;
685 		f->l_bak = htp;
686 		b->l_fwd = htp;
687 		hnxt->head = 1;
688 		bcopy(hnxt, htp, sizeof(*htp));
689 		lasthfree = hnxt;
690 	} else {
691 		lasthfree = htp;
692 	}
693 
694 	lasthfree->name[0] = '\0';
695 	lasthfree->h_fwd = 0;
696 	lasthfree->l_fwd = 0;
697 	slvcount--;
698 
699 	return lprv;
700 }
701 
702 
703 /*
704  * Remove all the machines from the host table that exist on the given
705  * network.  This is called when a master transitions to a slave on a
706  * given network.
707  */
708 void
709 rmnetmachs(struct netinfo *ntp)
710 {
711 	struct hosttbl *htp;
712 
713 	if (trace)
714 		prthp(CLK_TCK);
715 	for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
716 		if (ntp == htp->ntp)
717 			htp = remmach(htp);
718 	}
719 	if (trace)
720 		prthp(CLK_TCK);
721 }
722 
723 
724 void
725 masterup(struct netinfo *net)
726 {
727 	xmit(TSP_MASTERUP, 0, &net->dest_addr);
728 
729 	/*
730 	 * Do not tell new slaves our time for a while.  This ensures
731 	 * we do not tell them to start using our time, before we have
732 	 * found a good master.
733 	 */
734 	(void)gettimeofday(&net->slvwait, 0);
735 }
736 
737 void
738 newslave(struct tsp *msg)
739 {
740 	struct hosttbl *htp;
741 	struct tsp *answer, to;
742 	struct timeval now, tmptv;
743 
744 	if (!fromnet || fromnet->status != MASTER)
745 		return;
746 
747 	htp = addmach(msg->tsp_name, &from,fromnet);
748 	htp->seq = msg->tsp_seq;
749 	if (trace)
750 		prthp(0);
751 
752 	/*
753 	 * If we are stable, send our time to the slave.
754 	 * Do not go crazy if the date has been changed.
755 	 */
756 	(void)gettimeofday(&now, 0);
757 	if (now.tv_sec >= fromnet->slvwait.tv_sec+3
758 	    || now.tv_sec < fromnet->slvwait.tv_sec) {
759 		to.tsp_type = TSP_SETTIME;
760 		(void)strcpy(to.tsp_name, hostname);
761 		(void)gettimeofday(&tmptv, 0);
762 		to.tsp_time.tv_sec = tmptv.tv_sec;
763 		to.tsp_time.tv_usec = tmptv.tv_usec;
764 		answer = acksend(&to, &htp->addr,
765 				 htp->name, TSP_ACK,
766 				 0, htp->noanswer);
767 		if (answer) {
768 			htp->need_set = 0;
769 		} else {
770 			syslog(LOG_WARNING,
771 			       "no reply to initial SETTIME from %s",
772 			       htp->name);
773 			htp->noanswer = LOSTHOST;
774 		}
775 	}
776 }
777 
778 
779 /*
780  * react to a TSP_QUIT:
781  */
782 void
783 doquit(struct tsp *msg)
784 {
785 	if (fromnet->status == MASTER) {
786 		if (!good_host_name(msg->tsp_name)) {
787 			if (fromnet->quit_count <= 0) {
788 				syslog(LOG_NOTICE,"untrusted %s told us QUIT",
789 				       msg->tsp_name);
790 				suppress(&from, msg->tsp_name, fromnet);
791 				fromnet->quit_count = 1;
792 				return;
793 			}
794 			syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
795 			       msg->tsp_name);
796 			fromnet->quit_count = 2;
797 			fromnet->status = NOMASTER;
798 		} else {
799 			fromnet->status = SLAVE;
800 		}
801 		rmnetmachs(fromnet);
802 		longjmp(jmpenv, 2);		/* give up and be a slave */
803 
804 	} else {
805 		if (!good_host_name(msg->tsp_name)) {
806 			syslog(LOG_NOTICE, "untrusted %s told us QUIT",
807 			       msg->tsp_name);
808 			fromnet->quit_count = 2;
809 		}
810 	}
811 }
812 
813 void
814 traceon(void)
815 {
816 	if (!fd) {
817 		fd = fopen(_PATH_TIMEDLOG, "w");
818 		if (!fd) {
819 			trace = 0;
820 			return;
821 		}
822 		fprintf(fd,"Tracing started at %s\n", date());
823 	}
824 	trace = 1;
825 	get_goodgroup(1);
826 	setstatus();
827 	prthp(CLK_TCK);
828 }
829 
830 
831 void
832 traceoff(char *msg)
833 {
834 	get_goodgroup(1);
835 	setstatus();
836 	prthp(CLK_TCK);
837 	if (trace) {
838 		fprintf(fd, msg, date());
839 		(void)fclose(fd);
840 		fd = 0;
841 	}
842 #ifdef GPROF
843 	moncontrol(0);
844 	_mcleanup();
845 	moncontrol(1);
846 #endif
847 	trace = OFF;
848 }
849 
850