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