* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)timed.c 1.2 (Berkeley) %G%";
#include <protocols/timed.h>
int status
; /* either MASTER or SLAVE */
int slvcount
; /* no. of slaves controlled by master */
u_short sequence
; /* sequence number */
char hostname
[MAXHOSTNAMELEN
];
struct in_addr broadcastaddr
; /* local net broadcast address */
u_long netmask
, mynet
= 0; /* my network number & netmask */
struct sockaddr_in server
;
extern struct sockaddr_in from
;
* 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
* For problems and suggestions, please send mail to gusella@BERKELEY
char mastername
[MAXHOSTNAMELEN
];
struct netent
*getnetent();
struct sockaddr_in masteraddr
;
struct tsp resp
, conflict
, *answer
, *readmsg(), *acksend();
char *malloc(), *strcpy();
struct ifreq ifreq
, *ifr
;
fj
= "/usr/adm/timed.log";
openlog("timed", LOG_ODELAY
, LOG_DAEMON
);
fprintf(stderr
, "Timed: not superuser\n");
while (--argc
> 0 && **++argv
== '-') {
fprintf(stderr
, "timed: -%c: unknown option\n",
(void) ioctl(s
, (int)TIOCNOTTY
, (char *)0);
fprintf(fd
, "Tracing started on: %s\n\n",
openlog("timed", LOG_ODELAY
|LOG_CONS
, LOG_DAEMON
);
srvp
= getservbyname("timed", "udp");
syslog(LOG_CRIT
, "unknown service 'timed/udp'");
server
.sin_port
= srvp
->s_port
;
server
.sin_family
= AF_INET
;
sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
syslog(LOG_ERR
, "socket: %m");
if (setsockopt(sock
, SOL_SOCKET
, SO_BROADCAST
, (char *)&on
,
syslog(LOG_ERR
, "setsockopt: %m");
if (bind(sock
, &server
, sizeof(server
))) {
syslog(LOG_ERR
, "server already running");
syslog(LOG_ERR
, "bind: %m");
/* choose a unique seed for random number generation */
(void)gettimeofday(&time
, (struct timezone
*)0);
seed
= time
.tv_sec
+ time
.tv_usec
;
sequence
= casual((long)1, (long)MAXSEQ
); /* initial seq number */
/* rounds kernel variable time to multiple of 5 ms. */
time
.tv_usec
= -((time
.tv_usec
/1000) % 5) * 1000;
(void)adjtime(&time
, (struct timeval
*)0);
if (gethostname(hostname
, sizeof(hostname
) - 1) < 0) {
syslog(LOG_ERR
, "gethostname: %m");
localnet
= getnetbyname(netname
);
syslog(LOG_ERR
, "getnetbyname: unknown net %s",
ifc
.ifc_len
= sizeof(buf
);
if (ioctl(sock
, (int)SIOCGIFCONF
, (char *)&ifc
) < 0) {
syslog(LOG_ERR
, "get interface configuration: %m");
for (n
= ifc
.ifc_len
/sizeof(struct ifreq
); n
> 0; n
--, ifr
++) {
if (ioctl(sock
, (int)SIOCGIFFLAGS
,
syslog(LOG_ERR
, "get interface flags: %m");
if ((ifreq
.ifr_flags
& IFF_UP
) == 0 ||
(ifreq
.ifr_flags
& IFF_BROADCAST
) == 0) {
if (ioctl(sock
, (int)SIOCGIFNETMASK
,
syslog(LOG_ERR
, "get broadaddr: %m");
netmask
= ((struct sockaddr_in
*)
&ifreq
.ifr_addr
)->sin_addr
.s_addr
;
if (ioctl(sock
, (int)SIOCGIFBRDADDR
,
syslog(LOG_ERR
, "get broadaddr: %m");
n_addrlen
= sizeof(ifr
->ifr_addr
);
n_addr
= (char *)malloc((unsigned)n_addrlen
);
bcopy((char *)&ifreq
.ifr_broadaddr
, n_addr
, n_addrlen
);
sin
= (struct sockaddr_in
*)n_addr
;
broadcastaddr
= sin
->sin_addr
;
addr
= ntohl(broadcastaddr
.s_addr
);
while ((mask
& 1) == 0) {
if (addr
!= localnet
->n_net
)
mynet
= netmask
& broadcastaddr
.s_addr
;
syslog(LOG_ERR
, "No network usable");
/* us. delay to be used in response to broadcast */
delay1
= casual((long)10000, 200000);
/* election timer delay in secs. */
delay2
= casual((long)MINTOUT
, (long)MAXTOUT
);
resp
.tsp_type
= TSP_MASTERREQ
;
(void)strcpy(resp
.tsp_name
, hostname
);
answer
= acksend(&resp
, (char *)ANYADDR
, TSP_MASTERACK
);
(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
, (char *)ANYADDR
, &time
);
* 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
, (char *)mastername
,
syslog(LOG_ERR
, "error on sending TSP_CONFLICT");
/* open raw socket used to measure time differences */
sock_raw
= socket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
);
syslog(LOG_ERR
, "opening raw socket: %m");
* number (increased by 1) of slaves controlled by master:
* used in master.c, candidate.c, networkdelta.c, and
* Various conditions can cause conflict: race between
* two just started timedaemons when no master is present,
* or timedaemon started during an election.
* Conservative approach is taken: give up and became a
* slave postponing election of a master until first
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERREQ
, (char *)ANYADDR
, &time
);
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_MASTERUP
, (char *)ANYADDR
, &time
);
time
.tv_sec
= time
.tv_usec
= 0;
answer
= readmsg(TSP_ELECTION
, (char *)ANYADDR
, &time
);
/* this should not happen */
syslog(LOG_ERR
, "Attempt to enter invalid state");
/* if Mflag is not set timedaemon is forced to act as a slave */
resp
.tsp_type
= TSP_SLAVEUP
;
(void)strcpy(resp
.tsp_name
, hostname
);
* `casual' returns a random number in the range [inf, sup]
value
= (float)(random() & 0x7fffffff) / 0x7fffffff;
return(inf
+ (sup
- inf
) * value
);
(void)gettimeofday(&tv
, (struct timezone
*)0);
tp
= localtime((time_t *)&tv
.tv_sec
);