]>
git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/master.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1985, 1993
27 * The Regents of the University of California. All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 static char sccsid
[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
63 #ident "$Revision: 1.1.1.1 $"
68 #include <sys/types.h>
69 #include <sys/times.h>
72 #include <sys/schedctl.h>
75 #include "pathnames.h"
77 extern int measure_delta
;
78 extern jmp_buf jmpenv
;
83 static int slvcount
; /* slaves listening to our clock */
85 static void mchgdate
__P((struct tsp
*));
88 extern void logwtmp
__P((struct timeval
*, struct timeval
*));
90 extern void logwtmp
__P((char *, char *, char *));
94 * The main function of `master' is to periodically compute the differences
95 * (deltas) between its clock and the clocks of the slaves, to compute the
96 * network average delta, and to send to the slaves the differences between
97 * their individual deltas and the network delta.
98 * While waiting, it receives messages from the slaves (i.e. requests for
99 * master's name, remote requests to set the network time, ...), and
100 * takes the appropriate action.
109 struct timeval wait
, ntime
;
110 struct tsp
*msg
, *answer
, to
;
112 struct sockaddr_in taddr
;
113 char tname
[MAXHOSTNAMELEN
];
117 syslog(LOG_NOTICE
, "This machine is master");
119 fprintf(fd
, "This machine is master\n");
120 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
121 if (ntp
->status
== MASTER
)
124 (void)gettimeofday(&ntime
, 0);
125 pollingtime
= ntime
.tv_sec
+3;
131 /* Process all outstanding messages before spending the long time necessary
132 * to update all timers.
135 (void)gettimeofday(&ntime
, 0);
136 wait
.tv_sec
= pollingtime
- ntime
.tv_sec
;
140 msg
= readmsg(TSP_ANY
, ANYADDR
, &wait
, 0);
142 (void)gettimeofday(&ntime
, 0);
143 if (ntime
.tv_sec
>= pollingtime
) {
144 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
147 /* If a bogus master told us to quit, we can have decided to ignore a
148 * network. Therefore, periodically try to take over everything.
150 polls
= (polls
+ 1) % POLLRATE
;
151 if (0 == polls
&& nignorednets
> 0) {
152 trace_msg("Looking for nets to re-master\n");
153 for (ntp
= nettab
; ntp
; ntp
= ntp
->next
) {
154 if (ntp
->status
== IGNORE
155 || ntp
->status
== NOMASTER
) {
157 if (ntp
->status
== MASTER
) {
162 if (ntp
->status
== MASTER
163 && --ntp
->quit_count
< 0)
172 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
173 to
.tsp_type
= TSP_LOOP
;
174 to
.tsp_vers
= TSPVERSION
;
175 to
.tsp_seq
= sequence
++;
176 to
.tsp_hopcnt
= MAX_HOPCNT
;
177 (void)strcpy(to
.tsp_name
, hostname
);
179 if (sendto(sock
, (char *)&to
,
180 sizeof(struct tsp
), 0,
181 (struct sockaddr
*)&ntp
->dest_addr
,
182 sizeof(ntp
->dest_addr
)) < 0) {
183 trace_sendto_err(ntp
->dest_addr
.sin_addr
);
190 switch (msg
->tsp_type
) {
201 * XXX check to see it is from ourself
204 (void)cftime(newdate
, "%D %T", &msg
->tsp_time
.tv_sec
);
206 (void)strcpy(newdate
, ctime(&msg
->tsp_time
.tv_sec
));
208 if (!good_host_name(msg
->tsp_name
)) {
210 "attempted date change by %s to %s",
211 msg
->tsp_name
, newdate
);
217 (void)gettimeofday(&ntime
, 0);
218 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
222 if (!fromnet
|| fromnet
->status
!= MASTER
)
225 (void)cftime(newdate
, "%D %T", &msg
->tsp_time
.tv_sec
);
227 (void)strcpy(newdate
, ctime(&msg
->tsp_time
.tv_sec
));
229 htp
= findhost(msg
->tsp_name
);
232 "attempted SET DATEREQ by uncontrolled %s to %s",
233 msg
->tsp_name
, newdate
);
236 if (htp
->seq
== msg
->tsp_seq
)
238 htp
->seq
= msg
->tsp_seq
;
241 "attempted SET DATEREQ by untrusted %s to %s",
242 msg
->tsp_name
, newdate
);
248 (void)gettimeofday(&ntime
, 0);
249 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
253 xmit(TSP_ACK
, msg
->tsp_seq
, &from
);
264 traceoff("Tracing ended at %s\n");
270 if (fromnet
->status
== MASTER
) {
272 (void)addmach(msg
->tsp_name
, &from
,fromnet
);
275 (void)strcpy(tname
, msg
->tsp_name
);
276 to
.tsp_type
= TSP_QUIT
;
277 (void)strcpy(to
.tsp_name
, hostname
);
278 answer
= acksend(&to
, &taddr
, tname
,
280 if (answer
== NULL
) {
281 syslog(LOG_ERR
, "election error by %s",
288 * After a network partition, there can be
289 * more than one master: the first slave to
290 * come up will notify here the situation.
292 if (!fromnet
|| fromnet
->status
!= MASTER
)
294 (void)strcpy(to
.tsp_name
, hostname
);
296 /* The other master often gets into the same state,
297 * with boring results if we stay at it forever.
299 ntp
= fromnet
; /* (acksend() can leave fromnet=0 */
300 for (i
= 0; i
< 3; i
++) {
301 to
.tsp_type
= TSP_RESOLVE
;
302 (void)strcpy(to
.tsp_name
, hostname
);
303 answer
= acksend(&to
, &ntp
->dest_addr
,
304 ANYADDR
, TSP_MASTERACK
,
308 htp
= addmach(answer
->tsp_name
,&from
,ntp
);
309 to
.tsp_type
= TSP_QUIT
;
310 msg
= acksend(&to
, &htp
->addr
, htp
->name
,
311 TSP_ACK
, 0, htp
->noanswer
);
314 "no response from %s to CONFLICT-QUIT",
323 if (!fromnet
|| fromnet
->status
!= MASTER
)
326 * do not want to call synch() while waiting
329 (void)gettimeofday(&ntime
, (struct timezone
*)0);
330 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
334 doquit(msg
); /* become a slave */
338 if (!fromnet
|| fromnet
->status
!= MASTER
339 || !strcmp(msg
->tsp_name
, hostname
))
342 * We should not have received this from a net
343 * we are master on. There must be two masters.
345 htp
= addmach(msg
->tsp_name
, &from
,fromnet
);
346 to
.tsp_type
= TSP_QUIT
;
347 (void)strcpy(to
.tsp_name
, hostname
);
348 answer
= acksend(&to
, &htp
->addr
, htp
->name
,
352 "loop breakage: no reply from %s=%s to QUIT",
353 htp
->name
, inet_ntoa(htp
->addr
.sin_addr
));
360 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
361 nnets
, nmasternets
, nslavenets
, nignorednets
);
370 fprintf(fd
, "garbage message: ");
381 * change the system date on the master
387 char tname
[MAXHOSTNAMELEN
];
389 struct timeval otime
, ntime
;
391 (void)strcpy(tname
, msg
->tsp_name
);
393 xmit(TSP_DATEACK
, msg
->tsp_seq
, &from
);
395 (void)strcpy(olddate
, date());
397 /* adjust time for residence on the queue */
398 (void)gettimeofday(&otime
, 0);
399 adj_msg_time(msg
,&otime
);
401 timevalsub(&ntime
, &msg
->tsp_time
, &otime
);
402 if (ntime
.tv_sec
< MAXADJ
&& ntime
.tv_sec
> -MAXADJ
) {
404 * do not change the clock if we can adjust it
407 synch(tvtomsround(ntime
));
410 if (0 > settimeofday(&msg
->tsp_time
, 0)) {
411 syslog(LOG_ERR
, "settimeofday(): %m");
413 logwtmp(&otime
, &msg
->tsp_time
);
415 logwtmp("|", "date", "");
416 (void)settimeofday(&msg
->tsp_time
, 0);
417 logwtmp("}", "date", "");
422 syslog(LOG_NOTICE
, "date changed by %s from %s",
428 * synchronize all of the slaves
436 struct timeval check
, stop
, wait
;
443 fprintf(fd
, "measurements starting at %s\n", date());
444 (void)gettimeofday(&check
, 0);
446 /* run fast to get good time */
447 pri
= schedctl(NDPRI
,0,NDPHIMIN
);
449 syslog(LOG_ERR
, "schedctl(): %m");
451 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
452 if (htp
->noanswer
!= 0) {
453 measure_status
= measure(500, 100,
457 measure_status
= measure(3000, 100,
461 if (measure_status
!= GOOD
) {
462 /* The slave did not respond. We have
463 * just wasted lots of time on it.
465 htp
->delta
= HOSTDOWN
;
466 if (++htp
->noanswer
>= LOSTHOST
) {
469 "purging %s for not answering ICMP\n",
476 htp
->delta
= measure_delta
;
478 (void)gettimeofday(&stop
, 0);
479 timevalsub(&stop
, &stop
, &check
);
480 if (stop
.tv_sec
>= 1) {
484 * ack messages periodically
488 if (0 != readmsg(TSP_TRACEON
,ANYADDR
,
491 (void)gettimeofday(&check
, 0);
496 (void)schedctl(NDPRI
,0,pri
);
499 fprintf(fd
, "measurements finished at %s\n", date());
501 if (!(status
& SLAVE
)) {
503 mydelta
= networkdelta();
508 if (trace
&& (mydelta
!= 0 || (status
& SLAVE
)))
509 fprintf(fd
,"local correction of %ld ms.\n", mydelta
);
514 * sends the time to each slave after the master
515 * has received the command to set the network time
524 /* Do not listen to the consensus after forcing the time. This is because
525 * the consensus takes a while to reach the time we are dictating.
528 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
529 to
.tsp_type
= TSP_SETTIME
;
530 (void)strcpy(to
.tsp_name
, hostname
);
531 (void)gettimeofday(&to
.tsp_time
, 0);
532 answer
= acksend(&to
, &htp
->addr
, htp
->name
,
533 TSP_ACK
, 0, htp
->noanswer
);
535 /* We client does not respond, then we have
536 * just wasted lots of time on it.
539 "no reply to SETTIME from %s", htp
->name
);
540 if (++htp
->noanswer
>= LOSTHOST
) {
543 "purging %s for not answering",
557 static time_t next_time
;
564 if (!fd
) /* quit if tracing already off */
567 this_time
= times(&tm
);
568 if (this_time
+ delta
< next_time
)
570 next_time
= this_time
+ CLK_TCK
;
572 fprintf(fd
, "host table: %d entries at %s\n", slvcount
, date());
575 for (i
= 1; i
<= slvcount
; i
++, htp
= htp
->l_fwd
) {
576 l
= strlen(htp
->name
) + 1;
577 if (length
+l
>= 80) {
582 fprintf(fd
, " %s", htp
->name
);
588 static struct hosttbl
*newhost_hash
;
589 static struct hosttbl
*lasthfree
= &hosttbl
[0];
592 struct hosttbl
* /* answer or 0 */
601 for (p
= name
, i
= 0; i
< 8 && *p
!= '\0'; i
++, p
++)
603 newhost_hash
= &hosttbl
[j
% NHOSTS
];
606 if (htp
->name
[0] == '\0')
609 if (!strcmp(name
, htp
->name
))
612 } while (htp
!= newhost_hash
);
617 * add a host to the list of controlled machines if not already there
620 addmach(name
, addr
, ntp
)
622 struct sockaddr_in
*addr
;
625 struct hosttbl
*ret
, *p
, *b
, *f
;
627 ret
= findhost(name
);
629 if (slvcount
>= NHOSTS
) {
631 fprintf(fd
, "no more slots in host table\n");
634 syslog(LOG_ERR
, "no more slots in host table");
636 longjmp(jmpenv
, 2); /* give up and be a slave */
639 /* if our home hash slot is occupied, find a free entry
642 if (newhost_hash
->name
[0] != '\0') {
645 if (++lasthfree
> &hosttbl
[NHOSTS
])
646 lasthfree
= &hosttbl
[1];
647 } while (ret
->name
[0] != '\0');
649 if (!newhost_hash
->head
) {
650 /* Move an interloper using our home. Use
651 * scratch pointers in case the new head is
652 * pointing to itself.
654 f
= newhost_hash
->h_fwd
;
655 b
= newhost_hash
->h_bak
;
658 f
= newhost_hash
->l_fwd
;
659 b
= newhost_hash
->l_bak
;
662 bcopy(newhost_hash
,ret
,sizeof(*ret
));
668 /* link to an existing chain in our home
671 p
= newhost_hash
->h_bak
;
672 ret
->h_fwd
= newhost_hash
;
675 newhost_hash
->h_bak
= ret
;
685 (void)strncpy(ret
->name
, name
, sizeof(ret
->name
));
686 ret
->good
= good_host_name(name
);
688 ret
->l_bak
= self
.l_bak
;
689 self
.l_bak
->l_fwd
= ret
;
697 ret
->noanswer
= (ret
->noanswer
!= 0);
700 /* need to clear sequence number anyhow */
706 * remove the machine with the given index in the host table.
712 struct hosttbl
*lprv
, *hnxt
, *f
, *b
;
715 fprintf(fd
, "remove %s\n", htp
->name
);
717 /* get out of the lists */
718 htp
->l_fwd
->l_bak
= lprv
= htp
->l_bak
;
719 htp
->l_bak
->l_fwd
= htp
->l_fwd
;
720 htp
->h_fwd
->h_bak
= htp
->h_bak
;
721 htp
->h_bak
->h_fwd
= hnxt
= htp
->h_fwd
;
723 /* If we are in the home slot, pull up the chain */
724 if (htp
->head
&& hnxt
!= htp
) {
728 /* Use scratch pointers in case the new head is pointing to
740 bcopy(hnxt
, htp
, sizeof(*htp
));
746 lasthfree
->name
[0] = '\0';
747 lasthfree
->h_fwd
= 0;
748 lasthfree
->l_fwd
= 0;
756 * Remove all the machines from the host table that exist on the given
757 * network. This is called when a master transitions to a slave on a
768 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
780 xmit(TSP_MASTERUP
, 0, &net
->dest_addr
);
783 * Do not tell new slaves our time for a while. This ensures
784 * we do not tell them to start using our time, before we have
785 * found a good master.
787 (void)gettimeofday(&net
->slvwait
, 0);
795 struct tsp
*answer
, to
;
798 if (!fromnet
|| fromnet
->status
!= MASTER
)
801 htp
= addmach(msg
->tsp_name
, &from
,fromnet
);
802 htp
->seq
= msg
->tsp_seq
;
807 * If we are stable, send our time to the slave.
808 * Do not go crazy if the date has been changed.
810 (void)gettimeofday(&now
, 0);
811 if (now
.tv_sec
>= fromnet
->slvwait
.tv_sec
+3
812 || now
.tv_sec
< fromnet
->slvwait
.tv_sec
) {
813 to
.tsp_type
= TSP_SETTIME
;
814 (void)strcpy(to
.tsp_name
, hostname
);
815 (void)gettimeofday(&to
.tsp_time
, 0);
816 answer
= acksend(&to
, &htp
->addr
,
823 "no reply to initial SETTIME from %s",
825 htp
->noanswer
= LOSTHOST
;
832 * react to a TSP_QUIT:
838 if (fromnet
->status
== MASTER
) {
839 if (!good_host_name(msg
->tsp_name
)) {
840 if (fromnet
->quit_count
<= 0) {
841 syslog(LOG_NOTICE
,"untrusted %s told us QUIT",
843 suppress(&from
, msg
->tsp_name
, fromnet
);
844 fromnet
->quit_count
= 1;
847 syslog(LOG_NOTICE
, "untrusted %s told us QUIT twice",
849 fromnet
->quit_count
= 2;
850 fromnet
->status
= NOMASTER
;
852 fromnet
->status
= SLAVE
;
855 longjmp(jmpenv
, 2); /* give up and be a slave */
858 if (!good_host_name(msg
->tsp_name
)) {
859 syslog(LOG_NOTICE
, "untrusted %s told us QUIT",
861 fromnet
->quit_count
= 2;
870 fd
= fopen(_PATH_TIMEDLOG
, "w");
875 fprintf(fd
,"Tracing started at %s\n", date());
892 fprintf(fd
, msg
, date());
907 logwtmp(otime
, ntime
)
908 struct timeval
*otime
, *ntime
;
910 static struct utmp wtmp
[2] = {
911 {"","",OTIME_MSG
,0,OLD_TIME
,0,0,0},
912 {"","",NTIME_MSG
,0,NEW_TIME
,0,0,0}
914 static char *wtmpfile
= WTMP_FILE
;
917 wtmp
[0].ut_time
= otime
->tv_sec
+ (otime
->tv_usec
+ 500000) / 1000000;
918 wtmp
[1].ut_time
= ntime
->tv_sec
+ (ntime
->tv_usec
+ 500000) / 1000000;
919 if (wtmp
[0].ut_time
== wtmp
[1].ut_time
)
923 (void)pututline(&wtmp
[0]);
924 (void)pututline(&wtmp
[1]);
926 if ((f
= open(wtmpfile
, O_WRONLY
|O_APPEND
)) >= 0) {
927 (void) write(f
, (char *)wtmp
, sizeof(wtmp
));