MFC r1.13 (HEAD):
[dragonfly.git] / usr.sbin / timed / timed / slave.c
blobecdd74f244e8a87005154beb24c82037fd1db6f1
1 /*-
2 * Copyright (c) 1985, 1993
3 * The Regents of the University of California. All rights reserved.
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.
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.
33 * @(#)slave.c 8.1 (Berkeley) 6/6/93
34 * $FreeBSD: src/usr.sbin/timed/timed/slave.c,v 1.7 1999/08/28 01:20:18 peter Exp $
35 * $DragonFly: src/usr.sbin/timed/timed/slave.c,v 1.9 2004/09/05 02:20:15 dillon Exp $
38 #include "globals.h"
39 #include <setjmp.h>
40 #include "pathnames.h"
42 extern jmp_buf jmpenv;
43 extern int Mflag;
44 extern int justquit;
46 extern u_short sequence;
48 static char master_name[MAXHOSTNAMELEN];
49 static struct netinfo *old_slavenet;
50 static int old_status;
52 static void schgdate(struct tsp *, char *);
53 static void setmaster(struct tsp *);
54 static void answerdelay(void);
56 int
57 slave(void)
59 int tries;
60 long electiontime, refusetime, looktime, looptime, adjusttime;
61 u_short seq;
62 long fastelection;
63 #define FASTTOUT 3
64 struct in_addr cadr;
65 struct timeval otime;
66 struct sockaddr_in taddr;
67 char tname[MAXHOSTNAMELEN];
68 struct tsp *msg, to;
69 struct timeval ntime, wait;
70 time_t tsp_time_sec;
71 struct tsp *answer;
72 char olddate[32];
73 char newdate[32];
74 struct netinfo *ntp;
75 struct hosttbl *htp;
78 old_slavenet = 0;
79 seq = 0;
80 refusetime = 0;
81 adjusttime = 0;
83 gettimeofday(&ntime, 0);
84 electiontime = ntime.tv_sec + delay2;
85 fastelection = ntime.tv_sec + FASTTOUT;
86 if (justquit)
87 looktime = electiontime;
88 else
89 looktime = fastelection;
90 looptime = fastelection;
92 if (slavenet)
93 xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
94 if (status & MASTER) {
95 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
96 if (ntp->status == MASTER)
97 masterup(ntp);
101 loop:
102 get_goodgroup(0);
103 gettimeofday(&ntime, (struct timezone *)0);
104 if (ntime.tv_sec > electiontime) {
105 if (trace)
106 fprintf(fd, "election timer expired\n");
107 longjmp(jmpenv, 1);
110 if (ntime.tv_sec >= looktime) {
111 if (trace)
112 fprintf(fd, "Looking for nets to master\n");
114 if (Mflag && nignorednets > 0) {
115 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
116 if (ntp->status == IGNORE
117 || ntp->status == NOMASTER) {
118 lookformaster(ntp);
119 if (ntp->status == MASTER) {
120 masterup(ntp);
121 } else if (ntp->status == MASTER) {
122 ntp->status = NOMASTER;
125 if (ntp->status == MASTER
126 && --ntp->quit_count < 0)
127 ntp->quit_count = 0;
129 makeslave(slavenet); /* prune extras */
130 setstatus();
132 gettimeofday(&ntime, 0);
133 looktime = ntime.tv_sec + delay2;
135 if (ntime.tv_sec >= looptime) {
136 if (trace)
137 fprintf(fd, "Looking for loops\n");
138 for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
139 if (ntp->status == MASTER) {
140 to.tsp_type = TSP_LOOP;
141 to.tsp_vers = TSPVERSION;
142 to.tsp_seq = sequence++;
143 to.tsp_hopcnt = MAX_HOPCNT;
144 strlcpy(to.tsp_name, hostname, sizeof(to.tsp_name));
145 bytenetorder(&to);
146 if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
147 (struct sockaddr*)&ntp->dest_addr,
148 sizeof(ntp->dest_addr)) < 0) {
149 trace_sendto_err(ntp->dest_addr.sin_addr);
153 gettimeofday(&ntime, 0);
154 looptime = ntime.tv_sec + delay2;
157 wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
158 if (wait.tv_sec < 0)
159 wait.tv_sec = 0;
160 wait.tv_sec += FASTTOUT;
161 wait.tv_usec = 0;
162 msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
164 if (msg != NULL) {
166 * filter stuff not for us
168 switch (msg->tsp_type) {
169 case TSP_SETDATE:
170 case TSP_TRACEOFF:
171 case TSP_TRACEON:
173 * XXX check to see they are from ourself
175 break;
177 case TSP_TEST:
178 case TSP_MSITE:
179 break;
181 case TSP_MASTERUP:
182 if (!fromnet) {
183 if (trace) {
184 fprintf(fd, "slave ignored: ");
185 print(msg, &from);
187 goto loop;
189 break;
191 default:
192 if (!fromnet
193 || fromnet->status == IGNORE
194 || fromnet->status == NOMASTER) {
195 if (trace) {
196 fprintf(fd, "slave ignored: ");
197 print(msg, &from);
199 goto loop;
201 break;
206 * now process the message
208 switch (msg->tsp_type) {
210 case TSP_ADJTIME:
211 if (fromnet != slavenet)
212 break;
213 if (!good_host_name(msg->tsp_name)) {
214 syslog(LOG_NOTICE,
215 "attempted time adjustment by %s",
216 msg->tsp_name);
217 suppress(&from, msg->tsp_name, fromnet);
218 break;
221 * Speed up loop detection in case we have a loop.
222 * Otherwise the clocks can race until the loop
223 * is found.
225 gettimeofday(&otime, 0);
226 if (adjusttime < otime.tv_sec)
227 looptime -= (looptime-otime.tv_sec)/2 + 1;
229 setmaster(msg);
230 if (seq != msg->tsp_seq) {
231 seq = msg->tsp_seq;
232 synch(tvtomsround(msg->tsp_time));
234 gettimeofday(&ntime, 0);
235 electiontime = ntime.tv_sec + delay2;
236 fastelection = ntime.tv_sec + FASTTOUT;
237 adjusttime = ntime.tv_sec + SAMPLEINTVL*2;
238 break;
240 case TSP_SETTIME:
241 if (fromnet != slavenet)
242 break;
243 if (seq == msg->tsp_seq)
244 break;
245 seq = msg->tsp_seq;
247 /* adjust time for residence on the queue */
248 gettimeofday(&otime, 0);
249 adj_msg_time(msg,&otime);
252 * the following line is necessary due to syslog
253 * calling ctime() which clobbers the static buffer
255 strlcpy(olddate, date(), sizeof(olddate));
256 tsp_time_sec = msg->tsp_time.tv_sec;
257 strlcpy(newdate, ctime(&tsp_time_sec), sizeof(newdate));
259 if (!good_host_name(msg->tsp_name)) {
260 syslog(LOG_NOTICE,
261 "attempted time setting by untrusted %s to %s",
262 msg->tsp_name, newdate);
263 suppress(&from, msg->tsp_name, fromnet);
264 break;
267 setmaster(msg);
268 timevalsub(&ntime, &msg->tsp_time, &otime);
269 if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
271 * do not change the clock if we can adjust it
273 synch(tvtomsround(ntime));
274 } else {
275 logwtmp("|", "date", "");
276 settimeofday(&msg->tsp_time, 0);
277 logwtmp("{", "date", "");
278 syslog(LOG_NOTICE,
279 "date changed by %s from %s",
280 msg->tsp_name, olddate);
281 if (status & MASTER)
282 spreadtime();
284 gettimeofday(&ntime, 0);
285 electiontime = ntime.tv_sec + delay2;
286 fastelection = ntime.tv_sec + FASTTOUT;
288 /* This patches a bad protocol bug. Imagine a system with several networks,
289 * where there are a pair of redundant gateways between a pair of networks,
290 * each running timed. Assume that we start with a third machine mastering
291 * one of the networks, and one of the gateways mastering the other.
292 * Imagine that the third machine goes away and the non-master gateway
293 * decides to replace it. If things are timed just 'right,' we will have
294 * each gateway mastering one network for a little while. If a SETTIME
295 * message gets into the network at that time, perhaps from the newly
296 * masterful gateway as it was taking control, the SETTIME will loop
297 * forever. Each time a gateway receives it on its slave side, it will
298 * call spreadtime to forward it on its mastered network. We are now in
299 * a permanent loop, since the SETTIME msgs will keep any clock
300 * in the network from advancing. Normally, the 'LOOP' stuff will detect
301 * and correct the situation. However, with the clocks stopped, the
302 * 'looptime' timer cannot expire. While they are in this state, the
303 * masters will try to saturate the network with SETTIME packets.
305 looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
306 break;
308 case TSP_MASTERUP:
309 if (slavenet && fromnet != slavenet)
310 break;
311 if (!good_host_name(msg->tsp_name)) {
312 suppress(&from, msg->tsp_name, fromnet);
313 if (electiontime > fastelection)
314 electiontime = fastelection;
315 break;
317 makeslave(fromnet);
318 setmaster(msg);
319 setstatus();
320 answerdelay();
321 xmit(TSP_SLAVEUP, 0, &from);
322 gettimeofday(&ntime, 0);
323 electiontime = ntime.tv_sec + delay2;
324 fastelection = ntime.tv_sec + FASTTOUT;
325 refusetime = 0;
326 break;
328 case TSP_MASTERREQ:
329 if (fromnet->status != SLAVE)
330 break;
331 gettimeofday(&ntime, 0);
332 electiontime = ntime.tv_sec + delay2;
333 break;
335 case TSP_SETDATE:
336 tsp_time_sec = msg->tsp_time.tv_sec;
337 strlcpy(newdate, ctime(&tsp_time_sec), sizeof(newdate));
338 schgdate(msg, newdate);
339 break;
341 case TSP_SETDATEREQ:
342 if (fromnet->status != MASTER)
343 break;
344 tsp_time_sec = msg->tsp_time.tv_sec;
345 strlcpy(newdate, ctime(&tsp_time_sec), sizeof(newdate));
346 htp = findhost(msg->tsp_name);
347 if (0 == htp) {
348 syslog(LOG_WARNING,
349 "DATEREQ from uncontrolled machine");
350 break;
352 if (!htp->good) {
353 syslog(LOG_WARNING,
354 "attempted date change by untrusted %s to %s",
355 htp->name, newdate);
356 spreadtime();
357 break;
359 schgdate(msg, newdate);
360 break;
362 case TSP_TRACEON:
363 traceon();
364 break;
366 case TSP_TRACEOFF:
367 traceoff("Tracing ended at %s\n");
368 break;
370 case TSP_SLAVEUP:
371 newslave(msg);
372 break;
374 case TSP_ELECTION:
375 if (fromnet->status == SLAVE) {
376 gettimeofday(&ntime, 0);
377 electiontime = ntime.tv_sec + delay2;
378 fastelection = ntime.tv_sec + FASTTOUT;
379 seq = 0;
380 if (!good_host_name(msg->tsp_name)) {
381 syslog(LOG_NOTICE,
382 "suppress election of %s",
383 msg->tsp_name);
384 to.tsp_type = TSP_QUIT;
385 electiontime = fastelection;
386 } else if (cadr.s_addr != from.sin_addr.s_addr
387 && ntime.tv_sec < refusetime) {
388 /* if the candidate has to repeat itself, the old code would refuse it
389 * the second time. That would prevent elections.
391 to.tsp_type = TSP_REFUSE;
392 } else {
393 cadr.s_addr = from.sin_addr.s_addr;
394 to.tsp_type = TSP_ACCEPT;
395 refusetime = ntime.tv_sec + 30;
397 taddr = from;
398 strlcpy(tname, msg->tsp_name, sizeof(tname));
399 strlcpy(to.tsp_name, hostname,
400 sizeof(to.tsp_name));
401 answerdelay();
402 if (!acksend(&to, &taddr, tname,
403 TSP_ACK, 0, 0))
404 syslog(LOG_WARNING,
405 "no answer from candidate %s\n",
406 tname);
408 } else { /* fromnet->status == MASTER */
409 htp = addmach(msg->tsp_name, &from,fromnet);
410 to.tsp_type = TSP_QUIT;
411 strlcpy(to.tsp_name, hostname,
412 sizeof(to.tsp_name));
413 if (!acksend(&to, &htp->addr, htp->name,
414 TSP_ACK, 0, htp->noanswer)) {
415 syslog(LOG_ERR,
416 "no reply from %s to ELECTION-QUIT",
417 htp->name);
418 remmach(htp);
421 break;
423 case TSP_CONFLICT:
424 if (fromnet->status != MASTER)
425 break;
427 * After a network partition, there can be
428 * more than one master: the first slave to
429 * come up will notify here the situation.
431 strlcpy(to.tsp_name, hostname, sizeof(to.tsp_name));
433 /* The other master often gets into the same state,
434 * with boring results.
436 ntp = fromnet; /* (acksend() can leave fromnet=0 */
437 for (tries = 0; tries < 3; tries++) {
438 to.tsp_type = TSP_RESOLVE;
439 answer = acksend(&to, &ntp->dest_addr,
440 ANYADDR, TSP_MASTERACK,
441 ntp, 0);
442 if (answer == NULL)
443 break;
444 htp = addmach(answer->tsp_name,&from,ntp);
445 to.tsp_type = TSP_QUIT;
446 answer = acksend(&to, &htp->addr, htp->name,
447 TSP_ACK, 0, htp->noanswer);
448 if (!answer) {
449 syslog(LOG_WARNING,
450 "conflict error: no reply from %s to QUIT",
451 htp->name);
452 remmach(htp);
455 masterup(ntp);
456 break;
458 case TSP_MSITE:
459 if (!slavenet)
460 break;
461 taddr = from;
462 to.tsp_type = TSP_MSITEREQ;
463 to.tsp_vers = TSPVERSION;
464 to.tsp_seq = 0;
465 strlcpy(to.tsp_name, hostname, sizeof(to.tsp_name));
466 answer = acksend(&to, &slavenet->dest_addr,
467 ANYADDR, TSP_ACK,
468 slavenet, 0);
469 if (answer != NULL
470 && good_host_name(answer->tsp_name)) {
471 setmaster(answer);
472 to.tsp_type = TSP_ACK;
473 strlcpy(to.tsp_name, answer->tsp_name,
474 sizeof(to.tsp_name));
475 bytenetorder(&to);
476 if (sendto(sock, (char *)&to,
477 sizeof(struct tsp), 0,
478 (struct sockaddr*)&taddr,
479 sizeof(taddr)) < 0) {
480 trace_sendto_err(taddr.sin_addr);
483 break;
485 case TSP_MSITEREQ:
486 break;
488 case TSP_ACCEPT:
489 case TSP_REFUSE:
490 case TSP_RESOLVE:
491 break;
493 case TSP_QUIT:
494 doquit(msg); /* become a slave */
495 break;
497 case TSP_TEST:
498 electiontime = 0;
499 break;
501 case TSP_LOOP:
502 /* looking for loops of masters */
503 if (!(status & MASTER))
504 break;
505 if (fromnet->status == SLAVE) {
506 if (!strcmp(msg->tsp_name, hostname)) {
508 * Someone forwarded our message back to
509 * us. There must be a loop. Tell the
510 * master of this network to quit.
512 * The other master often gets into
513 * the same state, with boring results.
515 ntp = fromnet;
516 for (tries = 0; tries < 3; tries++) {
517 to.tsp_type = TSP_RESOLVE;
518 answer = acksend(&to, &ntp->dest_addr,
519 ANYADDR, TSP_MASTERACK,
520 ntp,0);
521 if (answer == NULL)
522 break;
523 taddr = from;
524 strlcpy(tname, answer->tsp_name,
525 sizeof(tname));
526 to.tsp_type = TSP_QUIT;
527 strlcpy(to.tsp_name, hostname,
528 sizeof(to.tsp_name));
529 if (!acksend(&to, &taddr, tname,
530 TSP_ACK, 0, 1)) {
531 syslog(LOG_ERR,
532 "no reply from %s to slave LOOP-QUIT",
533 tname);
534 } else {
535 electiontime = 0;
538 gettimeofday(&ntime, 0);
539 looptime = ntime.tv_sec + FASTTOUT;
540 } else {
541 if (msg->tsp_hopcnt-- < 1)
542 break;
543 bytenetorder(msg);
544 for (ntp = nettab; ntp != 0; ntp = ntp->next) {
545 if (ntp->status == MASTER
546 && 0 > sendto(sock, (char *)msg,
547 sizeof(struct tsp), 0,
548 (struct sockaddr*)&ntp->dest_addr,
549 sizeof(ntp->dest_addr)))
550 trace_sendto_err(ntp->dest_addr.sin_addr);
553 } else { /* fromnet->status == MASTER */
555 * We should not have received this from a net
556 * we are master on. There must be two masters,
557 * unless the packet was really from us.
559 if (from.sin_addr.s_addr
560 == fromnet->my_addr.s_addr) {
561 if (trace)
562 fprintf(fd,"discarding forwarded LOOP\n");
563 break;
567 * The other master often gets into the same
568 * state, with boring results.
570 ntp = fromnet;
571 for (tries = 0; tries < 3; tries++) {
572 to.tsp_type = TSP_RESOLVE;
573 answer = acksend(&to, &ntp->dest_addr,
574 ANYADDR, TSP_MASTERACK,
575 ntp,0);
576 if (!answer)
577 break;
578 htp = addmach(answer->tsp_name,
579 &from,ntp);
580 to.tsp_type = TSP_QUIT;
581 strlcpy(to.tsp_name, hostname,
582 sizeof(to.tsp_name));
583 if (!acksend(&to,&htp->addr,htp->name,
584 TSP_ACK, 0, htp->noanswer)) {
585 syslog(LOG_ERR,
586 "no reply from %s to master LOOP-QUIT",
587 htp->name);
588 remmach(htp);
591 gettimeofday(&ntime, 0);
592 looptime = ntime.tv_sec + FASTTOUT;
594 break;
595 default:
596 if (trace) {
597 fprintf(fd, "garbage message: ");
598 print(msg, &from);
600 break;
603 goto loop;
608 * tell the world who our master is
610 static void
611 setmaster(struct tsp *msg)
614 if (slavenet
615 && (slavenet != old_slavenet
616 || strcmp(msg->tsp_name, master_name)
617 || old_status != status)) {
618 strlcpy(master_name, msg->tsp_name, sizeof(master_name));
619 old_slavenet = slavenet;
620 old_status = status;
622 if (status & MASTER) {
623 syslog(LOG_NOTICE, "submaster to %s", master_name);
624 if (trace)
625 fprintf(fd, "submaster to %s\n", master_name);
627 } else {
628 syslog(LOG_NOTICE, "slave to %s", master_name);
629 if (trace)
630 fprintf(fd, "slave to %s\n", master_name);
638 * handle date change request on a slave
640 static void
641 schgdate(struct tsp *msg, char *newdate)
643 struct tsp to;
644 u_short seq;
645 struct sockaddr_in taddr;
646 struct timeval otime;
648 if (!slavenet)
649 return; /* no where to forward */
651 taddr = from;
652 seq = msg->tsp_seq;
654 syslog(LOG_INFO,
655 "forwarding date change by %s to %s",
656 msg->tsp_name, newdate);
658 /* adjust time for residence on the queue */
659 gettimeofday(&otime, 0);
660 adj_msg_time(msg, &otime);
662 to.tsp_type = TSP_SETDATEREQ;
663 to.tsp_time = msg->tsp_time;
664 strlcpy(to.tsp_name, hostname, sizeof(to.tsp_name));
665 if (!acksend(&to, &slavenet->dest_addr,
666 ANYADDR, TSP_DATEACK,
667 slavenet, 0))
668 return; /* no answer */
670 xmit(TSP_DATEACK, seq, &taddr);
675 * Used before answering a broadcast message to avoid network
676 * contention and likely collisions.
678 static void
679 answerdelay(void)
682 struct timeval timeout;
684 timeout.tv_sec = 0;
685 timeout.tv_usec = delay1;
687 select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
688 &timeout);