]>
git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/master.c
f4dbd7d0fa254ac03fa16b083b8dc43bbdf3bdd0
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1985, 1993
26 * The Regents of the University of California. All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 static char sccsid
[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
62 #ident "$Revision: 1.1 $"
67 #include <sys/types.h>
68 #include <sys/times.h>
71 #include <sys/schedctl.h>
74 #include "pathnames.h"
76 extern int measure_delta
;
77 extern jmp_buf jmpenv
;
82 static int slvcount
; /* slaves listening to our clock */
84 static void mchgdate
__P((struct tsp
*));
87 extern void logwtmp
__P((struct timeval
*, struct timeval
*));
89 extern void logwtmp
__P((char *, char *, char *));
93 * The main function of `master' is to periodically compute the differences
94 * (deltas) between its clock and the clocks of the slaves, to compute the
95 * network average delta, and to send to the slaves the differences between
96 * their individual deltas and the network delta.
97 * While waiting, it receives messages from the slaves (i.e. requests for
98 * master's name, remote requests to set the network time, ...), and
99 * takes the appropriate action.
108 struct timeval wait
, ntime
;
109 struct tsp
*msg
, *answer
, to
;
111 struct sockaddr_in taddr
;
112 char tname
[MAXHOSTNAMELEN
];
116 syslog(LOG_NOTICE
, "This machine is master");
118 fprintf(fd
, "This machine is master\n");
119 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
120 if (ntp
->status
== MASTER
)
123 (void)gettimeofday(&ntime
, 0);
124 pollingtime
= ntime
.tv_sec
+3;
130 /* Process all outstanding messages before spending the long time necessary
131 * to update all timers.
134 (void)gettimeofday(&ntime
, 0);
135 wait
.tv_sec
= pollingtime
- ntime
.tv_sec
;
139 msg
= readmsg(TSP_ANY
, ANYADDR
, &wait
, 0);
141 (void)gettimeofday(&ntime
, 0);
142 if (ntime
.tv_sec
>= pollingtime
) {
143 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
146 /* If a bogus master told us to quit, we can have decided to ignore a
147 * network. Therefore, periodically try to take over everything.
149 polls
= (polls
+ 1) % POLLRATE
;
150 if (0 == polls
&& nignorednets
> 0) {
151 trace_msg("Looking for nets to re-master\n");
152 for (ntp
= nettab
; ntp
; ntp
= ntp
->next
) {
153 if (ntp
->status
== IGNORE
154 || ntp
->status
== NOMASTER
) {
156 if (ntp
->status
== MASTER
) {
161 if (ntp
->status
== MASTER
162 && --ntp
->quit_count
< 0)
171 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
172 to
.tsp_type
= TSP_LOOP
;
173 to
.tsp_vers
= TSPVERSION
;
174 to
.tsp_seq
= sequence
++;
175 to
.tsp_hopcnt
= MAX_HOPCNT
;
176 (void)strcpy(to
.tsp_name
, hostname
);
178 if (sendto(sock
, (char *)&to
,
179 sizeof(struct tsp
), 0,
180 (struct sockaddr
*)&ntp
->dest_addr
,
181 sizeof(ntp
->dest_addr
)) < 0) {
182 trace_sendto_err(ntp
->dest_addr
.sin_addr
);
189 switch (msg
->tsp_type
) {
200 * XXX check to see it is from ourself
203 (void)cftime(newdate
, "%D %T", &msg
->tsp_time
.tv_sec
);
205 (void)strcpy(newdate
, ctime(&msg
->tsp_time
.tv_sec
));
207 if (!good_host_name(msg
->tsp_name
)) {
209 "attempted date change by %s to %s",
210 msg
->tsp_name
, newdate
);
216 (void)gettimeofday(&ntime
, 0);
217 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
221 if (!fromnet
|| fromnet
->status
!= MASTER
)
224 (void)cftime(newdate
, "%D %T", &msg
->tsp_time
.tv_sec
);
226 (void)strcpy(newdate
, ctime(&msg
->tsp_time
.tv_sec
));
228 htp
= findhost(msg
->tsp_name
);
231 "attempted SET DATEREQ by uncontrolled %s to %s",
232 msg
->tsp_name
, newdate
);
235 if (htp
->seq
== msg
->tsp_seq
)
237 htp
->seq
= msg
->tsp_seq
;
240 "attempted SET DATEREQ by untrusted %s to %s",
241 msg
->tsp_name
, newdate
);
247 (void)gettimeofday(&ntime
, 0);
248 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
252 xmit(TSP_ACK
, msg
->tsp_seq
, &from
);
263 traceoff("Tracing ended at %s\n");
269 if (fromnet
->status
== MASTER
) {
271 (void)addmach(msg
->tsp_name
, &from
,fromnet
);
274 (void)strcpy(tname
, msg
->tsp_name
);
275 to
.tsp_type
= TSP_QUIT
;
276 (void)strcpy(to
.tsp_name
, hostname
);
277 answer
= acksend(&to
, &taddr
, tname
,
279 if (answer
== NULL
) {
280 syslog(LOG_ERR
, "election error by %s",
287 * After a network partition, there can be
288 * more than one master: the first slave to
289 * come up will notify here the situation.
291 if (!fromnet
|| fromnet
->status
!= MASTER
)
293 (void)strcpy(to
.tsp_name
, hostname
);
295 /* The other master often gets into the same state,
296 * with boring results if we stay at it forever.
298 ntp
= fromnet
; /* (acksend() can leave fromnet=0 */
299 for (i
= 0; i
< 3; i
++) {
300 to
.tsp_type
= TSP_RESOLVE
;
301 (void)strcpy(to
.tsp_name
, hostname
);
302 answer
= acksend(&to
, &ntp
->dest_addr
,
303 ANYADDR
, TSP_MASTERACK
,
307 htp
= addmach(answer
->tsp_name
,&from
,ntp
);
308 to
.tsp_type
= TSP_QUIT
;
309 msg
= acksend(&to
, &htp
->addr
, htp
->name
,
310 TSP_ACK
, 0, htp
->noanswer
);
313 "no response from %s to CONFLICT-QUIT",
322 if (!fromnet
|| fromnet
->status
!= MASTER
)
325 * do not want to call synch() while waiting
328 (void)gettimeofday(&ntime
, (struct timezone
*)0);
329 pollingtime
= ntime
.tv_sec
+ SAMPLEINTVL
;
333 doquit(msg
); /* become a slave */
337 if (!fromnet
|| fromnet
->status
!= MASTER
338 || !strcmp(msg
->tsp_name
, hostname
))
341 * We should not have received this from a net
342 * we are master on. There must be two masters.
344 htp
= addmach(msg
->tsp_name
, &from
,fromnet
);
345 to
.tsp_type
= TSP_QUIT
;
346 (void)strcpy(to
.tsp_name
, hostname
);
347 answer
= acksend(&to
, &htp
->addr
, htp
->name
,
351 "loop breakage: no reply from %s=%s to QUIT",
352 htp
->name
, inet_ntoa(htp
->addr
.sin_addr
));
359 "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
360 nnets
, nmasternets
, nslavenets
, nignorednets
);
369 fprintf(fd
, "garbage message: ");
380 * change the system date on the master
386 char tname
[MAXHOSTNAMELEN
];
388 struct timeval otime
, ntime
;
390 (void)strcpy(tname
, msg
->tsp_name
);
392 xmit(TSP_DATEACK
, msg
->tsp_seq
, &from
);
394 (void)strcpy(olddate
, date());
396 /* adjust time for residence on the queue */
397 (void)gettimeofday(&otime
, 0);
398 adj_msg_time(msg
,&otime
);
400 timevalsub(&ntime
, &msg
->tsp_time
, &otime
);
401 if (ntime
.tv_sec
< MAXADJ
&& ntime
.tv_sec
> -MAXADJ
) {
403 * do not change the clock if we can adjust it
406 synch(tvtomsround(ntime
));
409 if (0 > settimeofday(&msg
->tsp_time
, 0)) {
410 syslog(LOG_ERR
, "settimeofday(): %m");
412 logwtmp(&otime
, &msg
->tsp_time
);
414 logwtmp("|", "date", "");
415 (void)settimeofday(&msg
->tsp_time
, 0);
416 logwtmp("}", "date", "");
421 syslog(LOG_NOTICE
, "date changed by %s from %s",
427 * synchronize all of the slaves
435 struct timeval check
, stop
, wait
;
442 fprintf(fd
, "measurements starting at %s\n", date());
443 (void)gettimeofday(&check
, 0);
445 /* run fast to get good time */
446 pri
= schedctl(NDPRI
,0,NDPHIMIN
);
448 syslog(LOG_ERR
, "schedctl(): %m");
450 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
451 if (htp
->noanswer
!= 0) {
452 measure_status
= measure(500, 100,
456 measure_status
= measure(3000, 100,
460 if (measure_status
!= GOOD
) {
461 /* The slave did not respond. We have
462 * just wasted lots of time on it.
464 htp
->delta
= HOSTDOWN
;
465 if (++htp
->noanswer
>= LOSTHOST
) {
468 "purging %s for not answering ICMP\n",
475 htp
->delta
= measure_delta
;
477 (void)gettimeofday(&stop
, 0);
478 timevalsub(&stop
, &stop
, &check
);
479 if (stop
.tv_sec
>= 1) {
483 * ack messages periodically
487 if (0 != readmsg(TSP_TRACEON
,ANYADDR
,
490 (void)gettimeofday(&check
, 0);
495 (void)schedctl(NDPRI
,0,pri
);
498 fprintf(fd
, "measurements finished at %s\n", date());
500 if (!(status
& SLAVE
)) {
502 mydelta
= networkdelta();
507 if (trace
&& (mydelta
!= 0 || (status
& SLAVE
)))
508 fprintf(fd
,"local correction of %ld ms.\n", mydelta
);
513 * sends the time to each slave after the master
514 * has received the command to set the network time
523 /* Do not listen to the consensus after forcing the time. This is because
524 * the consensus takes a while to reach the time we are dictating.
527 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
528 to
.tsp_type
= TSP_SETTIME
;
529 (void)strcpy(to
.tsp_name
, hostname
);
530 (void)gettimeofday(&to
.tsp_time
, 0);
531 answer
= acksend(&to
, &htp
->addr
, htp
->name
,
532 TSP_ACK
, 0, htp
->noanswer
);
534 /* We client does not respond, then we have
535 * just wasted lots of time on it.
538 "no reply to SETTIME from %s", htp
->name
);
539 if (++htp
->noanswer
>= LOSTHOST
) {
542 "purging %s for not answering",
556 static time_t next_time
;
563 if (!fd
) /* quit if tracing already off */
566 this_time
= times(&tm
);
567 if (this_time
+ delta
< next_time
)
569 next_time
= this_time
+ CLK_TCK
;
571 fprintf(fd
, "host table: %d entries at %s\n", slvcount
, date());
574 for (i
= 1; i
<= slvcount
; i
++, htp
= htp
->l_fwd
) {
575 l
= strlen(htp
->name
) + 1;
576 if (length
+l
>= 80) {
581 fprintf(fd
, " %s", htp
->name
);
587 static struct hosttbl
*newhost_hash
;
588 static struct hosttbl
*lasthfree
= &hosttbl
[0];
591 struct hosttbl
* /* answer or 0 */
600 for (p
= name
, i
= 0; i
< 8 && *p
!= '\0'; i
++, p
++)
602 newhost_hash
= &hosttbl
[j
% NHOSTS
];
605 if (htp
->name
[0] == '\0')
608 if (!strcmp(name
, htp
->name
))
611 } while (htp
!= newhost_hash
);
616 * add a host to the list of controlled machines if not already there
619 addmach(name
, addr
, ntp
)
621 struct sockaddr_in
*addr
;
624 struct hosttbl
*ret
, *p
, *b
, *f
;
626 ret
= findhost(name
);
628 if (slvcount
>= NHOSTS
) {
630 fprintf(fd
, "no more slots in host table\n");
633 syslog(LOG_ERR
, "no more slots in host table");
635 longjmp(jmpenv
, 2); /* give up and be a slave */
638 /* if our home hash slot is occupied, find a free entry
641 if (newhost_hash
->name
[0] != '\0') {
644 if (++lasthfree
> &hosttbl
[NHOSTS
])
645 lasthfree
= &hosttbl
[1];
646 } while (ret
->name
[0] != '\0');
648 if (!newhost_hash
->head
) {
649 /* Move an interloper using our home. Use
650 * scratch pointers in case the new head is
651 * pointing to itself.
653 f
= newhost_hash
->h_fwd
;
654 b
= newhost_hash
->h_bak
;
657 f
= newhost_hash
->l_fwd
;
658 b
= newhost_hash
->l_bak
;
661 bcopy(newhost_hash
,ret
,sizeof(*ret
));
667 /* link to an existing chain in our home
670 p
= newhost_hash
->h_bak
;
671 ret
->h_fwd
= newhost_hash
;
674 newhost_hash
->h_bak
= ret
;
684 (void)strncpy(ret
->name
, name
, sizeof(ret
->name
));
685 ret
->good
= good_host_name(name
);
687 ret
->l_bak
= self
.l_bak
;
688 self
.l_bak
->l_fwd
= ret
;
696 ret
->noanswer
= (ret
->noanswer
!= 0);
699 /* need to clear sequence number anyhow */
705 * remove the machine with the given index in the host table.
711 struct hosttbl
*lprv
, *hnxt
, *f
, *b
;
714 fprintf(fd
, "remove %s\n", htp
->name
);
716 /* get out of the lists */
717 htp
->l_fwd
->l_bak
= lprv
= htp
->l_bak
;
718 htp
->l_bak
->l_fwd
= htp
->l_fwd
;
719 htp
->h_fwd
->h_bak
= htp
->h_bak
;
720 htp
->h_bak
->h_fwd
= hnxt
= htp
->h_fwd
;
722 /* If we are in the home slot, pull up the chain */
723 if (htp
->head
&& hnxt
!= htp
) {
727 /* Use scratch pointers in case the new head is pointing to
739 bcopy(hnxt
, htp
, sizeof(*htp
));
745 lasthfree
->name
[0] = '\0';
746 lasthfree
->h_fwd
= 0;
747 lasthfree
->l_fwd
= 0;
755 * Remove all the machines from the host table that exist on the given
756 * network. This is called when a master transitions to a slave on a
767 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
779 xmit(TSP_MASTERUP
, 0, &net
->dest_addr
);
782 * Do not tell new slaves our time for a while. This ensures
783 * we do not tell them to start using our time, before we have
784 * found a good master.
786 (void)gettimeofday(&net
->slvwait
, 0);
794 struct tsp
*answer
, to
;
797 if (!fromnet
|| fromnet
->status
!= MASTER
)
800 htp
= addmach(msg
->tsp_name
, &from
,fromnet
);
801 htp
->seq
= msg
->tsp_seq
;
806 * If we are stable, send our time to the slave.
807 * Do not go crazy if the date has been changed.
809 (void)gettimeofday(&now
, 0);
810 if (now
.tv_sec
>= fromnet
->slvwait
.tv_sec
+3
811 || now
.tv_sec
< fromnet
->slvwait
.tv_sec
) {
812 to
.tsp_type
= TSP_SETTIME
;
813 (void)strcpy(to
.tsp_name
, hostname
);
814 (void)gettimeofday(&to
.tsp_time
, 0);
815 answer
= acksend(&to
, &htp
->addr
,
822 "no reply to initial SETTIME from %s",
824 htp
->noanswer
= LOSTHOST
;
831 * react to a TSP_QUIT:
837 if (fromnet
->status
== MASTER
) {
838 if (!good_host_name(msg
->tsp_name
)) {
839 if (fromnet
->quit_count
<= 0) {
840 syslog(LOG_NOTICE
,"untrusted %s told us QUIT",
842 suppress(&from
, msg
->tsp_name
, fromnet
);
843 fromnet
->quit_count
= 1;
846 syslog(LOG_NOTICE
, "untrusted %s told us QUIT twice",
848 fromnet
->quit_count
= 2;
849 fromnet
->status
= NOMASTER
;
851 fromnet
->status
= SLAVE
;
854 longjmp(jmpenv
, 2); /* give up and be a slave */
857 if (!good_host_name(msg
->tsp_name
)) {
858 syslog(LOG_NOTICE
, "untrusted %s told us QUIT",
860 fromnet
->quit_count
= 2;
869 fd
= fopen(_PATH_TIMEDLOG
, "w");
874 fprintf(fd
,"Tracing started at %s\n", date());
891 fprintf(fd
, msg
, date());
906 logwtmp(otime
, ntime
)
907 struct timeval
*otime
, *ntime
;
909 static struct utmp wtmp
[2] = {
910 {"","",OTIME_MSG
,0,OLD_TIME
,0,0,0},
911 {"","",NTIME_MSG
,0,NEW_TIME
,0,0,0}
913 static char *wtmpfile
= WTMP_FILE
;
916 wtmp
[0].ut_time
= otime
->tv_sec
+ (otime
->tv_usec
+ 500000) / 1000000;
917 wtmp
[1].ut_time
= ntime
->tv_sec
+ (ntime
->tv_usec
+ 500000) / 1000000;
918 if (wtmp
[0].ut_time
== wtmp
[1].ut_time
)
922 (void)pututline(&wtmp
[0]);
923 (void)pututline(&wtmp
[1]);
925 if ((f
= open(wtmpfile
, O_WRONLY
|O_APPEND
)) >= 0) {
926 (void) write(f
, (char *)wtmp
, sizeof(wtmp
));