Home | History | Annotate | Line # | Download | only in timed
      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