1/*	$NetBSD: master.c,v 1.17 2007/01/25 23:51:11 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
35static char sccsid[] = "@(#)master.c	8.1 (Berkeley) 6/6/93";
36#else
37__RCSID("$NetBSD: master.c,v 1.17 2007/01/25 23:51:11 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
49extern int measure_delta;
50extern jmp_buf jmpenv;
51extern int Mflag;
52extern int justquit;
53
54static int dictate;
55static int slvcount;			/* slaves listening to our clock */
56
57static 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 */
70void
71master(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 */
103loop:
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 */
344static void
345mchgdate(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 */
381void
382synch(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 */
454void
455spreadtime(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
494void
495prthp(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
528static struct hosttbl *newhost_hash;
529static struct hosttbl *lasthfree = &hosttbl[0];
530
531
532struct hosttbl *			/* answer or 0 */
533findhost(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 */
558struct hosttbl *
559addmach(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 */
645struct hosttbl *
646remmach(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 */
696void
697rmnetmachs(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
712void
713masterup(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
725void
726newslave(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 */
770void
771doquit(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
801void
802traceon(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
819void
820traceoff(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