* Copyright (c) 1985 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)master.c 2.20 (Berkeley) 3/2/91";
#include <protocols/timed.h>
extern int measure_delta
;
* The main function of `master' is to periodically compute the differences
* (deltas) between its clock and the clocks of the slaves, to compute the
* network average delta, and to send to the slaves the differences between
* their individual deltas and the network delta.
* While waiting, it receives messages from the slaves (i.e. requests for
* master's name, remote requests to set the network time, ...), and
* takes the appropriate action.
struct sockaddr_in saveaddr
;
struct tsp
*answer
, *acksend();
struct sockaddr_in server
;
register struct netinfo
*ntp
;
fp
= fopen(_PATH_MASTERLOG
, "w");
syslog(LOG_INFO
, "This machine is master");
fprintf(fd
, "THIS MACHINE IS MASTER\n");
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
)
if (ntp
->status
== MASTER
)
(void)gettimeofday(&time
, (struct timezone
*)0);
if (time
.tv_sec
>= pollingtime
) {
pollingtime
= time
.tv_sec
+ SAMPLEINTVL
;
for (ntp
= nettab
; ntp
!= NULL
; ntp
= ntp
->next
) {
to
.tsp_vers
= TSPVERSION
;
(void)strcpy(to
.tsp_name
, hostname
);
if (sendto(sock
, (char *)&to
, sizeof(struct tsp
), 0,
(struct sockaddr
*)&ntp
->dest_addr
,
sizeof(struct sockaddr_in
)) < 0) {
syslog(LOG_ERR
, "sendto: %m");
wait
.tv_sec
= pollingtime
- time
.tv_sec
;
msg
= readmsg(TSP_ANY
, (char *)ANYADDR
, &wait
, (struct netinfo
*)NULL
);
ind
= addmach(msg
->tsp_name
, &from
);
newslave(ind
, msg
->tsp_seq
);
* the following line is necessary due to syslog
* calling ctime() which clobbers the static buffer
(void)strcpy(olddate
, date());
(void)gettimeofday(&time
, &tzone
);
time
.tv_sec
= msg
->tsp_time
.tv_sec
;
time
.tv_usec
= msg
->tsp_time
.tv_usec
;
logwtmp("|", "date", "");
(void)settimeofday(&time
, &tzone
);
logwtmp("}", "date", "");
syslog(LOG_NOTICE
, "date changed from: %s", olddate
);
msg
->tsp_type
= TSP_DATEACK
;
msg
->tsp_vers
= TSPVERSION
;
(void)strcpy(msg
->tsp_name
, hostname
);
if (sendto(sock
, (char *)msg
, sizeof(struct tsp
), 0,
(struct sockaddr
*)&saveaddr
,
sizeof(struct sockaddr_in
)) < 0) {
syslog(LOG_ERR
, "sendto: %m");
ind
= findhost(msg
->tsp_name
);
"DATEREQ from uncontrolled machine");
if (hp
[ind
].seq
!= msg
->tsp_seq
) {
hp
[ind
].seq
= msg
->tsp_seq
;
* the following line is necessary due to syslog
* calling ctime() which clobbers the static buffer
(void)strcpy(olddate
, date());
(void)gettimeofday(&time
, &tzone
);
time
.tv_sec
= msg
->tsp_time
.tv_sec
;
time
.tv_usec
= msg
->tsp_time
.tv_usec
;
logwtmp("|", "date", "");
(void)settimeofday(&time
, &tzone
);
logwtmp("{", "date", "");
"date changed by %s from: %s",
fd
= fopen(tracefile
, "w");
fprintf(fd
, "Tracing started on: %s\n\n",
fprintf(fd
, "Tracing ended on: %s\n", date());
(void)strcpy(to
.tsp_name
, hostname
);
answer
= acksend(&to
, &server
, msg
->tsp_name
, TSP_ACK
,
syslog(LOG_ERR
, "election error");
(void) addmach(msg
->tsp_name
, &from
);
* After a network partition, there can be
* more than one master: the first slave to
* come up will notify here the situation.
(void)strcpy(to
.tsp_name
, hostname
);
to
.tsp_type
= TSP_RESOLVE
;
answer
= acksend(&to
, &fromnet
->dest_addr
,
(char *)ANYADDR
, TSP_MASTERACK
, fromnet
);
msg
= acksend(&to
, &server
, answer
->tsp_name
,
TSP_ACK
, (struct netinfo
*)NULL
);
syslog(LOG_ERR
, "error on sending QUIT");
(void) addmach(answer
->tsp_name
, &from
);
* do not want to call synch() while waiting
(void)gettimeofday(&time
, (struct timezone
*)0);
pollingtime
= time
.tv_sec
+ SAMPLEINTVL
;
* We should not have received this from a net
* we are master on. There must be two masters
(void)strcpy(to
.tsp_name
, hostname
);
answer
= acksend(&to
, &server
, msg
->tsp_name
, TSP_ACK
,
"loop breakage: no reply to QUIT");
(void)addmach(msg
->tsp_name
, &from
);
fprintf(fd
, "garbage: ");
* `synch' synchronizes all the slaves by calling measure,
* networkdelta and correct
struct timeval start
, end
;
(void)gettimeofday(&start
, (struct timezone
*)0);
if (header
== ON
|| --lines
== 0) {
fprintf(fp
, "%s\n", date());
for (i
=0; i
<slvcount
; i
++)
fprintf(fp
, "%.7s\t", hp
[i
].name
);
for(i
=1; i
<slvcount
; i
++) {
if ((measure_status
= measure(&tack
, &hp
[i
].addr
)) <0) {
syslog(LOG_ERR
, "measure: %m");
hp
[i
].delta
= measure_delta
;
if (measure_status
== GOOD
)
/* called by a submaster */
fprintf(fd
, "submaster correct: %d ms.\n",
netdelta
= networkdelta();
"master correct: %d ms.\n",
end
.tv_sec
-= start
.tv_sec
;
end
.tv_usec
-= start
.tv_usec
;
fprintf(fp
, "%d ms.\n", (end
.tv_sec
*1000+end
.tv_usec
/1000));
for(i
=1; i
<slvcount
; i
++) {
if (hp
[i
].delta
== HOSTDOWN
) {
* 'spreadtime' sends the time to each slave after the master
* has received the command to set the network time
struct tsp
*answer
, *acksend();
for(i
=1; i
<slvcount
; i
++) {
to
.tsp_type
= TSP_SETTIME
;
(void)strcpy(to
.tsp_name
, hostname
);
(void)gettimeofday(&to
.tsp_time
, (struct timezone
*)0);
answer
= acksend(&to
, &hp
[i
].addr
, hp
[i
].name
, TSP_ACK
,
"no reply to SETTIME from: %s", hp
[i
].name
);
for (i
=1; i
<slvcount
; i
++) {
if (strcmp(name
, hp
[i
].name
) == 0) {
* 'addmach' adds a host to the list of controlled machines
struct sockaddr_in
*addr
;
hp
[slvcount
].addr
= *addr
;
hp
[slvcount
].name
= (char *)malloc(MAXHOSTNAMELEN
);
(void)strcpy(hp
[slvcount
].name
, name
);
syslog(LOG_ERR
, "no more slots in host table");
/* need to clear sequence number anyhow */
* Remove all the machines from the host table that exist on the given
* network. This is called when a master transitions to a slave on a
register struct netinfo
*ntp
;
for (i
= 1; i
< slvcount
; i
++)
if ((hp
[i
].addr
.sin_addr
.s_addr
& ntp
->mask
) == ntp
->net
)
* remove the machine with the given index in the host table.
fprintf(fd
, "rmmach: %s\n", hp
[ind
].name
);
hp
[ind
] = hp
[--slvcount
];
fprintf(fd
, "host table:");
for (i
=1; i
<slvcount
; i
++)
fprintf(fd
, " %s", hp
[i
].name
);
struct tsp to
, *msg
, *readmsg();
to
.tsp_type
= TSP_MASTERUP
;
to
.tsp_vers
= TSPVERSION
;
(void)strcpy(to
.tsp_name
, hostname
);
if (sendto(sock
, (char *)&to
, sizeof(struct tsp
), 0,
(struct sockaddr
*)&net
->dest_addr
,
sizeof(struct sockaddr_in
)) < 0) {
syslog(LOG_ERR
, "sendto: %m");
msg
= readmsg(TSP_SLAVEUP
, (char *)ANYADDR
, &wait
, net
);
(void) addmach(msg
->tsp_name
, &from
);
struct tsp
*answer
, *acksend();
if (seq
== 0 || hp
[ind
].seq
!= seq
) {
to
.tsp_type
= TSP_SETTIME
;
(void)strcpy(to
.tsp_name
, hostname
);
* give the upcoming slave the time
* to check its input queue before
(void) gettimeofday(&to
.tsp_time
,
answer
= acksend(&to
, &hp
[ind
].addr
,
"no reply to initial SETTIME from: %s",