slave.c revision 30642
11553Srgrimes/*-
21553Srgrimes * Copyright (c) 1985, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3530642Scharnier#if 0
361553Srgrimesstatic char sccsid[] = "@(#)slave.c	8.1 (Berkeley) 6/6/93";
3730642Scharnier#endif
3830642Scharnierstatic const char rcsid[] =
3930642Scharnier	"$Id$";
401553Srgrimes#endif /* not lint */
411553Srgrimes
421553Srgrimes#include "globals.h"
431553Srgrimes#include <setjmp.h>
441553Srgrimes#include "pathnames.h"
451553Srgrimes
461553Srgrimesextern jmp_buf jmpenv;
471553Srgrimesextern int Mflag;
481553Srgrimesextern int justquit;
491553Srgrimes
501553Srgrimesextern u_short sequence;
511553Srgrimes
521553Srgrimesstatic char master_name[MAXHOSTNAMELEN+1];
531553Srgrimesstatic struct netinfo *old_slavenet;
541553Srgrimesstatic int old_status;
551553Srgrimes
561553Srgrimesstatic void schgdate __P((struct tsp *, char *));
571553Srgrimesstatic void setmaster __P((struct tsp *));
581553Srgrimesstatic void answerdelay __P((void));
591553Srgrimes
601553Srgrimes#ifdef sgi
611553Srgrimesextern void logwtmp __P((struct timeval *, struct timeval *));
621553Srgrimes#else
631553Srgrimesextern void logwtmp __P((char *, char *, char *));
641553Srgrimes#endif /* sgi */
651553Srgrimes
661553Srgrimesint
671553Srgrimesslave()
681553Srgrimes{
691553Srgrimes	int tries;
701553Srgrimes	long electiontime, refusetime, looktime, looptime, adjtime;
711553Srgrimes	u_short seq;
721553Srgrimes	long fastelection;
731553Srgrimes#define FASTTOUT 3
741553Srgrimes	struct in_addr cadr;
751553Srgrimes	struct timeval otime;
761553Srgrimes	struct sockaddr_in taddr;
771553Srgrimes	char tname[MAXHOSTNAMELEN];
781553Srgrimes	struct tsp *msg, to;
791553Srgrimes	struct timeval ntime, wait;
801553Srgrimes	struct tsp *answer;
811553Srgrimes	int timeout();
821553Srgrimes	char olddate[32];
831553Srgrimes	char newdate[32];
841553Srgrimes	struct netinfo *ntp;
851553Srgrimes	struct hosttbl *htp;
861553Srgrimes
871553Srgrimes
881553Srgrimes	old_slavenet = 0;
891553Srgrimes	seq = 0;
901553Srgrimes	refusetime = 0;
911553Srgrimes	adjtime = 0;
921553Srgrimes
931553Srgrimes	(void)gettimeofday(&ntime, 0);
941553Srgrimes	electiontime = ntime.tv_sec + delay2;
951553Srgrimes	fastelection = ntime.tv_sec + FASTTOUT;
961553Srgrimes	if (justquit)
971553Srgrimes		looktime = electiontime;
981553Srgrimes	else
991553Srgrimes		looktime = fastelection;
1001553Srgrimes	looptime = fastelection;
1011553Srgrimes
1021553Srgrimes	if (slavenet)
1031553Srgrimes		xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
1041553Srgrimes	if (status & MASTER) {
1051553Srgrimes		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
1061553Srgrimes			if (ntp->status == MASTER)
1071553Srgrimes				masterup(ntp);
1081553Srgrimes		}
1091553Srgrimes	}
1101553Srgrimes
1111553Srgrimesloop:
1121553Srgrimes	get_goodgroup(0);
1131553Srgrimes	(void)gettimeofday(&ntime, (struct timezone *)0);
1141553Srgrimes	if (ntime.tv_sec > electiontime) {
1151553Srgrimes		if (trace)
1161553Srgrimes			fprintf(fd, "election timer expired\n");
1171553Srgrimes		longjmp(jmpenv, 1);
1181553Srgrimes	}
1191553Srgrimes
1201553Srgrimes	if (ntime.tv_sec >= looktime) {
1211553Srgrimes		if (trace)
1221553Srgrimes			fprintf(fd, "Looking for nets to master\n");
1231553Srgrimes
1241553Srgrimes		if (Mflag && nignorednets > 0) {
1251553Srgrimes			for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
1261553Srgrimes				if (ntp->status == IGNORE
1271553Srgrimes				    || ntp->status == NOMASTER) {
1281553Srgrimes					lookformaster(ntp);
1291553Srgrimes					if (ntp->status == MASTER) {
1301553Srgrimes						masterup(ntp);
1311553Srgrimes					} else if (ntp->status == MASTER) {
1321553Srgrimes						ntp->status = NOMASTER;
1331553Srgrimes					}
1341553Srgrimes				}
1351553Srgrimes				if (ntp->status == MASTER
1361553Srgrimes				    && --ntp->quit_count < 0)
1371553Srgrimes					ntp->quit_count = 0;
1381553Srgrimes			}
1391553Srgrimes			makeslave(slavenet);	/* prune extras */
1401553Srgrimes			setstatus();
1411553Srgrimes		}
1421553Srgrimes		(void)gettimeofday(&ntime, 0);
1431553Srgrimes		looktime = ntime.tv_sec + delay2;
1441553Srgrimes	}
1451553Srgrimes	if (ntime.tv_sec >= looptime) {
1461553Srgrimes		if (trace)
1471553Srgrimes			fprintf(fd, "Looking for loops\n");
1481553Srgrimes		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
1491553Srgrimes		    if (ntp->status == MASTER) {
1501553Srgrimes			to.tsp_type = TSP_LOOP;
1511553Srgrimes			to.tsp_vers = TSPVERSION;
1521553Srgrimes			to.tsp_seq = sequence++;
1531553Srgrimes			to.tsp_hopcnt = MAX_HOPCNT;
15430642Scharnier			(void)strncpy(to.tsp_name, hostname,
15530642Scharnier					sizeof to.tsp_name-1);
15630642Scharnier			to.tsp_name[sizeof to.tsp_name-1] = '\0';
1571553Srgrimes			bytenetorder(&to);
1581553Srgrimes			if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
1591553Srgrimes				   (struct sockaddr*)&ntp->dest_addr,
1601553Srgrimes				   sizeof(ntp->dest_addr)) < 0) {
1611553Srgrimes				trace_sendto_err(ntp->dest_addr.sin_addr);
1621553Srgrimes			}
1631553Srgrimes		    }
1641553Srgrimes		}
1651553Srgrimes		(void)gettimeofday(&ntime, 0);
1661553Srgrimes		looptime = ntime.tv_sec + delay2;
1671553Srgrimes	}
1681553Srgrimes
1691553Srgrimes	wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
1701553Srgrimes	if (wait.tv_sec < 0)
1711553Srgrimes		wait.tv_sec = 0;
1721553Srgrimes	wait.tv_sec += FASTTOUT;
1731553Srgrimes	wait.tv_usec = 0;
1741553Srgrimes	msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
1751553Srgrimes
1761553Srgrimes	if (msg != NULL) {
1771553Srgrimes		/*
1781553Srgrimes		 * filter stuff not for us
1791553Srgrimes		 */
1801553Srgrimes		switch (msg->tsp_type) {
1811553Srgrimes		case TSP_SETDATE:
1821553Srgrimes		case TSP_TRACEOFF:
1831553Srgrimes		case TSP_TRACEON:
1841553Srgrimes			/*
1851553Srgrimes			 * XXX check to see they are from ourself
1861553Srgrimes			 */
1871553Srgrimes			break;
1881553Srgrimes
1891553Srgrimes		case TSP_TEST:
1901553Srgrimes		case TSP_MSITE:
1911553Srgrimes			break;
1921553Srgrimes
1931553Srgrimes		case TSP_MASTERUP:
1941553Srgrimes			if (!fromnet) {
1951553Srgrimes				if (trace) {
1961553Srgrimes					fprintf(fd, "slave ignored: ");
1971553Srgrimes					print(msg, &from);
1981553Srgrimes				}
1991553Srgrimes				goto loop;
2001553Srgrimes			}
2011553Srgrimes			break;
2021553Srgrimes
2031553Srgrimes		default:
2041553Srgrimes			if (!fromnet
2051553Srgrimes			    || fromnet->status == IGNORE
2061553Srgrimes			    || fromnet->status == NOMASTER) {
2071553Srgrimes				if (trace) {
2081553Srgrimes					fprintf(fd, "slave ignored: ");
2091553Srgrimes					print(msg, &from);
2101553Srgrimes				}
2111553Srgrimes				goto loop;
2121553Srgrimes			}
2131553Srgrimes			break;
2141553Srgrimes		}
2151553Srgrimes
2161553Srgrimes
2171553Srgrimes		/*
2181553Srgrimes		 * now process the message
2191553Srgrimes		 */
2201553Srgrimes		switch (msg->tsp_type) {
2211553Srgrimes
2221553Srgrimes		case TSP_ADJTIME:
2231553Srgrimes			if (fromnet != slavenet)
2241553Srgrimes				break;
2251553Srgrimes			if (!good_host_name(msg->tsp_name)) {
2261553Srgrimes				syslog(LOG_NOTICE,
2271553Srgrimes				   "attempted time adjustment by %s",
2281553Srgrimes				       msg->tsp_name);
2291553Srgrimes				suppress(&from, msg->tsp_name, fromnet);
2301553Srgrimes				break;
2311553Srgrimes			}
2321553Srgrimes			/*
2331553Srgrimes			 * Speed up loop detection in case we have a loop.
2341553Srgrimes			 * Otherwise the clocks can race until the loop
2351553Srgrimes			 * is found.
2361553Srgrimes			 */
2371553Srgrimes			(void)gettimeofday(&otime, 0);
2381553Srgrimes			if (adjtime < otime.tv_sec)
2391553Srgrimes				looptime -= (looptime-otime.tv_sec)/2 + 1;
2401553Srgrimes
2411553Srgrimes			setmaster(msg);
2421553Srgrimes			if (seq != msg->tsp_seq) {
2431553Srgrimes				seq = msg->tsp_seq;
2441553Srgrimes				synch(tvtomsround(msg->tsp_time));
2451553Srgrimes			}
2461553Srgrimes			(void)gettimeofday(&ntime, 0);
2471553Srgrimes			electiontime = ntime.tv_sec + delay2;
2481553Srgrimes			fastelection = ntime.tv_sec + FASTTOUT;
2491553Srgrimes			adjtime = ntime.tv_sec + SAMPLEINTVL*2;
2501553Srgrimes			break;
2511553Srgrimes
2521553Srgrimes		case TSP_SETTIME:
2531553Srgrimes			if (fromnet != slavenet)
2541553Srgrimes				break;
2551553Srgrimes			if (seq == msg->tsp_seq)
2561553Srgrimes				break;
2571553Srgrimes			seq = msg->tsp_seq;
2581553Srgrimes
2591553Srgrimes			/* adjust time for residence on the queue */
2601553Srgrimes			(void)gettimeofday(&otime, 0);
2611553Srgrimes			adj_msg_time(msg,&otime);
2621553Srgrimes#ifdef sgi
2631553Srgrimes			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
2641553Srgrimes			(void)cftime(olddate, "%D %T", &otime.tv_sec);
2651553Srgrimes#else
2661553Srgrimes			/*
2671553Srgrimes			 * the following line is necessary due to syslog
2681553Srgrimes			 * calling ctime() which clobbers the static buffer
2691553Srgrimes			 */
27030642Scharnier			(void)strncpy(olddate, date(), sizeof olddate-1);
27130642Scharnier			olddate[sizeof olddate-1] = '\0';
27230642Scharnier			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
27330642Scharnier					sizeof newdate-1);
27430642Scharnier			newdate[sizeof newdate-1] = '\0';
2751553Srgrimes#endif /* sgi */
2761553Srgrimes
2771553Srgrimes			if (!good_host_name(msg->tsp_name)) {
2781553Srgrimes				syslog(LOG_NOTICE,
2791553Srgrimes			    "attempted time setting by untrusted %s to %s",
2801553Srgrimes				       msg->tsp_name, newdate);
2811553Srgrimes				suppress(&from, msg->tsp_name, fromnet);
2821553Srgrimes				break;
2831553Srgrimes			}
2841553Srgrimes
2851553Srgrimes			setmaster(msg);
2861553Srgrimes			timevalsub(&ntime, &msg->tsp_time, &otime);
2871553Srgrimes			if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
2881553Srgrimes				/*
2891553Srgrimes				 * do not change the clock if we can adjust it
2901553Srgrimes				 */
2911553Srgrimes				synch(tvtomsround(ntime));
2921553Srgrimes			} else {
2931553Srgrimes#ifdef sgi
2941553Srgrimes				if (0 > settimeofday(&msg->tsp_time, 0)) {
2951553Srgrimes					syslog(LOG_ERR,"settimeofdate(): %m");
2961553Srgrimes					break;
2971553Srgrimes				}
2981553Srgrimes				logwtmp(&otime, &msg->tsp_time);
2991553Srgrimes#else
3001553Srgrimes				logwtmp("|", "date", "");
3011553Srgrimes				(void)settimeofday(&msg->tsp_time, 0);
30219093Sscrappy				logwtmp("{", "date", "");
3031553Srgrimes#endif /* sgi */
3041553Srgrimes				syslog(LOG_NOTICE,
3051553Srgrimes				       "date changed by %s from %s",
3061553Srgrimes					msg->tsp_name, olddate);
3071553Srgrimes				if (status & MASTER)
3081553Srgrimes					spreadtime();
3091553Srgrimes			}
3101553Srgrimes			(void)gettimeofday(&ntime, 0);
3111553Srgrimes			electiontime = ntime.tv_sec + delay2;
3121553Srgrimes			fastelection = ntime.tv_sec + FASTTOUT;
3131553Srgrimes
3141553Srgrimes/* This patches a bad protocol bug.  Imagine a system with several networks,
3151553Srgrimes * where there are a pair of redundant gateways between a pair of networks,
3161553Srgrimes * each running timed.  Assume that we start with a third machine mastering
3171553Srgrimes * one of the networks, and one of the gateways mastering the other.
3181553Srgrimes * Imagine that the third machine goes away and the non-master gateway
3191553Srgrimes * decides to replace it.  If things are timed just 'right,' we will have
3201553Srgrimes * each gateway mastering one network for a little while.  If a SETTIME
3211553Srgrimes * message gets into the network at that time, perhaps from the newly
3221553Srgrimes * masterful gateway as it was taking control, the SETTIME will loop
3231553Srgrimes * forever.  Each time a gateway receives it on its slave side, it will
3241553Srgrimes * call spreadtime to forward it on its mastered network.  We are now in
3251553Srgrimes * a permanent loop, since the SETTIME msgs will keep any clock
3261553Srgrimes * in the network from advancing.  Normally, the 'LOOP' stuff will detect
3271553Srgrimes * and correct the situation.  However, with the clocks stopped, the
3281553Srgrimes * 'looptime' timer cannot expire.  While they are in this state, the
3291553Srgrimes * masters will try to saturate the network with SETTIME packets.
3301553Srgrimes */
3311553Srgrimes			looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
3321553Srgrimes			break;
3331553Srgrimes
3341553Srgrimes		case TSP_MASTERUP:
3351553Srgrimes			if (slavenet && fromnet != slavenet)
3361553Srgrimes				break;
3371553Srgrimes			if (!good_host_name(msg->tsp_name)) {
3381553Srgrimes				suppress(&from, msg->tsp_name, fromnet);
3391553Srgrimes				if (electiontime > fastelection)
3401553Srgrimes					electiontime = fastelection;
3411553Srgrimes				break;
3421553Srgrimes			}
3431553Srgrimes			makeslave(fromnet);
3441553Srgrimes			setmaster(msg);
3451553Srgrimes			setstatus();
3461553Srgrimes			answerdelay();
3471553Srgrimes			xmit(TSP_SLAVEUP, 0, &from);
3481553Srgrimes			(void)gettimeofday(&ntime, 0);
3491553Srgrimes			electiontime = ntime.tv_sec + delay2;
3501553Srgrimes			fastelection = ntime.tv_sec + FASTTOUT;
3511553Srgrimes			refusetime = 0;
3521553Srgrimes			break;
3531553Srgrimes
3541553Srgrimes		case TSP_MASTERREQ:
3551553Srgrimes			if (fromnet->status != SLAVE)
3561553Srgrimes				break;
3571553Srgrimes			(void)gettimeofday(&ntime, 0);
3581553Srgrimes			electiontime = ntime.tv_sec + delay2;
3591553Srgrimes			break;
3601553Srgrimes
3611553Srgrimes		case TSP_SETDATE:
3621553Srgrimes#ifdef sgi
3631553Srgrimes			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
3641553Srgrimes#else
36530642Scharnier			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
36630642Scharnier					sizeof newdate-1);
36730642Scharnier			newdate[sizeof newdate-1] = '\0';
3681553Srgrimes#endif /* sgi */
3691553Srgrimes			schgdate(msg, newdate);
3701553Srgrimes			break;
3711553Srgrimes
3721553Srgrimes		case TSP_SETDATEREQ:
3731553Srgrimes			if (fromnet->status != MASTER)
3741553Srgrimes				break;
3751553Srgrimes#ifdef sgi
3761553Srgrimes			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
3771553Srgrimes#else
37830642Scharnier			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
37930642Scharnier					sizeof newdate-1);
38030642Scharnier			newdate[sizeof newdate-1] = '\0';
3811553Srgrimes#endif /* sgi */
3821553Srgrimes			htp = findhost(msg->tsp_name);
3831553Srgrimes			if (0 == htp) {
3841553Srgrimes				syslog(LOG_WARNING,
3851553Srgrimes				       "DATEREQ from uncontrolled machine");
3861553Srgrimes				break;
3871553Srgrimes			}
3881553Srgrimes			if (!htp->good) {
3891553Srgrimes				syslog(LOG_WARNING,
3901553Srgrimes				"attempted date change by untrusted %s to %s",
3911553Srgrimes				       htp->name, newdate);
3921553Srgrimes				spreadtime();
3931553Srgrimes				break;
3941553Srgrimes			}
3951553Srgrimes			schgdate(msg, newdate);
3961553Srgrimes			break;
3971553Srgrimes
3981553Srgrimes		case TSP_TRACEON:
3991553Srgrimes			traceon();
4001553Srgrimes			break;
4011553Srgrimes
4021553Srgrimes		case TSP_TRACEOFF:
4031553Srgrimes			traceoff("Tracing ended at %s\n");
4041553Srgrimes			break;
4051553Srgrimes
4061553Srgrimes		case TSP_SLAVEUP:
4071553Srgrimes			newslave(msg);
4081553Srgrimes			break;
4091553Srgrimes
4101553Srgrimes		case TSP_ELECTION:
4111553Srgrimes			if (fromnet->status == SLAVE) {
4121553Srgrimes				(void)gettimeofday(&ntime, 0);
4131553Srgrimes				electiontime = ntime.tv_sec + delay2;
4141553Srgrimes				fastelection = ntime.tv_sec + FASTTOUT;
4151553Srgrimes				seq = 0;
4161553Srgrimes				if (!good_host_name(msg->tsp_name)) {
4171553Srgrimes					syslog(LOG_NOTICE,
4181553Srgrimes					       "suppress election of %s",
4191553Srgrimes					       msg->tsp_name);
4201553Srgrimes					to.tsp_type = TSP_QUIT;
4211553Srgrimes					electiontime = fastelection;
4221553Srgrimes				} else if (cadr.s_addr != from.sin_addr.s_addr
4231553Srgrimes					   && ntime.tv_sec < refusetime) {
4241553Srgrimes/* if the candidate has to repeat itself, the old code would refuse it
4251553Srgrimes * the second time.  That would prevent elections.
4261553Srgrimes */
4271553Srgrimes					to.tsp_type = TSP_REFUSE;
4281553Srgrimes				} else {
4291553Srgrimes					cadr.s_addr = from.sin_addr.s_addr;
4301553Srgrimes					to.tsp_type = TSP_ACCEPT;
4311553Srgrimes					refusetime = ntime.tv_sec + 30;
4321553Srgrimes				}
4331553Srgrimes				taddr = from;
43430642Scharnier				(void)strncpy(tname, msg->tsp_name,
43530642Scharnier						sizeof tname-1);
43630642Scharnier				tname[sizeof tname-1] = '\0';
43730642Scharnier				(void)strncpy(to.tsp_name, hostname,
43830642Scharnier						sizeof to.tsp_name-1);
43930642Scharnier				to.tsp_name[sizeof to.tsp_name-1] = '\0';
4401553Srgrimes				answerdelay();
4411553Srgrimes				if (!acksend(&to, &taddr, tname,
4421553Srgrimes					     TSP_ACK, 0, 0))
4431553Srgrimes					syslog(LOG_WARNING,
4441553Srgrimes					     "no answer from candidate %s\n",
4451553Srgrimes					       tname);
4461553Srgrimes
4471553Srgrimes			} else {	/* fromnet->status == MASTER */
4481553Srgrimes				htp = addmach(msg->tsp_name, &from,fromnet);
4491553Srgrimes				to.tsp_type = TSP_QUIT;
45030642Scharnier				(void)strncpy(to.tsp_name, hostname,
45130642Scharnier						sizeof to.tsp_name-1);
45230642Scharnier				to.tsp_name[sizeof to.tsp_name-1] = '\0';
4531553Srgrimes				if (!acksend(&to, &htp->addr, htp->name,
4541553Srgrimes					     TSP_ACK, 0, htp->noanswer)) {
4551553Srgrimes					syslog(LOG_ERR,
4561553Srgrimes					  "no reply from %s to ELECTION-QUIT",
4571553Srgrimes					       htp->name);
4581553Srgrimes					(void)remmach(htp);
4591553Srgrimes				}
4601553Srgrimes			}
4611553Srgrimes			break;
4621553Srgrimes
4631553Srgrimes		case TSP_CONFLICT:
4641553Srgrimes			if (fromnet->status != MASTER)
4651553Srgrimes				break;
4661553Srgrimes			/*
4671553Srgrimes			 * After a network partition, there can be
4681553Srgrimes			 * more than one master: the first slave to
4691553Srgrimes			 * come up will notify here the situation.
4701553Srgrimes			 */
47130642Scharnier			(void)strncpy(to.tsp_name, hostname,
47230642Scharnier					sizeof to.tsp_name-1);
47330642Scharnier			to.tsp_name[sizeof to.tsp_name-1] = '\0';
4741553Srgrimes
4751553Srgrimes			/* The other master often gets into the same state,
4761553Srgrimes			 * with boring results.
4771553Srgrimes			 */
4781553Srgrimes			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
4791553Srgrimes			for (tries = 0; tries < 3; tries++) {
4801553Srgrimes				to.tsp_type = TSP_RESOLVE;
4811553Srgrimes				answer = acksend(&to, &ntp->dest_addr,
4821553Srgrimes						 ANYADDR, TSP_MASTERACK,
4831553Srgrimes						 ntp, 0);
4841553Srgrimes				if (answer == NULL)
4851553Srgrimes					break;
4861553Srgrimes				htp = addmach(answer->tsp_name,&from,ntp);
4871553Srgrimes				to.tsp_type = TSP_QUIT;
4881553Srgrimes				answer = acksend(&to, &htp->addr, htp->name,
4891553Srgrimes						 TSP_ACK, 0, htp->noanswer);
4901553Srgrimes				if (!answer) {
4911553Srgrimes					syslog(LOG_WARNING,
4921553Srgrimes				  "conflict error: no reply from %s to QUIT",
4931553Srgrimes						htp->name);
4941553Srgrimes					(void)remmach(htp);
4951553Srgrimes				}
4961553Srgrimes			}
4971553Srgrimes			masterup(ntp);
4981553Srgrimes			break;
4991553Srgrimes
5001553Srgrimes		case TSP_MSITE:
5011553Srgrimes			if (!slavenet)
5021553Srgrimes				break;
5031553Srgrimes			taddr = from;
5041553Srgrimes			to.tsp_type = TSP_MSITEREQ;
5051553Srgrimes			to.tsp_vers = TSPVERSION;
5061553Srgrimes			to.tsp_seq = 0;
50730642Scharnier			(void)strncpy(to.tsp_name, hostname,
50830642Scharnier					sizeof to.tsp_name-1);
50930642Scharnier			to.tsp_name[sizeof to.tsp_name-1] = '\0';
5101553Srgrimes			answer = acksend(&to, &slavenet->dest_addr,
5111553Srgrimes					 ANYADDR, TSP_ACK,
5121553Srgrimes					 slavenet, 0);
5131553Srgrimes			if (answer != NULL
5141553Srgrimes			    && good_host_name(answer->tsp_name)) {
5151553Srgrimes				setmaster(answer);
5161553Srgrimes				to.tsp_type = TSP_ACK;
51730642Scharnier				(void)strncpy(to.tsp_name, answer->tsp_name,
51830642Scharnier						sizeof to.tsp_name-1);
51930642Scharnier				to.tsp_name[sizeof to.tsp_name-1] = '\0';
5201553Srgrimes				bytenetorder(&to);
5211553Srgrimes				if (sendto(sock, (char *)&to,
5221553Srgrimes					   sizeof(struct tsp), 0,
52330642Scharnier					   (struct sockaddr*)&taddr,
52430642Scharnier						sizeof(taddr)) < 0) {
5251553Srgrimes					trace_sendto_err(taddr.sin_addr);
5261553Srgrimes				}
5271553Srgrimes			}
5281553Srgrimes			break;
5291553Srgrimes
5301553Srgrimes		case TSP_MSITEREQ:
5311553Srgrimes			break;
5321553Srgrimes
5331553Srgrimes		case TSP_ACCEPT:
5341553Srgrimes		case TSP_REFUSE:
5351553Srgrimes		case TSP_RESOLVE:
5361553Srgrimes			break;
5371553Srgrimes
5381553Srgrimes		case TSP_QUIT:
5391553Srgrimes			doquit(msg);		/* become a slave */
5401553Srgrimes			break;
5411553Srgrimes
5421553Srgrimes		case TSP_TEST:
5431553Srgrimes			electiontime = 0;
5441553Srgrimes			break;
5451553Srgrimes
5461553Srgrimes		case TSP_LOOP:
5471553Srgrimes			/* looking for loops of masters */
5481553Srgrimes			if (!(status & MASTER))
5491553Srgrimes				break;
5501553Srgrimes			if (fromnet->status == SLAVE) {
5511553Srgrimes			    if (!strcmp(msg->tsp_name, hostname)) {
5521553Srgrimes				/*
5531553Srgrimes				 * Someone forwarded our message back to
5541553Srgrimes				 * us.  There must be a loop.  Tell the
5551553Srgrimes				 * master of this network to quit.
5561553Srgrimes				 *
5571553Srgrimes				 * The other master often gets into
5581553Srgrimes				 * the same state, with boring results.
5591553Srgrimes				 */
5601553Srgrimes				ntp = fromnet;
5611553Srgrimes				for (tries = 0; tries < 3; tries++) {
5621553Srgrimes				    to.tsp_type = TSP_RESOLVE;
5631553Srgrimes				    answer = acksend(&to, &ntp->dest_addr,
5641553Srgrimes						     ANYADDR, TSP_MASTERACK,
5651553Srgrimes						     ntp,0);
5661553Srgrimes				    if (answer == NULL)
5671553Srgrimes					break;
5681553Srgrimes				    taddr = from;
56930642Scharnier				    (void)strncpy(tname, answer->tsp_name,
57030642Scharnier							sizeof tname-1);
57130642Scharnier				    tname[sizeof tname-1] = '\0';
5721553Srgrimes				    to.tsp_type = TSP_QUIT;
57330642Scharnier				    (void)strncpy(to.tsp_name, hostname,
57430642Scharnier							sizeof to.tsp_name-1);
57530642Scharnier				    to.tsp_name[sizeof to.tsp_name-1] = '\0';
5761553Srgrimes				    if (!acksend(&to, &taddr, tname,
5771553Srgrimes						 TSP_ACK, 0, 1)) {
5781553Srgrimes					syslog(LOG_ERR,
5791553Srgrimes					"no reply from %s to slave LOOP-QUIT",
5801553Srgrimes						 tname);
5811553Srgrimes				    } else {
5821553Srgrimes					electiontime = 0;
5831553Srgrimes				    }
5841553Srgrimes				}
5851553Srgrimes				(void)gettimeofday(&ntime, 0);
5861553Srgrimes				looptime = ntime.tv_sec + FASTTOUT;
5871553Srgrimes			    } else {
5881553Srgrimes				if (msg->tsp_hopcnt-- < 1)
5891553Srgrimes				    break;
5901553Srgrimes				bytenetorder(msg);
5911553Srgrimes				for (ntp = nettab; ntp != 0; ntp = ntp->next) {
5921553Srgrimes				    if (ntp->status == MASTER
5931553Srgrimes					&& 0 > sendto(sock, (char *)msg,
5941553Srgrimes						      sizeof(struct tsp), 0,
5951553Srgrimes					      (struct sockaddr*)&ntp->dest_addr,
5961553Srgrimes						      sizeof(ntp->dest_addr)))
5971553Srgrimes				    trace_sendto_err(ntp->dest_addr.sin_addr);
5981553Srgrimes				}
5991553Srgrimes			    }
6001553Srgrimes			} else {	/* fromnet->status == MASTER */
6011553Srgrimes			    /*
6021553Srgrimes			     * We should not have received this from a net
6031553Srgrimes			     * we are master on.  There must be two masters,
6041553Srgrimes			     * unless the packet was really from us.
6051553Srgrimes			     */
6061553Srgrimes			    if (from.sin_addr.s_addr
6071553Srgrimes				== fromnet->my_addr.s_addr) {
6081553Srgrimes				if (trace)
6091553Srgrimes				    fprintf(fd,"discarding forwarded LOOP\n");
6101553Srgrimes				break;
6111553Srgrimes			    }
6121553Srgrimes
6131553Srgrimes			    /*
6141553Srgrimes			     * The other master often gets into the same
6151553Srgrimes			     * state, with boring results.
6161553Srgrimes			     */
6171553Srgrimes			    ntp = fromnet;
6181553Srgrimes			    for (tries = 0; tries < 3; tries++) {
6191553Srgrimes				to.tsp_type = TSP_RESOLVE;
6201553Srgrimes				answer = acksend(&to, &ntp->dest_addr,
6211553Srgrimes						 ANYADDR, TSP_MASTERACK,
6221553Srgrimes						ntp,0);
6231553Srgrimes				if (!answer)
6241553Srgrimes					break;
6251553Srgrimes				htp = addmach(answer->tsp_name,
6261553Srgrimes					      &from,ntp);
6271553Srgrimes				to.tsp_type = TSP_QUIT;
62830642Scharnier				(void)strncpy(to.tsp_name, hostname,
62930642Scharnier						sizeof to.tsp_name-1);
63030642Scharnier				to.tsp_name[sizeof to.tsp_name-1] = '\0';
6311553Srgrimes				if (!acksend(&to,&htp->addr,htp->name,
6321553Srgrimes					     TSP_ACK, 0, htp->noanswer)) {
6331553Srgrimes					syslog(LOG_ERR,
6341553Srgrimes				    "no reply from %s to master LOOP-QUIT",
6351553Srgrimes					       htp->name);
6361553Srgrimes					(void)remmach(htp);
6371553Srgrimes				}
6381553Srgrimes			    }
6391553Srgrimes			    (void)gettimeofday(&ntime, 0);
6401553Srgrimes			    looptime = ntime.tv_sec + FASTTOUT;
6411553Srgrimes			}
6421553Srgrimes			break;
6431553Srgrimes		default:
6441553Srgrimes			if (trace) {
6451553Srgrimes				fprintf(fd, "garbage message: ");
6461553Srgrimes				print(msg, &from);
6471553Srgrimes			}
6481553Srgrimes			break;
6491553Srgrimes		}
6501553Srgrimes	}
6511553Srgrimes	goto loop;
6521553Srgrimes}
6531553Srgrimes
6541553Srgrimes
6551553Srgrimes/*
6561553Srgrimes * tell the world who our master is
6571553Srgrimes */
6581553Srgrimesstatic void
6591553Srgrimessetmaster(msg)
6601553Srgrimes	struct tsp *msg;
6611553Srgrimes{
6621553Srgrimes	if (slavenet
6631553Srgrimes	    && (slavenet != old_slavenet
6641553Srgrimes		|| strcmp(msg->tsp_name, master_name)
6651553Srgrimes		|| old_status != status)) {
66630642Scharnier		(void)strncpy(master_name, msg->tsp_name,
66730642Scharnier				sizeof master_name-1);
66830642Scharnier		master_name[sizeof master_name-1] = '\0';
6691553Srgrimes		old_slavenet = slavenet;
6701553Srgrimes		old_status = status;
6711553Srgrimes
6721553Srgrimes		if (status & MASTER) {
6731553Srgrimes			syslog(LOG_NOTICE, "submaster to %s", master_name);
6741553Srgrimes			if (trace)
6751553Srgrimes				fprintf(fd, "submaster to %s\n", master_name);
6761553Srgrimes
6771553Srgrimes		} else {
6781553Srgrimes			syslog(LOG_NOTICE, "slave to %s", master_name);
6791553Srgrimes			if (trace)
6801553Srgrimes				fprintf(fd, "slave to %s\n", master_name);
6811553Srgrimes		}
6821553Srgrimes	}
6831553Srgrimes}
6841553Srgrimes
6851553Srgrimes
6861553Srgrimes
6871553Srgrimes/*
6881553Srgrimes * handle date change request on a slave
6891553Srgrimes */
6901553Srgrimesstatic void
6911553Srgrimesschgdate(msg, newdate)
6921553Srgrimes	struct tsp *msg;
6931553Srgrimes	char *newdate;
6941553Srgrimes{
6951553Srgrimes	struct tsp to;
6961553Srgrimes	u_short seq;
6971553Srgrimes	struct sockaddr_in taddr;
6981553Srgrimes	struct timeval otime;
6991553Srgrimes
7001553Srgrimes	if (!slavenet)
7011553Srgrimes		return;			/* no where to forward */
7021553Srgrimes
7031553Srgrimes	taddr = from;
7041553Srgrimes	seq = msg->tsp_seq;
7051553Srgrimes
7061553Srgrimes	syslog(LOG_INFO,
7071553Srgrimes	       "forwarding date change by %s to %s",
7081553Srgrimes	       msg->tsp_name, newdate);
7091553Srgrimes
7101553Srgrimes	/* adjust time for residence on the queue */
7111553Srgrimes	(void)gettimeofday(&otime, 0);
7121553Srgrimes	adj_msg_time(msg, &otime);
7131553Srgrimes
7141553Srgrimes	to.tsp_type = TSP_SETDATEREQ;
7151553Srgrimes	to.tsp_time = msg->tsp_time;
71630642Scharnier	(void)strncpy(to.tsp_name, hostname, sizeof to.tsp_name-1);
71730642Scharnier	to.tsp_name[sizeof to.tsp_name-1] = '\0';
7181553Srgrimes	if (!acksend(&to, &slavenet->dest_addr,
7191553Srgrimes		     ANYADDR, TSP_DATEACK,
7201553Srgrimes		     slavenet, 0))
7211553Srgrimes		return;			/* no answer */
7221553Srgrimes
7231553Srgrimes	xmit(TSP_DATEACK, seq, &taddr);
7241553Srgrimes}
7251553Srgrimes
7261553Srgrimes
7271553Srgrimes/*
7281553Srgrimes * Used before answering a broadcast message to avoid network
7291553Srgrimes * contention and likely collisions.
7301553Srgrimes */
7311553Srgrimesstatic void
7321553Srgrimesanswerdelay()
7331553Srgrimes{
7341553Srgrimes#ifdef sgi
7351553Srgrimes	sginap(delay1);
7361553Srgrimes#else
7371553Srgrimes	struct timeval timeout;
7381553Srgrimes
7391553Srgrimes	timeout.tv_sec = 0;
7401553Srgrimes	timeout.tv_usec = delay1;
7411553Srgrimes
7421553Srgrimes	(void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
7431553Srgrimes	    &timeout);
7441553Srgrimes	return;
7451553Srgrimes#endif /* sgi */
7461553Srgrimes}
747