]>
git.saurik.com Git - apple/network_cmds.git/blob - timed.tproj/timed.tproj/timed.c
0079b161e006ef6153d283380ad51feebe4d8b14
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 copyright
[] =
59 "@(#) Copyright (c) 1985, 1993\n\
60 The Regents of the University of California. All rights reserved.\n";
64 static char sccsid
[] = "@(#)timed.c 8.2 (Berkeley) 3/26/95";
68 #ident "$Revision: 1.1 $"
75 #include <sys/ioctl.h>
77 #include "pathnames.h"
79 #include <sys/types.h>
80 #include <sys/times.h>
83 #include <sys/syssgi.h>
84 #include <sys/schedctl.h>
88 int sock
, sock_raw
= -1;
90 u_short sequence
; /* sequence number */
94 int nslavenets
; /* nets were I could be a slave */
95 int nmasternets
; /* nets were I could be a master */
96 int nignorednets
; /* ignored nets */
97 int nnets
; /* nets I am connected to */
99 FILE *fd
; /* trace file FD */
103 struct netinfo
*nettab
= 0;
104 struct netinfo
*slavenet
;
115 struct hosttbl hosttbl
[NHOSTS
+1]; /* known hosts */
117 static struct goodhost
{ /* hosts that we trust */
118 char name
[MAXHOSTNAMELEN
+1];
119 struct goodhost
*next
;
123 static char *goodgroup
; /* net group of trusted hosts */
124 static void checkignorednets
__P((void));
125 static void pickslavenet
__P((struct netinfo
*));
126 static void add_good_host
__P((char *, int));
130 char *timetrim_wpat
= "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
131 char *timetrim_rpat
= "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
133 double tot_adj
, hr_adj
; /* totals in nsec */
134 double tot_ticks
, hr_ticks
;
136 int bufspace
= 60*1024;
141 * The timedaemons synchronize the clocks of hosts in a local area network.
142 * One daemon runs as master, all the others as slaves. The master
143 * performs the task of computing clock differences and sends correction
144 * values to the slaves.
145 * Slaves start an election to choose a new master when the latter disappears
146 * because of a machine crash, network partition, or when killed.
147 * A resolution protocol is used to kill all but one of the masters
148 * that happen to exist in segments of a partitioned network when the
149 * network partition is fixed.
151 * Authors: Riccardo Gusella & Stefano Zatti
153 * overhauled at Silicon Graphics
163 struct timeval ntime
;
164 struct servent
*srvp
;
165 char buf
[BUFSIZ
], *cp
, *cplim
;
167 struct ifreq ifreq
, ifreqf
, *ifr
;
168 register struct netinfo
*ntp
;
169 struct netinfo
*ntip
;
170 struct netinfo
*savefromnet
;
171 struct netent
*nentp
;
173 static struct sockaddr_in server
;
177 extern int optind
, opterr
;
182 #define IN_MSG "timed: -i and -n make no sense together\n"
185 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp] [-P trimfile]\n"
188 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
190 #define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
203 if (0 > syssgi(SGI_GETTIMETRIM
, &timetrim
)) {
204 perror("timed: syssgi(GETTIMETRIM)");
207 tot_ticks
= hr_ticks
= times(&tms
);
211 while ((c
= getopt(argc
, argv
, "Mtdn:i:F:G:P:")) != EOF
) {
223 fprintf(stderr
, IN_MSG
);
233 fprintf(stderr
, IN_MSG
);
242 add_good_host(optarg
,1);
243 while (optind
< argc
&& argv
[optind
][0] != '-')
244 add_good_host(argv
[optind
++], 1);
251 if (goodgroup
!= 0) {
252 fprintf(stderr
,"timed: only one net group\n");
259 timetrim_fn
= optarg
;
264 fprintf(stderr
, USAGE
);
270 fprintf(stderr
, USAGE
);
275 if (timetrim_fn
== 0) {
277 } else if (0 == (timetrim_st
= fopen(timetrim_fn
, "r+"))) {
278 if (errno
!= ENOENT
) {
279 (void)fprintf(stderr
,"timed: ");
288 i
= fscanf(timetrim_st
, timetrim_rpat
,
289 &trim
, &adj
, &ticks
);
295 && trim
!= rint(adj
*CLK_TCK
/ticks
))) {
296 if (trace
&& i
!= EOF
)
297 (void)fprintf(stderr
,
298 "timed: unrecognized contents in %s\n",
301 if (0 > syssgi(SGI_SETTIMETRIM
,
303 perror("timed: syssgi(SETTIMETRIM)");
310 (void)fclose(timetrim_st
);
314 /* If we care about which machine is the master, then we must
315 * be willing to be a master
317 if (0 != goodgroup
|| 0 != goodhosts
)
320 if (gethostname(hostname
, sizeof(hostname
) - 1) < 0) {
321 perror("gethostname");
331 if (goodhosts
!= 0) /* trust ourself */
332 add_good_host(hostname
,1);
334 srvp
= getservbyname("timed", "udp");
336 fprintf(stderr
, "unknown service 'timed/udp'\n");
340 server
.sin_addr
.s_addr
= INADDR_ANY
;
341 server
.sin_port
= srvp
->s_port
;
342 server
.sin_family
= AF_INET
;
343 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
348 if (setsockopt(sock
, SOL_SOCKET
, SO_BROADCAST
, (char *)&on
,
350 perror("setsockopt");
353 if (bind(sock
, (struct sockaddr
*)&server
, sizeof(server
))) {
354 if (errno
== EADDRINUSE
)
355 fprintf(stderr
,"timed: time daemon already running\n");
362 * handle many slaves with our buffer
364 if (0 > setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, (char*)&bufspace
,
366 perror("setsockopt");
371 /* choose a unique seed for random number generation */
372 (void)gettimeofday(&ntime
, 0);
373 srandom(ntime
.tv_sec
+ ntime
.tv_usec
);
375 sequence
= random(); /* initial seq number */
378 /* rounds kernel variable time to multiple of 5 ms. */
380 ntime
.tv_usec
= -((ntime
.tv_usec
/1000) % 5) * 1000;
381 (void)adjtime(&ntime
, (struct timeval
*)0);
384 for (nt
= nets
; nt
; nt
= nt
->next
) {
385 nentp
= getnetbyname(nt
->name
);
387 nt
->net
= inet_network(nt
->name
);
388 if (nt
->net
!= INADDR_NONE
)
389 nentp
= getnetbyaddr(nt
->net
, AF_INET
);
392 nt
->net
= nentp
->n_net
;
393 } else if (nt
->net
== INADDR_NONE
) {
394 fprintf(stderr
, "timed: unknown net %s\n", nt
->name
);
396 } else if (nt
->net
== INADDR_ANY
) {
397 fprintf(stderr
, "timed: bad net %s\n", nt
->name
);
401 "timed: warning: %s unknown in /etc/networks\n",
405 if (0 == (nt
->net
& 0xff000000))
407 if (0 == (nt
->net
& 0xff000000))
409 if (0 == (nt
->net
& 0xff000000))
412 ifc
.ifc_len
= sizeof(buf
);
414 if (ioctl(sock
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
415 perror("timed: get interface configuration");
420 #define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */
422 #define size(p) max((p).sa_len, sizeof(p))
424 cplim
= buf
+ ifc
.ifc_len
; /*skip over if's with big ifr_addr's */
425 for (cp
= buf
; cp
< cplim
;
426 cp
+= sizeof (ifr
->ifr_name
) + size(ifr
->ifr_addr
)) {
427 ifr
= (struct ifreq
*)cp
;
428 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
431 ntp
= (struct netinfo
*)malloc(sizeof(struct netinfo
));
432 bzero(ntp
,sizeof(*ntp
));
433 ntp
->my_addr
=((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
;
434 ntp
->status
= NOMASTER
;
438 if (ioctl(sock
, SIOCGIFFLAGS
, (char *)&ifreqf
) < 0) {
439 perror("get interface flags");
442 if ((ifreqf
.ifr_flags
& IFF_UP
) == 0)
444 if ((ifreqf
.ifr_flags
& IFF_BROADCAST
) == 0 &&
445 (ifreqf
.ifr_flags
& IFF_POINTOPOINT
) == 0) {
450 if (ioctl(sock
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0) {
451 perror("get netmask");
454 ntp
->mask
= ((struct sockaddr_in
*)
455 &ifreq
.ifr_addr
)->sin_addr
.s_addr
;
457 if (ifreqf
.ifr_flags
& IFF_BROADCAST
) {
458 if (ioctl(sock
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
459 perror("get broadaddr");
462 ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_broadaddr
;
463 /* What if the broadcast address is all ones?
464 * So we cannot just mask ntp->dest_addr. */
465 ntp
->net
= ntp
->my_addr
;
466 ntp
->net
.s_addr
&= ntp
->mask
;
468 if (ioctl(sock
, SIOCGIFDSTADDR
,
469 (char *)&ifreq
) < 0) {
470 perror("get destaddr");
473 ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_dstaddr
;
474 ntp
->net
= ntp
->dest_addr
.sin_addr
;
477 ntp
->dest_addr
.sin_port
= port
;
479 for (nt
= nets
; nt
; nt
= nt
->next
) {
480 if (ntp
->net
.s_addr
== nt
->net
)
483 if (nflag
&& !nt
|| iflag
&& nt
)
487 if (nettab
== NULL
) {
496 (void) free((char *)ntp
);
497 if (nettab
== NULL
) {
498 fprintf(stderr
, "timed: no network usable\n");
504 (void)schedctl(RENICE
,0,10); /* run fast to get good time */
506 /* ticks to delay before responding to a broadcast */
507 delay1
= casual(0, CLK_TCK
/10);
510 /* microseconds to delay before responding to a broadcast */
511 delay1
= casual(1, 100*1000);
514 /* election timer delay in secs. */
515 delay2
= casual(MINTOUT
, MAXTOUT
);
519 (void)_daemonize(debug
? _DF_NOFORK
|_DF_NOCHDIR
: 0, sock
, -1, -1);
527 openlog("timed", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
530 * keep returning here
532 ret
= setjmp(jmpenv
);
533 savefromnet
= fromnet
;
544 /* Just lost our master */
546 slavenet
->status
= election(slavenet
);
547 if (!slavenet
|| slavenet
->status
== MASTER
) {
551 makeslave(slavenet
); /* prune extras */
556 /* Just been told to quit */
558 pickslavenet(savefromnet
);
563 if (!(status
& MASTER
) && sock_raw
!= -1) {
564 /* sock_raw is not being used now */
565 (void)close(sock_raw
);
569 if (status
== MASTER
)
575 if (sock_raw
!= -1) {
576 (void)close(sock_raw
);
581 /* we just lost our master or were told to quit */
584 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
585 if (ntp
->status
== MASTER
)
587 ntp
->status
= NOMASTER
;
602 * suppress an upstart, untrustworthy, self-appointed master
605 suppress(addr
, name
,net
)
606 struct sockaddr_in
*addr
;
610 struct sockaddr_in tgt
;
611 char tname
[MAXHOSTNAMELEN
];
613 static struct timeval wait
;
616 fprintf(fd
, "suppress: %s\n", name
);
618 (void)strcpy(tname
, name
);
620 while (0 != readmsg(TSP_ANY
, ANYADDR
, &wait
, net
)) {
622 fprintf(fd
, "suppress:\tdiscarded packet from %s\n",
626 syslog(LOG_NOTICE
, "suppressing false master %s", tname
);
627 msg
.tsp_type
= TSP_QUIT
;
628 (void)strcpy(msg
.tsp_name
, hostname
);
629 (void)acksend(&msg
, &tgt
, tname
, TSP_ACK
, 0, 1);
636 struct tsp resp
, conflict
, *answer
;
637 struct timeval ntime
;
638 char mastername
[MAXHOSTNAMELEN
];
639 struct sockaddr_in masteraddr
;
644 /* look for master */
645 resp
.tsp_type
= TSP_MASTERREQ
;
646 (void)strcpy(resp
.tsp_name
, hostname
);
647 answer
= acksend(&resp
, &ntp
->dest_addr
, ANYADDR
,
648 TSP_MASTERACK
, ntp
, 0);
649 if (answer
!= 0 && !good_host_name(answer
->tsp_name
)) {
650 suppress(&from
, answer
->tsp_name
, ntp
);
651 ntp
->status
= NOMASTER
;
656 * Various conditions can cause conflict: races between
657 * two just started timedaemons when no master is
658 * present, or timedaemons started during an election.
659 * A conservative approach is taken. Give up and became a
660 * slave, postponing election of a master until first
663 ntime
.tv_sec
= ntime
.tv_usec
= 0;
664 answer
= readmsg(TSP_MASTERREQ
, ANYADDR
, &ntime
, ntp
);
666 if (!good_host_name(answer
->tsp_name
)) {
667 suppress(&from
, answer
->tsp_name
, ntp
);
668 ntp
->status
= NOMASTER
;
673 ntime
.tv_sec
= ntime
.tv_usec
= 0;
674 answer
= readmsg(TSP_MASTERUP
, ANYADDR
, &ntime
, ntp
);
676 if (!good_host_name(answer
->tsp_name
)) {
677 suppress(&from
, answer
->tsp_name
, ntp
);
678 ntp
->status
= NOMASTER
;
683 ntime
.tv_sec
= ntime
.tv_usec
= 0;
684 answer
= readmsg(TSP_ELECTION
, ANYADDR
, &ntime
, ntp
);
686 if (!good_host_name(answer
->tsp_name
)) {
687 suppress(&from
, answer
->tsp_name
, ntp
);
688 ntp
->status
= NOMASTER
;
694 ntp
->status
= MASTER
;
696 ntp
->status
= NOMASTER
;
701 (void)strcpy(mastername
, answer
->tsp_name
);
705 * If network has been partitioned, there might be other
706 * masters; tell the one we have just acknowledged that
707 * it has to gain control over the others.
710 ntime
.tv_usec
= 300000;
711 answer
= readmsg(TSP_MASTERACK
, ANYADDR
, &ntime
, ntp
);
713 * checking also not to send CONFLICT to ack'ed master
714 * due to duplicated MASTERACKs
716 if (answer
!= NULL
&&
717 strcmp(answer
->tsp_name
, mastername
) != 0) {
718 conflict
.tsp_type
= TSP_CONFLICT
;
719 (void)strcpy(conflict
.tsp_name
, hostname
);
720 if (!acksend(&conflict
, &masteraddr
, mastername
,
723 "error on sending TSP_CONFLICT");
729 * based on the current network configuration, set the status, and count
738 nmasternets
= nslavenets
= nnets
= nignorednets
= 0;
740 fprintf(fd
, "Net status:\n");
741 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
742 switch ((int)ntp
->status
) {
755 fprintf(fd
, "\t%-16s", inet_ntoa(ntp
->net
));
756 switch ((int)ntp
->status
) {
758 fprintf(fd
, "NOMASTER\n");
761 fprintf(fd
, "MASTER\n");
764 fprintf(fd
, "SLAVE\n");
767 fprintf(fd
, "IGNORE\n");
770 fprintf(fd
, "invalid state %d\n",
776 status
|= ntp
->status
;
781 "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
782 nnets
, nmasternets
, nslavenets
, nignorednets
, delay2
);
789 register struct netinfo
*ntp
;
791 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
792 if (ntp
->status
== SLAVE
&& ntp
!= net
)
793 ntp
->status
= IGNORE
;
799 * Try to become master over ignored nets..
804 register struct netinfo
*ntp
;
806 for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
807 if (!Mflag
&& ntp
->status
== SLAVE
)
810 if (ntp
->status
== IGNORE
|| ntp
->status
== NOMASTER
) {
812 if (!Mflag
&& ntp
->status
== SLAVE
)
819 * choose a good network on which to be a slave
820 * The ignored networks must have already been checked.
821 * Take a hint about for a good network.
827 if (slavenet
!= 0 && slavenet
->status
== SLAVE
) {
828 makeslave(slavenet
); /* prune extras */
832 if (ntp
== 0 || ntp
->status
!= SLAVE
) {
833 for (ntp
= nettab
; ntp
!= 0; ntp
= ntp
->next
) {
834 if (ntp
->status
== SLAVE
)
842 * returns a random number in the range [inf, sup]
850 value
= ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
851 return(inf
+ (sup
- inf
)*value
);
861 (void)gettimeofday(&tv
, (struct timezone
*)0);
862 (void)cftime(tm
, "%D %T", &tv
.tv_sec
);
867 (void)gettimeofday(&tv
, (struct timezone
*)0);
868 return (ctime(&tv
.tv_sec
));
876 register struct nets
**netlist
= &nets
;
879 netlist
= &((*netlist
)->next
);
880 *netlist
= (struct nets
*)malloc(sizeof **netlist
);
882 fprintf(stderr
,"malloc failed\n");
885 bzero((char *)*netlist
, sizeof(**netlist
));
886 (*netlist
)->name
= name
;
889 /* note a host as trustworthy */
891 add_good_host(name
, perm
)
893 int perm
; /* 1=not part of the netgroup */
895 register struct goodhost
*ghp
;
896 register struct hostent
*hentp
;
898 ghp
= (struct goodhost
*)malloc(sizeof(*ghp
));
900 syslog(LOG_ERR
, "malloc failed");
904 bzero((char*)ghp
, sizeof(*ghp
));
905 (void)strncpy(&ghp
->name
[0], name
, sizeof(ghp
->name
));
906 ghp
->next
= goodhosts
;
910 hentp
= gethostbyname(name
);
911 if (0 == hentp
&& perm
)
912 (void)fprintf(stderr
, "unknown host %s\n", name
);
916 /* update our image of the net-group of trustworthy hosts
922 # define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
923 static unsigned long last_update
= -NG_DELAY
;
924 unsigned long new_update
;
926 struct goodhost
*ghp
, **ghpp
;
927 char *mach
, *usr
, *dom
;
931 /* if no netgroup, then we are finished */
932 if (goodgroup
== 0 || !Mflag
)
935 /* Do not chatter with the netgroup master too often.
937 new_update
= times(&tm
);
938 if (new_update
< last_update
+ NG_DELAY
941 last_update
= new_update
;
943 /* forget the old temporary entries */
945 while (0 != (ghp
= *ghpp
)) {
955 /* quit now if we are not one of the trusted masters
957 if (!innetgr(goodgroup
, &hostname
[0], 0,0)) {
959 (void)fprintf(fd
, "get_goodgroup: %s not in %s\n",
960 &hostname
[0], goodgroup
);
964 (void)fprintf(fd
, "get_goodgroup: %s in %s\n",
965 &hostname
[0], goodgroup
);
967 /* mark the entire netgroup as trusted */
968 (void)setnetgrent(goodgroup
);
969 while (getnetgrent(&mach
,&usr
,&dom
)) {
971 add_good_host(mach
,0);
975 /* update list of slaves */
976 for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
977 htp
->good
= good_host_name(&htp
->name
[0]);
983 /* see if a machine is trustworthy
985 int /* 1=trust hp to change our date */
989 register struct goodhost
*ghp
= goodhosts
;
992 if (!ghp
|| !Mflag
) /* trust everyone if no one named */
997 if (c
== ghp
->name
[0]
998 && !strcasecmp(name
, ghp
->name
))
999 return 1; /* found him, so say so */
1000 } while (0 != (ghp
= ghp
->next
));
1002 if (!strcasecmp(name
,hostname
)) /* trust ourself */
1005 return 0; /* did not find him */