]>
git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/timed.c
95d440a302e06945d4d92e4d73ab85dfeec2dc2b
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 copyright
[] =
60 "@(#) Copyright (c) 1985, 1993\n\
61 The Regents of the University of California. All rights reserved.\n";
65 static char sccsid
[] = "@(#)timed.c 8.2 (Berkeley) 3/26/95";
69 #ident "$Revision: 1.1.1.1 $"
76 #include <sys/ioctl.h>
78 #include "pathnames.h"
80 #include <sys/types.h>
81 #include <sys/times.h>
84 #include <sys/syssgi.h>
85 #include <sys/schedctl.h>
89 int sock
, sock_raw
= -1;
91 u_short sequence
; /* sequence number */
95 int nslavenets
; /* nets were I could be a slave */
96 int nmasternets
; /* nets were I could be a master */
97 int nignorednets
; /* ignored nets */
98 int nnets
; /* nets I am connected to */
100 FILE *fd
; /* trace file FD */
104 struct netinfo
*nettab
= 0;
105 struct netinfo
*slavenet
;
116 struct hosttbl hosttbl
[NHOSTS
+1]; /* known hosts */
118 static struct goodhost
{ /* hosts that we trust */
119 char name
[MAXHOSTNAMELEN
+1];
120 struct goodhost
*next
;
124 static char *goodgroup
; /* net group of trusted hosts */
125 static void checkignorednets
__P((void));
126 static void pickslavenet
__P((struct netinfo
*));
127 static void add_good_host
__P((char *, int));
131 char *timetrim_wpat
= "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
132 char *timetrim_rpat
= "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
134 double tot_adj
, hr_adj
; /* totals in nsec */
135 double tot_ticks
, hr_ticks
;
137 int bufspace
= 60*1024;
142 * The timedaemons synchronize the clocks of hosts in a local area network.
143 * One daemon runs as master, all the others as slaves. The master
144 * performs the task of computing clock differences and sends correction
145 * values to the slaves.
146 * Slaves start an election to choose a new master when the latter disappears
147 * because of a machine crash, network partition, or when killed.
148 * A resolution protocol is used to kill all but one of the masters
149 * that happen to exist in segments of a partitioned network when the
150 * network partition is fixed.
152 * Authors: Riccardo Gusella & Stefano Zatti
154 * overhauled at Silicon Graphics
164 struct timeval ntime
;
165 struct servent
*srvp
;
166 char buf
[BUFSIZ
], *cp
, *cplim
;
168 struct ifreq ifreq
, ifreqf
, *ifr
;
169 register struct netinfo
*ntp
;
170 struct netinfo
*ntip
;
171 struct netinfo
*savefromnet
;
172 struct netent
*nentp
;
174 static struct sockaddr_in server
;
178 extern int optind
, opterr
;
183 #define IN_MSG "timed: -i and -n make no sense together\n"
186 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp] [-P trimfile]\n"
189 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
191 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
204 if (0 > syssgi(SGI_GETTIMETRIM
, &timetrim
)) {
205 perror("timed: syssgi(GETTIMETRIM)");
208 tot_ticks
= hr_ticks
= times(&tms
);
212 while ((c
= getopt(argc
, argv
, "Mtdn:i:F:G:P:")) != EOF
) {
224 fprintf(stderr
, IN_MSG
);
234 fprintf(stderr
, IN_MSG
);
243 add_good_host(optarg
,1);
244 while (optind
< argc
&& argv
[optind
][0] != '-')
245 add_good_host(argv
[optind
++], 1);
252 if (goodgroup
!= 0) {
253 fprintf(stderr
,"timed: only one net group\n");
260 timetrim_fn
= optarg
;
265 fprintf(stderr
, USAGE
);
271 fprintf(stderr
, USAGE
);
276 if (timetrim_fn
== 0) {
278 } else if (0 == (timetrim_st
= fopen(timetrim_fn
, "r+"))) {
279 if (errno
!= ENOENT
) {
280 (void)fprintf(stderr
,"timed: ");
289 i
= fscanf(timetrim_st
, timetrim_rpat
,
290 &trim
, &adj
, &ticks
);
296 && trim
!= rint(adj
*CLK_TCK
/ticks
))) {
297 if (trace
&& i
!= EOF
)
298 (void)fprintf(stderr
,
299 "timed: unrecognized contents in %s\n",
302 if (0 > syssgi(SGI_SETTIMETRIM
,
304 perror("timed: syssgi(SETTIMETRIM)");
311 (void)fclose(timetrim_st
);
315 /* If we care about which machine is the master, then we must
316 * be willing to be a master
318 if (0 != goodgroup
|| 0 != goodhosts
)
321 if (gethostname(hostname
, sizeof(hostname
) - 1) < 0) {
322 perror("gethostname");
332 if (goodhosts
!= 0) /* trust ourself */
333 add_good_host(hostname
,1);
335 srvp
= getservbyname("timed", "udp");
337 fprintf(stderr
, "unknown service 'timed/udp'\n");
341 server
.sin_addr
.s_addr
= INADDR_ANY
;
342 server
.sin_port
= srvp
->s_port
;
343 server
.sin_family
= AF_INET
;
344 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
349 if (setsockopt(sock
, SOL_SOCKET
, SO_BROADCAST
, (char *)&on
,
351 perror("setsockopt");
354 if (bind(sock
, (struct sockaddr
*)&server
, sizeof(server
))) {
355 if (errno
== EADDRINUSE
)
356 fprintf(stderr
,"timed: time daemon already running\n");
363 * handle many slaves with our buffer
365 if (0 > setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, (char*)&bufspace
,
367 perror("setsockopt");
372 /* choose a unique seed for random number generation */
373 (void)gettimeofday(&ntime
, 0);
374 srandom(ntime
.tv_sec
+ ntime
.tv_usec
);
376 sequence
= random(); /* initial seq number */
379 /* rounds kernel variable time to multiple of 5 ms. */
381 ntime
.tv_usec
= -((ntime
.tv_usec
/1000) % 5) * 1000;
382 (void)adjtime(&ntime
, (struct timeval
*)0);
385 for (nt
= nets
; nt
; nt
= nt
->next
) {
386 nentp
= getnetbyname(nt
->name
);
388 nt
->net
= inet_network(nt
->name
);
389 if (nt
->net
!= INADDR_NONE
)
390 nentp
= getnetbyaddr(nt
->net
, AF_INET
);
393 nt
->net
= nentp
->n_net
;
394 } else if (nt
->net
== INADDR_NONE
) {
395 fprintf(stderr
, "timed: unknown net %s\n", nt
->name
);
397 } else if (nt
->net
== INADDR_ANY
) {
398 fprintf(stderr
, "timed: bad net %s\n", nt
->name
);
402 "timed: warning: %s unknown in /etc/networks\n",
406 if (0 == (nt
->net
& 0xff000000))
408 if (0 == (nt
->net
& 0xff000000))
410 if (0 == (nt
->net
& 0xff000000))
413 ifc
.ifc_len
= sizeof(buf
);
415 if (ioctl(sock
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
416 perror("timed: get interface configuration");
421 #define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */
423 #define size(p) max((p).sa_len, sizeof(p))
425 cplim
= buf
+ ifc
.ifc_len
; /*skip over if's with big ifr_addr's */
426 for (cp
= buf
; cp
< cplim
;
427 cp
+= sizeof (ifr
->ifr_name
) + size(ifr
->ifr_addr
)) {
428 ifr
= (struct ifreq
*)cp
;
429 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
432 ntp
= (struct netinfo
*)malloc(sizeof(struct netinfo
));
433 bzero(ntp
,sizeof(*ntp
));
434 ntp
->my_addr
=((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
;
435 ntp
->status
= NOMASTER
;
439 if (ioctl(sock
, SIOCGIFFLAGS
, (char *)&ifreqf
) < 0) {
440 perror("get interface flags");
443 if ((ifreqf
.ifr_flags
& IFF_UP
) == 0)
445 if ((ifreqf
.ifr_flags
& IFF_BROADCAST
) == 0 &&
446 (ifreqf
.ifr_flags
& IFF_POINTOPOINT
) == 0) {
451 if (ioctl(sock
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0) {
452 perror("get netmask");
455 ntp
->mask
= ((struct sockaddr_in
*)
456 &ifreq
.ifr_addr
)->sin_addr
.s_addr
;
458 if (ifreqf
.ifr_flags
& IFF_BROADCAST
) {
459 if (ioctl(sock
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
460 perror("get broadaddr");
463 ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_broadaddr
;
464 /* What if the broadcast address is all ones?
465 * So we cannot just mask ntp->dest_addr. */
466 ntp
->net
= ntp
->my_addr
;
467 ntp
->net
.s_addr
&= ntp
->mask
;
469 if (ioctl(sock
, SIOCGIFDSTADDR
,
470 (char *)&ifreq
) < 0) {
471 perror("get destaddr");
474 ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_dstaddr
;
475 ntp
->net
= ntp
->dest_addr
.sin_addr
;
478 ntp
->dest_addr
.sin_port
= port
;
480 for (nt
= nets
; nt
; nt
= nt
->next
) {
481 if (ntp
->net
.s_addr
== nt
->net
)
484 if (nflag
&& !nt
|| iflag
&& nt
)
488 if (nettab
== NULL
) {
497 (void) free((char *)ntp
);
498 if (nettab
== NULL
) {
499 fprintf(stderr
, "timed: no network usable\n");
505 (void)schedctl(RENICE
,0,10); /* run fast to get good time */
507 /* ticks to delay before responding to a broadcast */
508 delay1
= casual(0, CLK_TCK
/10);
511 /* microseconds to delay before responding to a broadcast */
512 delay1
= casual(1, 100*1000);
515 /* election timer delay in secs. */
516 delay2
= casual(MINTOUT
, MAXTOUT
);
520 (void)_daemonize(debug
? _DF_NOFORK
|_DF_NOCHDIR
: 0, sock
, -1, -1);
528 openlog("timed", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
531 * keep returning here
533 ret
= setjmp(jmpenv
);
534 savefromnet
= fromnet
;
545 /* Just lost our master */
547 slavenet
->status
= election(slavenet
);
548 if (!slavenet
|| slavenet
->status
== MASTER
) {
552 makeslave(slavenet
); /* prune extras */
557 /* Just been told to quit */
559 pickslavenet(savefromnet
);
564 if (!(status
& MASTER
) && sock_raw
!= -1) {
565 /* sock_raw is not being used now */
566 (void)close(sock_raw
);
570 if (status
== MASTER
)
576 if (sock_raw
!= -1) {
577 (void)close(sock_raw
);
582 /* we just lost our master or were told to quit */
585 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
586 if (ntp
->status
== MASTER
)
588 ntp
->status
= NOMASTER
;
603 * suppress an upstart, untrustworthy, self-appointed master
606 suppress(addr
, name
,net
)
607 struct sockaddr_in
*addr
;
611 struct sockaddr_in tgt
;
612 char tname
[MAXHOSTNAMELEN
];
614 static struct timeval wait
;
617 fprintf(fd
, "suppress: %s\n", name
);
619 (void)strcpy(tname
, name
);
621 while (0 != readmsg(TSP_ANY
, ANYADDR
, &wait
, net
)) {
623 fprintf(fd
, "suppress:\tdiscarded packet from %s\n",
627 syslog(LOG_NOTICE
, "suppressing false master %s", tname
);
628 msg
.tsp_type
= TSP_QUIT
;
629 (void)strcpy(msg
.tsp_name
, hostname
);
630 (void)acksend(&msg
, &tgt
, tname
, TSP_ACK
, 0, 1);
637 struct tsp resp
, conflict
, *answer
;
638 struct timeval ntime
;
639 char mastername
[MAXHOSTNAMELEN
];
640 struct sockaddr_in masteraddr
;
645 /* look for master */
646 resp
.tsp_type
= TSP_MASTERREQ
;
647 (void)strcpy(resp
.tsp_name
, hostname
);
648 answer
= acksend(&resp
, &ntp
->dest_addr
, ANYADDR
,
649 TSP_MASTERACK
, ntp
, 0);
650 if (answer
!= 0 && !good_host_name(answer
->tsp_name
)) {
651 suppress(&from
, answer
->tsp_name
, ntp
);
652 ntp
->status
= NOMASTER
;
657 * Various conditions can cause conflict: races between
658 * two just started timedaemons when no master is
659 * present, or timedaemons started during an election.
660 * A conservative approach is taken. Give up and became a
661 * slave, postponing election of a master until first
664 ntime
.tv_sec
= ntime
.tv_usec
= 0;
665 answer
= readmsg(TSP_MASTERREQ
, ANYADDR
, &ntime
, ntp
);
667 if (!good_host_name(answer
->tsp_name
)) {
668 suppress(&from
, answer
->tsp_name
, ntp
);
669 ntp
->status
= NOMASTER
;
674 ntime
.tv_sec
= ntime
.tv_usec
= 0;
675 answer
= readmsg(TSP_MASTERUP
, ANYADDR
, &ntime
, ntp
);
677 if (!good_host_name(answer
->tsp_name
)) {
678 suppress(&from
, answer
->tsp_name
, ntp
);
679 ntp
->status
= NOMASTER
;
684 ntime
.tv_sec
= ntime
.tv_usec
= 0;
685 answer
= readmsg(TSP_ELECTION
, ANYADDR
, &ntime
, ntp
);
687 if (!good_host_name(answer
->tsp_name
)) {
688 suppress(&from
, answer
->tsp_name
, ntp
);
689 ntp
->status
= NOMASTER
;
695 ntp
->status
= MASTER
;
697 ntp
->status
= NOMASTER
;
702 (void)strcpy(mastername
, answer
->tsp_name
);
706 * If network has been partitioned, there might be other
707 * masters; tell the one we have just acknowledged that
708 * it has to gain control over the others.
711 ntime
.tv_usec
= 300000;
712 answer
= readmsg(TSP_MASTERACK
, ANYADDR
, &ntime
, ntp
);
714 * checking also not to send CONFLICT to ack'ed master
715 * due to duplicated MASTERACKs
717 if (answer
!= NULL
&&
718 strcmp(answer
->tsp_name
, mastername
) != 0) {
719 conflict
.tsp_type
= TSP_CONFLICT
;
720 (void)strcpy(conflict
.tsp_name
, hostname
);
721 if (!acksend(&conflict
, &masteraddr
, mastername
,
724 "error on sending TSP_CONFLICT");
730 * based on the current network configuration, set the status, and count
739 nmasternets
= nslavenets
= nnets
= nignorednets
= 0;
741 fprintf(fd
, "Net status:\n");
742 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
743 switch ((int)ntp
->status
) {
756 fprintf(fd
, "\t%-16s", inet_ntoa(ntp
->net
));
757 switch ((int)ntp
->status
) {
759 fprintf(fd
, "NOMASTER\n");
762 fprintf(fd
, "MASTER\n");
765 fprintf(fd
, "SLAVE\n");
768 fprintf(fd
, "IGNORE\n");
771 fprintf(fd
, "invalid state %d\n",
777 status
|= ntp
->status
;
782 "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
783 nnets
, nmasternets
, nslavenets
, nignorednets
, delay2
);
790 register struct netinfo
*ntp
;
792 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
793 if (ntp
->status
== SLAVE
&& ntp
!= net
)
794 ntp
->status
= IGNORE
;
800 * Try to become master over ignored nets..
805 register struct netinfo
*ntp
;
807 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
808 if (!Mflag
&& ntp
->status
== SLAVE
)
811 if (ntp
->status
== IGNORE
|| ntp
->status
== NOMASTER
) {
813 if (!Mflag
&& ntp
->status
== SLAVE
)
820 * choose a good network on which to be a slave
821 * The ignored networks must have already been checked.
822 * Take a hint about for a good network.
828 if (slavenet
!= 0 && slavenet
->status
== SLAVE
) {
829 makeslave(slavenet
); /* prune extras */
833 if (ntp
== 0 || ntp
->status
!= SLAVE
) {
834 for (ntp
= nettab
; ntp
!= 0; ntp
= ntp
->next
) {
835 if (ntp
->status
== SLAVE
)
843 * returns a random number in the range [inf, sup]
851 value
= ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
852 return(inf
+ (sup
- inf
)*value
);
862 (void)gettimeofday(&tv
, (struct timezone
*)0);
863 (void)cftime(tm
, "%D %T", &tv
.tv_sec
);
868 (void)gettimeofday(&tv
, (struct timezone
*)0);
869 return (ctime(&tv
.tv_sec
));
877 register struct nets
**netlist
= &nets
;
880 netlist
= &((*netlist
)->next
);
881 *netlist
= (struct nets
*)malloc(sizeof **netlist
);
883 fprintf(stderr
,"malloc failed\n");
886 bzero((char *)*netlist
, sizeof(**netlist
));
887 (*netlist
)->name
= name
;
890 /* note a host as trustworthy */
892 add_good_host(name
, perm
)
894 int perm
; /* 1=not part of the netgroup */
896 register struct goodhost
*ghp
;
897 register struct hostent
*hentp
;
899 ghp
= (struct goodhost
*)malloc(sizeof(*ghp
));
901 syslog(LOG_ERR
, "malloc failed");
905 bzero((char*)ghp
, sizeof(*ghp
));
906 (void)strncpy(&ghp
->name
[0], name
, sizeof(ghp
->name
));
907 ghp
->next
= goodhosts
;
911 hentp
= gethostbyname(name
);
912 if (0 == hentp
&& perm
)
913 (void)fprintf(stderr
, "unknown host %s\n", name
);
917 /* update our image of the net-group of trustworthy hosts
923 # define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
924 static unsigned long last_update
= -NG_DELAY
;
925 unsigned long new_update
;
927 struct goodhost
*ghp
, **ghpp
;
928 char *mach
, *usr
, *dom
;
932 /* if no netgroup, then we are finished */
933 if (goodgroup
== 0 || !Mflag
)
936 /* Do not chatter with the netgroup master too often.
938 new_update
= times(&tm
);
939 if (new_update
< last_update
+ NG_DELAY
942 last_update
= new_update
;
944 /* forget the old temporary entries */
946 while (0 != (ghp
= *ghpp
)) {
956 /* quit now if we are not one of the trusted masters
958 if (!innetgr(goodgroup
, &hostname
[0], 0,0)) {
960 (void)fprintf(fd
, "get_goodgroup: %s not in %s\n",
961 &hostname
[0], goodgroup
);
965 (void)fprintf(fd
, "get_goodgroup: %s in %s\n",
966 &hostname
[0], goodgroup
);
968 /* mark the entire netgroup as trusted */
969 (void)setnetgrent(goodgroup
);
970 while (getnetgrent(&mach
,&usr
,&dom
)) {
972 add_good_host(mach
,0);
976 /* update list of slaves */
977 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
978 htp
->good
= good_host_name(&htp
->name
[0]);
984 /* see if a machine is trustworthy
986 int /* 1=trust hp to change our date */
990 register struct goodhost
*ghp
= goodhosts
;
993 if (!ghp
|| !Mflag
) /* trust everyone if no one named */
998 if (c
== ghp
->name
[0]
999 && !strcasecmp(name
, ghp
->name
))
1000 return 1; /* found him, so say so */
1001 } while (0 != (ghp
= ghp
->next
));
1003 if (!strcasecmp(name
,hostname
)) /* trust ourself */
1006 return 0; /* did not find him */