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