slave.c revision 30642
1/*-
2 * Copyright (c) 1985, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)slave.c	8.1 (Berkeley) 6/6/93";
37#endif
38static const char rcsid[] =
39	"$Id$";
40#endif /* not lint */
41
42#include "globals.h"
43#include <setjmp.h>
44#include "pathnames.h"
45
46extern jmp_buf jmpenv;
47extern int Mflag;
48extern int justquit;
49
50extern u_short sequence;
51
52static char master_name[MAXHOSTNAMELEN+1];
53static struct netinfo *old_slavenet;
54static int old_status;
55
56static void schgdate __P((struct tsp *, char *));
57static void setmaster __P((struct tsp *));
58static void answerdelay __P((void));
59
60#ifdef sgi
61extern void logwtmp __P((struct timeval *, struct timeval *));
62#else
63extern void logwtmp __P((char *, char *, char *));
64#endif /* sgi */
65
66int
67slave()
68{
69	int tries;
70	long electiontime, refusetime, looktime, looptime, adjtime;
71	u_short seq;
72	long fastelection;
73#define FASTTOUT 3
74	struct in_addr cadr;
75	struct timeval otime;
76	struct sockaddr_in taddr;
77	char tname[MAXHOSTNAMELEN];
78	struct tsp *msg, to;
79	struct timeval ntime, wait;
80	struct tsp *answer;
81	int timeout();
82	char olddate[32];
83	char newdate[32];
84	struct netinfo *ntp;
85	struct hosttbl *htp;
86
87
88	old_slavenet = 0;
89	seq = 0;
90	refusetime = 0;
91	adjtime = 0;
92
93	(void)gettimeofday(&ntime, 0);
94	electiontime = ntime.tv_sec + delay2;
95	fastelection = ntime.tv_sec + FASTTOUT;
96	if (justquit)
97		looktime = electiontime;
98	else
99		looktime = fastelection;
100	looptime = fastelection;
101
102	if (slavenet)
103		xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
104	if (status & MASTER) {
105		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
106			if (ntp->status == MASTER)
107				masterup(ntp);
108		}
109	}
110
111loop:
112	get_goodgroup(0);
113	(void)gettimeofday(&ntime, (struct timezone *)0);
114	if (ntime.tv_sec > electiontime) {
115		if (trace)
116			fprintf(fd, "election timer expired\n");
117		longjmp(jmpenv, 1);
118	}
119
120	if (ntime.tv_sec >= looktime) {
121		if (trace)
122			fprintf(fd, "Looking for nets to master\n");
123
124		if (Mflag && nignorednets > 0) {
125			for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
126				if (ntp->status == IGNORE
127				    || ntp->status == NOMASTER) {
128					lookformaster(ntp);
129					if (ntp->status == MASTER) {
130						masterup(ntp);
131					} else if (ntp->status == MASTER) {
132						ntp->status = NOMASTER;
133					}
134				}
135				if (ntp->status == MASTER
136				    && --ntp->quit_count < 0)
137					ntp->quit_count = 0;
138			}
139			makeslave(slavenet);	/* prune extras */
140			setstatus();
141		}
142		(void)gettimeofday(&ntime, 0);
143		looktime = ntime.tv_sec + delay2;
144	}
145	if (ntime.tv_sec >= looptime) {
146		if (trace)
147			fprintf(fd, "Looking for loops\n");
148		for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
149		    if (ntp->status == MASTER) {
150			to.tsp_type = TSP_LOOP;
151			to.tsp_vers = TSPVERSION;
152			to.tsp_seq = sequence++;
153			to.tsp_hopcnt = MAX_HOPCNT;
154			(void)strncpy(to.tsp_name, hostname,
155					sizeof to.tsp_name-1);
156			to.tsp_name[sizeof to.tsp_name-1] = '\0';
157			bytenetorder(&to);
158			if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
159				   (struct sockaddr*)&ntp->dest_addr,
160				   sizeof(ntp->dest_addr)) < 0) {
161				trace_sendto_err(ntp->dest_addr.sin_addr);
162			}
163		    }
164		}
165		(void)gettimeofday(&ntime, 0);
166		looptime = ntime.tv_sec + delay2;
167	}
168
169	wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
170	if (wait.tv_sec < 0)
171		wait.tv_sec = 0;
172	wait.tv_sec += FASTTOUT;
173	wait.tv_usec = 0;
174	msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
175
176	if (msg != NULL) {
177		/*
178		 * filter stuff not for us
179		 */
180		switch (msg->tsp_type) {
181		case TSP_SETDATE:
182		case TSP_TRACEOFF:
183		case TSP_TRACEON:
184			/*
185			 * XXX check to see they are from ourself
186			 */
187			break;
188
189		case TSP_TEST:
190		case TSP_MSITE:
191			break;
192
193		case TSP_MASTERUP:
194			if (!fromnet) {
195				if (trace) {
196					fprintf(fd, "slave ignored: ");
197					print(msg, &from);
198				}
199				goto loop;
200			}
201			break;
202
203		default:
204			if (!fromnet
205			    || fromnet->status == IGNORE
206			    || fromnet->status == NOMASTER) {
207				if (trace) {
208					fprintf(fd, "slave ignored: ");
209					print(msg, &from);
210				}
211				goto loop;
212			}
213			break;
214		}
215
216
217		/*
218		 * now process the message
219		 */
220		switch (msg->tsp_type) {
221
222		case TSP_ADJTIME:
223			if (fromnet != slavenet)
224				break;
225			if (!good_host_name(msg->tsp_name)) {
226				syslog(LOG_NOTICE,
227				   "attempted time adjustment by %s",
228				       msg->tsp_name);
229				suppress(&from, msg->tsp_name, fromnet);
230				break;
231			}
232			/*
233			 * Speed up loop detection in case we have a loop.
234			 * Otherwise the clocks can race until the loop
235			 * is found.
236			 */
237			(void)gettimeofday(&otime, 0);
238			if (adjtime < otime.tv_sec)
239				looptime -= (looptime-otime.tv_sec)/2 + 1;
240
241			setmaster(msg);
242			if (seq != msg->tsp_seq) {
243				seq = msg->tsp_seq;
244				synch(tvtomsround(msg->tsp_time));
245			}
246			(void)gettimeofday(&ntime, 0);
247			electiontime = ntime.tv_sec + delay2;
248			fastelection = ntime.tv_sec + FASTTOUT;
249			adjtime = ntime.tv_sec + SAMPLEINTVL*2;
250			break;
251
252		case TSP_SETTIME:
253			if (fromnet != slavenet)
254				break;
255			if (seq == msg->tsp_seq)
256				break;
257			seq = msg->tsp_seq;
258
259			/* adjust time for residence on the queue */
260			(void)gettimeofday(&otime, 0);
261			adj_msg_time(msg,&otime);
262#ifdef sgi
263			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
264			(void)cftime(olddate, "%D %T", &otime.tv_sec);
265#else
266			/*
267			 * the following line is necessary due to syslog
268			 * calling ctime() which clobbers the static buffer
269			 */
270			(void)strncpy(olddate, date(), sizeof olddate-1);
271			olddate[sizeof olddate-1] = '\0';
272			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
273					sizeof newdate-1);
274			newdate[sizeof newdate-1] = '\0';
275#endif /* sgi */
276
277			if (!good_host_name(msg->tsp_name)) {
278				syslog(LOG_NOTICE,
279			    "attempted time setting by untrusted %s to %s",
280				       msg->tsp_name, newdate);
281				suppress(&from, msg->tsp_name, fromnet);
282				break;
283			}
284
285			setmaster(msg);
286			timevalsub(&ntime, &msg->tsp_time, &otime);
287			if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
288				/*
289				 * do not change the clock if we can adjust it
290				 */
291				synch(tvtomsround(ntime));
292			} else {
293#ifdef sgi
294				if (0 > settimeofday(&msg->tsp_time, 0)) {
295					syslog(LOG_ERR,"settimeofdate(): %m");
296					break;
297				}
298				logwtmp(&otime, &msg->tsp_time);
299#else
300				logwtmp("|", "date", "");
301				(void)settimeofday(&msg->tsp_time, 0);
302				logwtmp("{", "date", "");
303#endif /* sgi */
304				syslog(LOG_NOTICE,
305				       "date changed by %s from %s",
306					msg->tsp_name, olddate);
307				if (status & MASTER)
308					spreadtime();
309			}
310			(void)gettimeofday(&ntime, 0);
311			electiontime = ntime.tv_sec + delay2;
312			fastelection = ntime.tv_sec + FASTTOUT;
313
314/* This patches a bad protocol bug.  Imagine a system with several networks,
315 * where there are a pair of redundant gateways between a pair of networks,
316 * each running timed.  Assume that we start with a third machine mastering
317 * one of the networks, and one of the gateways mastering the other.
318 * Imagine that the third machine goes away and the non-master gateway
319 * decides to replace it.  If things are timed just 'right,' we will have
320 * each gateway mastering one network for a little while.  If a SETTIME
321 * message gets into the network at that time, perhaps from the newly
322 * masterful gateway as it was taking control, the SETTIME will loop
323 * forever.  Each time a gateway receives it on its slave side, it will
324 * call spreadtime to forward it on its mastered network.  We are now in
325 * a permanent loop, since the SETTIME msgs will keep any clock
326 * in the network from advancing.  Normally, the 'LOOP' stuff will detect
327 * and correct the situation.  However, with the clocks stopped, the
328 * 'looptime' timer cannot expire.  While they are in this state, the
329 * masters will try to saturate the network with SETTIME packets.
330 */
331			looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
332			break;
333
334		case TSP_MASTERUP:
335			if (slavenet && fromnet != slavenet)
336				break;
337			if (!good_host_name(msg->tsp_name)) {
338				suppress(&from, msg->tsp_name, fromnet);
339				if (electiontime > fastelection)
340					electiontime = fastelection;
341				break;
342			}
343			makeslave(fromnet);
344			setmaster(msg);
345			setstatus();
346			answerdelay();
347			xmit(TSP_SLAVEUP, 0, &from);
348			(void)gettimeofday(&ntime, 0);
349			electiontime = ntime.tv_sec + delay2;
350			fastelection = ntime.tv_sec + FASTTOUT;
351			refusetime = 0;
352			break;
353
354		case TSP_MASTERREQ:
355			if (fromnet->status != SLAVE)
356				break;
357			(void)gettimeofday(&ntime, 0);
358			electiontime = ntime.tv_sec + delay2;
359			break;
360
361		case TSP_SETDATE:
362#ifdef sgi
363			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
364#else
365			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
366					sizeof newdate-1);
367			newdate[sizeof newdate-1] = '\0';
368#endif /* sgi */
369			schgdate(msg, newdate);
370			break;
371
372		case TSP_SETDATEREQ:
373			if (fromnet->status != MASTER)
374				break;
375#ifdef sgi
376			(void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
377#else
378			(void)strncpy(newdate, ctime(&msg->tsp_time.tv_sec),
379					sizeof newdate-1);
380			newdate[sizeof newdate-1] = '\0';
381#endif /* sgi */
382			htp = findhost(msg->tsp_name);
383			if (0 == htp) {
384				syslog(LOG_WARNING,
385				       "DATEREQ from uncontrolled machine");
386				break;
387			}
388			if (!htp->good) {
389				syslog(LOG_WARNING,
390				"attempted date change by untrusted %s to %s",
391				       htp->name, newdate);
392				spreadtime();
393				break;
394			}
395			schgdate(msg, newdate);
396			break;
397
398		case TSP_TRACEON:
399			traceon();
400			break;
401
402		case TSP_TRACEOFF:
403			traceoff("Tracing ended at %s\n");
404			break;
405
406		case TSP_SLAVEUP:
407			newslave(msg);
408			break;
409
410		case TSP_ELECTION:
411			if (fromnet->status == SLAVE) {
412				(void)gettimeofday(&ntime, 0);
413				electiontime = ntime.tv_sec + delay2;
414				fastelection = ntime.tv_sec + FASTTOUT;
415				seq = 0;
416				if (!good_host_name(msg->tsp_name)) {
417					syslog(LOG_NOTICE,
418					       "suppress election of %s",
419					       msg->tsp_name);
420					to.tsp_type = TSP_QUIT;
421					electiontime = fastelection;
422				} else if (cadr.s_addr != from.sin_addr.s_addr
423					   && ntime.tv_sec < refusetime) {
424/* if the candidate has to repeat itself, the old code would refuse it
425 * the second time.  That would prevent elections.
426 */
427					to.tsp_type = TSP_REFUSE;
428				} else {
429					cadr.s_addr = from.sin_addr.s_addr;
430					to.tsp_type = TSP_ACCEPT;
431					refusetime = ntime.tv_sec + 30;
432				}
433				taddr = from;
434				(void)strncpy(tname, msg->tsp_name,
435						sizeof tname-1);
436				tname[sizeof tname-1] = '\0';
437				(void)strncpy(to.tsp_name, hostname,
438						sizeof to.tsp_name-1);
439				to.tsp_name[sizeof to.tsp_name-1] = '\0';
440				answerdelay();
441				if (!acksend(&to, &taddr, tname,
442					     TSP_ACK, 0, 0))
443					syslog(LOG_WARNING,
444					     "no answer from candidate %s\n",
445					       tname);
446
447			} else {	/* fromnet->status == MASTER */
448				htp = addmach(msg->tsp_name, &from,fromnet);
449				to.tsp_type = TSP_QUIT;
450				(void)strncpy(to.tsp_name, hostname,
451						sizeof to.tsp_name-1);
452				to.tsp_name[sizeof to.tsp_name-1] = '\0';
453				if (!acksend(&to, &htp->addr, htp->name,
454					     TSP_ACK, 0, htp->noanswer)) {
455					syslog(LOG_ERR,
456					  "no reply from %s to ELECTION-QUIT",
457					       htp->name);
458					(void)remmach(htp);
459				}
460			}
461			break;
462
463		case TSP_CONFLICT:
464			if (fromnet->status != MASTER)
465				break;
466			/*
467			 * After a network partition, there can be
468			 * more than one master: the first slave to
469			 * come up will notify here the situation.
470			 */
471			(void)strncpy(to.tsp_name, hostname,
472					sizeof to.tsp_name-1);
473			to.tsp_name[sizeof to.tsp_name-1] = '\0';
474
475			/* The other master often gets into the same state,
476			 * with boring results.
477			 */
478			ntp = fromnet;	/* (acksend() can leave fromnet=0 */
479			for (tries = 0; tries < 3; tries++) {
480				to.tsp_type = TSP_RESOLVE;
481				answer = acksend(&to, &ntp->dest_addr,
482						 ANYADDR, TSP_MASTERACK,
483						 ntp, 0);
484				if (answer == NULL)
485					break;
486				htp = addmach(answer->tsp_name,&from,ntp);
487				to.tsp_type = TSP_QUIT;
488				answer = acksend(&to, &htp->addr, htp->name,
489						 TSP_ACK, 0, htp->noanswer);
490				if (!answer) {
491					syslog(LOG_WARNING,
492				  "conflict error: no reply from %s to QUIT",
493						htp->name);
494					(void)remmach(htp);
495				}
496			}
497			masterup(ntp);
498			break;
499
500		case TSP_MSITE:
501			if (!slavenet)
502				break;
503			taddr = from;
504			to.tsp_type = TSP_MSITEREQ;
505			to.tsp_vers = TSPVERSION;
506			to.tsp_seq = 0;
507			(void)strncpy(to.tsp_name, hostname,
508					sizeof to.tsp_name-1);
509			to.tsp_name[sizeof to.tsp_name-1] = '\0';
510			answer = acksend(&to, &slavenet->dest_addr,
511					 ANYADDR, TSP_ACK,
512					 slavenet, 0);
513			if (answer != NULL
514			    && good_host_name(answer->tsp_name)) {
515				setmaster(answer);
516				to.tsp_type = TSP_ACK;
517				(void)strncpy(to.tsp_name, answer->tsp_name,
518						sizeof to.tsp_name-1);
519				to.tsp_name[sizeof to.tsp_name-1] = '\0';
520				bytenetorder(&to);
521				if (sendto(sock, (char *)&to,
522					   sizeof(struct tsp), 0,
523					   (struct sockaddr*)&taddr,
524						sizeof(taddr)) < 0) {
525					trace_sendto_err(taddr.sin_addr);
526				}
527			}
528			break;
529
530		case TSP_MSITEREQ:
531			break;
532
533		case TSP_ACCEPT:
534		case TSP_REFUSE:
535		case TSP_RESOLVE:
536			break;
537
538		case TSP_QUIT:
539			doquit(msg);		/* become a slave */
540			break;
541
542		case TSP_TEST:
543			electiontime = 0;
544			break;
545
546		case TSP_LOOP:
547			/* looking for loops of masters */
548			if (!(status & MASTER))
549				break;
550			if (fromnet->status == SLAVE) {
551			    if (!strcmp(msg->tsp_name, hostname)) {
552				/*
553				 * Someone forwarded our message back to
554				 * us.  There must be a loop.  Tell the
555				 * master of this network to quit.
556				 *
557				 * The other master often gets into
558				 * the same state, with boring results.
559				 */
560				ntp = fromnet;
561				for (tries = 0; tries < 3; tries++) {
562				    to.tsp_type = TSP_RESOLVE;
563				    answer = acksend(&to, &ntp->dest_addr,
564						     ANYADDR, TSP_MASTERACK,
565						     ntp,0);
566				    if (answer == NULL)
567					break;
568				    taddr = from;
569				    (void)strncpy(tname, answer->tsp_name,
570							sizeof tname-1);
571				    tname[sizeof tname-1] = '\0';
572				    to.tsp_type = TSP_QUIT;
573				    (void)strncpy(to.tsp_name, hostname,
574							sizeof to.tsp_name-1);
575				    to.tsp_name[sizeof to.tsp_name-1] = '\0';
576				    if (!acksend(&to, &taddr, tname,
577						 TSP_ACK, 0, 1)) {
578					syslog(LOG_ERR,
579					"no reply from %s to slave LOOP-QUIT",
580						 tname);
581				    } else {
582					electiontime = 0;
583				    }
584				}
585				(void)gettimeofday(&ntime, 0);
586				looptime = ntime.tv_sec + FASTTOUT;
587			    } else {
588				if (msg->tsp_hopcnt-- < 1)
589				    break;
590				bytenetorder(msg);
591				for (ntp = nettab; ntp != 0; ntp = ntp->next) {
592				    if (ntp->status == MASTER
593					&& 0 > sendto(sock, (char *)msg,
594						      sizeof(struct tsp), 0,
595					      (struct sockaddr*)&ntp->dest_addr,
596						      sizeof(ntp->dest_addr)))
597				    trace_sendto_err(ntp->dest_addr.sin_addr);
598				}
599			    }
600			} else {	/* fromnet->status == MASTER */
601			    /*
602			     * We should not have received this from a net
603			     * we are master on.  There must be two masters,
604			     * unless the packet was really from us.
605			     */
606			    if (from.sin_addr.s_addr
607				== fromnet->my_addr.s_addr) {
608				if (trace)
609				    fprintf(fd,"discarding forwarded LOOP\n");
610				break;
611			    }
612
613			    /*
614			     * The other master often gets into the same
615			     * state, with boring results.
616			     */
617			    ntp = fromnet;
618			    for (tries = 0; tries < 3; tries++) {
619				to.tsp_type = TSP_RESOLVE;
620				answer = acksend(&to, &ntp->dest_addr,
621						 ANYADDR, TSP_MASTERACK,
622						ntp,0);
623				if (!answer)
624					break;
625				htp = addmach(answer->tsp_name,
626					      &from,ntp);
627				to.tsp_type = TSP_QUIT;
628				(void)strncpy(to.tsp_name, hostname,
629						sizeof to.tsp_name-1);
630				to.tsp_name[sizeof to.tsp_name-1] = '\0';
631				if (!acksend(&to,&htp->addr,htp->name,
632					     TSP_ACK, 0, htp->noanswer)) {
633					syslog(LOG_ERR,
634				    "no reply from %s to master LOOP-QUIT",
635					       htp->name);
636					(void)remmach(htp);
637				}
638			    }
639			    (void)gettimeofday(&ntime, 0);
640			    looptime = ntime.tv_sec + FASTTOUT;
641			}
642			break;
643		default:
644			if (trace) {
645				fprintf(fd, "garbage message: ");
646				print(msg, &from);
647			}
648			break;
649		}
650	}
651	goto loop;
652}
653
654
655/*
656 * tell the world who our master is
657 */
658static void
659setmaster(msg)
660	struct tsp *msg;
661{
662	if (slavenet
663	    && (slavenet != old_slavenet
664		|| strcmp(msg->tsp_name, master_name)
665		|| old_status != status)) {
666		(void)strncpy(master_name, msg->tsp_name,
667				sizeof master_name-1);
668		master_name[sizeof master_name-1] = '\0';
669		old_slavenet = slavenet;
670		old_status = status;
671
672		if (status & MASTER) {
673			syslog(LOG_NOTICE, "submaster to %s", master_name);
674			if (trace)
675				fprintf(fd, "submaster to %s\n", master_name);
676
677		} else {
678			syslog(LOG_NOTICE, "slave to %s", master_name);
679			if (trace)
680				fprintf(fd, "slave to %s\n", master_name);
681		}
682	}
683}
684
685
686
687/*
688 * handle date change request on a slave
689 */
690static void
691schgdate(msg, newdate)
692	struct tsp *msg;
693	char *newdate;
694{
695	struct tsp to;
696	u_short seq;
697	struct sockaddr_in taddr;
698	struct timeval otime;
699
700	if (!slavenet)
701		return;			/* no where to forward */
702
703	taddr = from;
704	seq = msg->tsp_seq;
705
706	syslog(LOG_INFO,
707	       "forwarding date change by %s to %s",
708	       msg->tsp_name, newdate);
709
710	/* adjust time for residence on the queue */
711	(void)gettimeofday(&otime, 0);
712	adj_msg_time(msg, &otime);
713
714	to.tsp_type = TSP_SETDATEREQ;
715	to.tsp_time = msg->tsp_time;
716	(void)strncpy(to.tsp_name, hostname, sizeof to.tsp_name-1);
717	to.tsp_name[sizeof to.tsp_name-1] = '\0';
718	if (!acksend(&to, &slavenet->dest_addr,
719		     ANYADDR, TSP_DATEACK,
720		     slavenet, 0))
721		return;			/* no answer */
722
723	xmit(TSP_DATEACK, seq, &taddr);
724}
725
726
727/*
728 * Used before answering a broadcast message to avoid network
729 * contention and likely collisions.
730 */
731static void
732answerdelay()
733{
734#ifdef sgi
735	sginap(delay1);
736#else
737	struct timeval timeout;
738
739	timeout.tv_sec = 0;
740	timeout.tv_usec = delay1;
741
742	(void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
743	    &timeout);
744	return;
745#endif /* sgi */
746}
747