* Copyright (c
) 1985 Regents of the University of California
.
* %sccs
.include
.redist
.c
%
"@(#) Copyright (c) 1985 Regents of the University of California.\n\
static char sccsid
[] = "@(#)timed.c 5.1 (Berkeley) %G%";
#ident "$Revision: 1.25 $"
#include <sys/schedctl.h>
u_short sequence
; /* sequence number */
int nslavenets
; /* nets were I could be a slave */
int nmasternets
; /* nets were I could be a master */
int nignorednets
; /* ignored nets */
int nnets
; /* nets I am connected to */
FILE *fd
; /* trace file FD */
struct netinfo
*nettab
= 0;
struct netinfo
*slavenet
;
struct hosttbl hosttbl
[NHOSTS
+1]; /* known hosts */
static struct goodhost
{ /* hosts that we trust */
char name
[MAXHOSTNAMELEN
+1];
static char *goodgroup
; /* net group of trusted hosts */
static void checkignorednets
__P((void));
static void pickslavenet
__P((struct netinfo
*));
static void add_good_host
__P((char *, int));
char *timetrim_wpat
= "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
char *timetrim_rpat
= "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
double tot_adj
, hr_adj
; /* totals in nsec */
double tot_ticks
, hr_ticks
;
* The timedaemons synchronize the clocks of hosts in a local area network.
* One daemon runs as master, all the others as slaves. The master
* performs the task of computing clock differences and sends correction
* Slaves start an election to choose a new master when the latter disappears
* because of a machine crash, network partition, or when killed.
* A resolution protocol is used to kill all but one of the masters
* that happen to exist in segments of a partitioned network when the
* network partition is fixed.
* Authors: Riccardo Gusella & Stefano Zatti
* overhauled at Silicon Graphics
char buf
[BUFSIZ
], *cp
, *cplim
;
struct ifreq ifreq
, ifreqf
, *ifr
;
register struct netinfo
*ntp
;
struct netinfo
*savefromnet
;
struct sockaddr_in server
;
extern int optind
, opterr
;
#define IN_MSG "timed: -i and -n make no sense together\n"
#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp] [-P trimfile]\n"
#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n"
#define USAGE "timed: [-dtM] [-i net|-n net] [-F host1 host2 ...]\n"
if (0 > syssgi(SGI_GETTIMETRIM
, &timetrim
)) {
perror("timed: syssgi(GETTIMETRIM)");
tot_ticks
= hr_ticks
= times(&tms
);
while ((c
= getopt(argc
, argv
, "Mtdn:i:F:G:P:")) != EOF
) {
while (optind
< argc
&& argv
[optind
][0] != '-')
add_good_host(argv
[optind
++], 1);
fprintf(stderr
,"timed: only one net group\n");
timetrim_st
= fopen(timetrim_fn
, "r+");
(void)fprintf(stderr
,"timed: ");
i
= fscanf(timetrim_st
, timetrim_rpat
,
&& trim
!= rint(adj
*CLK_TCK
/ticks
))) {
"timed: unrecognized contents in %s\n",
if (0 > syssgi(SGI_SETTIMETRIM
,
perror("timed: syssgi(SETTIMETRIM)");
(void)fclose(timetrim_st
);
if (gethostname(hostname
, sizeof(hostname
) - 1) < 0) {
if (goodhosts
!= 0) /* trust ourself */
add_good_host(hostname
,1);
srvp
= getservbyname("timed", "udp");
fprintf(stderr
, "unknown service 'timed/udp'\n");
server
.sin_port
= srvp
->s_port
;
server
.sin_family
= AF_INET
;
sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
if (setsockopt(sock
, SOL_SOCKET
, SO_BROADCAST
, (char *)&on
,
if (bind(sock
, (struct sockaddr
*)&server
, sizeof(server
))) {
fprintf(stderr
,"timed: time daemon already running\n");
* handle many slaves with our buffer
if (0 > setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, (char*)&bufspace
,
/* choose a unique seed for random number generation */
(void)gettimeofday(&ntime
, 0);
srandom(ntime
.tv_sec
+ ntime
.tv_usec
);
sequence
= random(); /* initial seq number */
/* rounds kernel variable time to multiple of 5 ms. */
ntime
.tv_usec
= -((ntime
.tv_usec
/1000) % 5) * 1000;
(void)adjtime(&ntime
, (struct timeval
*)0);
for (nt
= nets
; nt
; nt
= nt
->next
) {
nentp
= getnetbyname(nt
->name
);
nt
->net
= inet_network(nt
->name
);
if (nt
->net
!= INADDR_NONE
)
nentp
= getnetbyaddr(nt
->net
, AF_INET
);
} else if (nt
->net
== INADDR_NONE
) {
fprintf(stderr
, "timed: unknown net %s\n", nt
->name
);
} else if (nt
->net
== INADDR_ANY
) {
fprintf(stderr
, "timed: bad net %s\n", nt
->name
);
"timed: warning: %s unknown in /etc/networks\n",
if (0 == (nt
->net
& 0xff000000))
if (0 == (nt
->net
& 0xff000000))
if (0 == (nt
->net
& 0xff000000))
ifc
.ifc_len
= sizeof(buf
);
if (ioctl(sock
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
perror("timed: get interface configuration");
#define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */
#define size(p) max((p).sa_len, sizeof(p))
cplim
= buf
+ ifc
.ifc_len
; /*skip over if's with big ifr_addr's */
for (cp
= buf
; cp
< cplim
;
cp
+= sizeof (ifr
->ifr_name
) + size(ifr
->ifr_addr
)) {
ifr
= (struct ifreq
*)cp
;
if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
ntp
= (struct netinfo
*)malloc(sizeof(struct netinfo
));
ntp
->my_addr
=((struct sockaddr_in
*)&ifr
->ifr_addr
)->sin_addr
;
if (ioctl(sock
, SIOCGIFFLAGS
, (char *)&ifreqf
) < 0) {
perror("get interface flags");
if ((ifreqf
.ifr_flags
& IFF_UP
) == 0)
if ((ifreqf
.ifr_flags
& IFF_BROADCAST
) == 0 &&
(ifreqf
.ifr_flags
& IFF_POINTOPOINT
) == 0) {
if (ioctl(sock
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0) {
ntp
->mask
= ((struct sockaddr_in
*)
&ifreq
.ifr_addr
)->sin_addr
.s_addr
;
if (ifreqf
.ifr_flags
& IFF_BROADCAST
) {
if (ioctl(sock
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_broadaddr
;
/* What if the broadcast address is all ones?
* So we cannot just mask ntp->dest_addr. */
ntp
->net
.s_addr
&= ntp
->mask
;
if (ioctl(sock
, SIOCGIFDSTADDR
,
ntp
->dest_addr
= *(struct sockaddr_in
*)&ifreq
.ifr_dstaddr
;
ntp
->net
= ntp
->dest_addr
.sin_addr
;
ntp
->dest_addr
.sin_port
= port
;
for (nt
= nets
; nt
; nt
= nt
->next
) {
if (ntp
->net
.s_addr
== nt
->net
)
if (nflag
&& !nt
|| iflag
&& nt
)
(void) free((char *)ntp
);
fprintf(stderr
, "timed: no network usable\n");
(void)schedctl(RENICE
,0,10); /* run fast to get good time */
/* ticks to delay before responding to a broadcast */
delay1
= casual(0, CLK_TCK
/10);
/* microseconds to delay before responding to a broadcast */
delay1
= casual(1, 100*1000);
/* election timer delay in secs. */
delay2
= casual(MINTOUT
, MAXTOUT
);
for (s
= getdtablesize(); s
>= 0; --s
)
(void) open("/dev/null", 0);
(void)_daemonize(debug
? _DF_NOFORK
|_DF_NOCHDIR
: 0, sock
, -1, -1);
openlog("timed", LOG_CONS
|LOG_PID
, LOG_DAEMON
);
/* Just lost our master */
slavenet
->status
= election(slavenet
);
if (!slavenet
|| slavenet
->status
== MASTER
) {
makeslave(slavenet
); /* prune extras */
/* Just been told to quit */
pickslavenet(savefromnet
);
if (!(status
& MASTER
) && sock_raw
!= -1) {
/* sock_raw is not being used now */
/* we just lost our master or were told to quit */
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
if (ntp
->status
== MASTER
)
* suppress an upstart, untrustworthy, self-appointed master
struct sockaddr_in
*addr
;
char tname
[MAXHOSTNAMELEN
];
static struct timeval wait
;
fprintf(fd
, "suppress: %s\n", name
);
(void)strcpy(tname
, name
);
while (0 != readmsg(TSP_ANY
, ANYADDR
, &wait
, net
)) {
fprintf(fd
, "suppress:\tdiscarded packet from %s\n",
syslog(LOG_NOTICE
, "suppressing false master %s", tname
);
(void)strcpy(msg
.tsp_name
, hostname
);
(void)acksend(&msg
, &tgt
, tname
, TSP_ACK
, 0, 1);
struct tsp resp
, conflict
, *answer
;
char mastername
[MAXHOSTNAMELEN
];
struct sockaddr_in masteraddr
;
resp
.tsp_type
= TSP_MASTERREQ
;
(void)strcpy(resp
.tsp_name
, hostname
);
answer
= acksend(&resp
, &ntp
->dest_addr
, ANYADDR
,
if (answer
!= 0 && !good_host_name(answer
->tsp_name
)) {
suppress(&from
, answer
->tsp_name
, ntp
);
* Various conditions can cause conflict: races between
* two just started timedaemons when no master is
* present, or timedaemons started during an election.
* A conservative approach is taken. Give up and became a
* slave, postponing election of a master until first
ntime
.tv_sec
= ntime
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERREQ
, ANYADDR
, &ntime
, ntp
);
if (!good_host_name(answer
->tsp_name
)) {
suppress(&from
, answer
->tsp_name
, ntp
);
ntime
.tv_sec
= ntime
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERUP
, ANYADDR
, &ntime
, ntp
);
if (!good_host_name(answer
->tsp_name
)) {
suppress(&from
, answer
->tsp_name
, ntp
);
ntime
.tv_sec
= ntime
.tv_usec
= 0;
answer
= readmsg(TSP_ELECTION
, ANYADDR
, &ntime
, ntp
);
if (!good_host_name(answer
->tsp_name
)) {
suppress(&from
, answer
->tsp_name
, ntp
);
(void)strcpy(mastername
, answer
->tsp_name
);
* If network has been partitioned, there might be other
* masters; tell the one we have just acknowledged that
* it has to gain control over the others.
answer
= readmsg(TSP_MASTERACK
, ANYADDR
, &ntime
, ntp
);
* checking also not to send CONFLICT to ack'ed master
* due to duplicated MASTERACKs
strcmp(answer
->tsp_name
, mastername
) != 0) {
conflict
.tsp_type
= TSP_CONFLICT
;
(void)strcpy(conflict
.tsp_name
, hostname
);
if (!acksend(&conflict
, &masteraddr
, mastername
,
"error on sending TSP_CONFLICT");
* based on the current network configuration, set the status, and count
nmasternets
= nslavenets
= nnets
= nignorednets
= 0;
fprintf(fd
, "Net status:\n");
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
switch ((int)ntp
->status
) {
fprintf(fd
, "\t%-16s", inet_ntoa(ntp
->net
));
switch ((int)ntp
->status
) {
fprintf(fd
, "NOMASTER\n");
fprintf(fd
, "invalid state %d\n",
"\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
nnets
, nmasternets
, nslavenets
, nignorednets
, delay2
);
register struct netinfo
*ntp
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
if (ntp
->status
== SLAVE
&& ntp
!= net
)
* Try to become master over ignored nets..
register struct netinfo
*ntp
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
if (!Mflag
&& ntp
->status
== SLAVE
)
if (ntp
->status
== IGNORE
|| ntp
->status
== NOMASTER
) {
if (!Mflag
&& ntp
->status
== SLAVE
)
* choose a good network on which to be a slave
* The ignored networks must have already been checked.
* Take a hint about for a good network.
if (slavenet
!= 0 && slavenet
->status
== SLAVE
) {
makeslave(slavenet
); /* prune extras */
if (ntp
== 0 || ntp
->status
!= SLAVE
) {
for (ntp
= nettab
; ntp
!= 0; ntp
= ntp
->next
) {
if (ntp
->status
== SLAVE
)
* returns a random number in the range [inf, sup]
value
= ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
return(inf
+ (sup
- inf
)*value
);
(void)gettimeofday(&tv
, (struct timezone
*)0);
(void)cftime(tm
, "%D %T", &tv
.tv_sec
);
(void)gettimeofday(&tv
, (struct timezone
*)0);
return (ctime(&tv
.tv_sec
));
register struct nets
**netlist
= &nets
;
netlist
= &((*netlist
)->next
);
*netlist
= (struct nets
*)malloc(sizeof **netlist
);
fprintf(stderr
,"malloc failed\n");
bzero((char *)*netlist
, sizeof(**netlist
));
/* note a host as trustworthy */
add_good_host(name
, perm
)
int perm
; /* 1=not part of the netgroup */
register struct goodhost
*ghp
;
register struct hostent
*hentp
;
ghp
= (struct goodhost
*)malloc(sizeof(*ghp
));
syslog(LOG_ERR
, "malloc failed");
bzero((char*)ghp
, sizeof(*ghp
));
(void)strncpy(&ghp
->name
[0], name
, sizeof(ghp
->name
));
hentp
= gethostbyname(name
);
(void)fprintf(stderr
, "unknown host %s\n", name
);
/* update our image of the net-group of trustworthy hosts
# define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
static unsigned long last_update
= -NG_DELAY
;
unsigned long new_update
;
struct goodhost
*ghp
, **ghpp
;
/* if no netgroup, then we are finished */
if (goodgroup
== 0 || !Mflag
)
/* Do not chatter with the netgroup master too often.
if (new_update
< last_update
+ NG_DELAY
last_update
= new_update
;
/* forget the old temporary entries */
while (0 != (ghp
= *ghpp
)) {
/* quit now if we are not one of the trusted masters
if (!innetgr(goodgroup
, &hostname
[0], 0,0)) {
(void)fprintf(fd
, "get_goodgroup: %s not in %s\n",
&hostname
[0], goodgroup
);
(void)fprintf(fd
, "get_goodgroup: %s in %s\n",
&hostname
[0], goodgroup
);
/* mark the entire netgroup as trusted */
(void)setnetgrent(goodgroup
);
while (getnetgrent(&mach
,&usr
,&dom
)) {
/* update list of slaves */
for (htp
= self
.l_fwd
; htp
!= &self
; htp
= htp
->l_fwd
) {
htp
->good
= good_host_name(&htp
->name
[0]);
/* see if a machine is trustworthy
int /* 1=trust hp to change our date */
register struct goodhost
*ghp
= goodhosts
;
if (!ghp
|| !Mflag
) /* trust everyone if no one named */
&& !strcasecmp(name
, ghp
->name
))
return 1; /* found him, so say so */
} while (0 != (ghp
= ghp
->next
));
if (!strcasecmp(name
,hostname
)) /* trust ourself */
return 0; /* did not find him */