be careful with measure file pointer, multiple quits can cause problems
[unix-history] / usr / src / usr.sbin / timed / timed / master.c
/*
* Copyright (c) 1985 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#ifndef lint
static char sccsid[] = "@(#)master.c 2.14 (Berkeley) %G%";
#endif not lint
#include "globals.h"
#include <protocols/timed.h>
#include <sys/file.h>
#include <setjmp.h>
#include <utmp.h>
extern int machup;
extern int measure_delta;
extern jmp_buf jmpenv;
extern u_short sequence;
#ifdef MEASURE
int header;
FILE *fp = NULL;
#endif
/*
* 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.
*/
master()
{
int ind;
long pollingtime;
struct timeval wait;
struct timeval time;
struct timeval otime;
struct timezone tzone;
struct tsp *msg, to;
struct sockaddr_in saveaddr;
int findhost();
char *date();
struct tsp *readmsg();
struct tsp *answer, *acksend();
char olddate[32];
struct sockaddr_in server;
register struct netinfo *ntp;
#ifdef MEASURE
if (fp == NULL) {
fp = fopen("/usr/adm/timed.masterlog", "w");
setlinebuf(fp);
}
#endif
syslog(LOG_INFO, "This machine is master");
if (trace)
fprintf(fd, "THIS MACHINE IS MASTER\n");
for (ntp = nettab; ntp != NULL; ntp = ntp->next)
if (ntp->status == MASTER)
masterup(ntp);
pollingtime = 0;
loop:
(void)gettimeofday(&time, (struct timezone *)0);
if (time.tv_sec >= pollingtime) {
pollingtime = time.tv_sec + SAMPLEINTVL;
synch(0L);
for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
to.tsp_type = TSP_LOOP;
to.tsp_vers = TSPVERSION;
to.tsp_seq = sequence++;
to.tsp_hopcnt = 10;
(void)strcpy(to.tsp_name, hostname);
bytenetorder(&to);
if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
&ntp->dest_addr, sizeof(struct sockaddr_in)) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
}
}
wait.tv_sec = pollingtime - time.tv_sec;
wait.tv_usec = 0;
msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
if (msg != NULL) {
switch (msg->tsp_type) {
case TSP_MASTERREQ:
break;
case TSP_SLAVEUP:
ind = addmach(msg->tsp_name, &from);
newslave(ind, msg->tsp_seq);
break;
case TSP_SETDATE:
saveaddr = from;
/*
* the following line is necessary due to syslog
* calling ctime() which clobbers the static buffer
*/
(void)strcpy(olddate, date());
(void)gettimeofday(&time, &tzone);
otime = time;
time.tv_sec = msg->tsp_time.tv_sec;
time.tv_usec = msg->tsp_time.tv_usec;
(void)settimeofday(&time, &tzone);
syslog(LOG_NOTICE, "date changed from: %s", olddate);
logwtmp(otime, time);
msg->tsp_type = TSP_DATEACK;
msg->tsp_vers = TSPVERSION;
(void)strcpy(msg->tsp_name, hostname);
bytenetorder(msg);
if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
&saveaddr, sizeof(struct sockaddr_in)) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
spreadtime();
pollingtime = 0;
break;
case TSP_SETDATEREQ:
ind = findhost(msg->tsp_name);
if (ind < 0) {
syslog(LOG_WARNING,
"DATEREQ from uncontrolled machine");
break;
}
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);
otime = time;
time.tv_sec = msg->tsp_time.tv_sec;
time.tv_usec = msg->tsp_time.tv_usec;
(void)settimeofday(&time, &tzone);
syslog(LOG_NOTICE,
"date changed by %s from: %s",
msg->tsp_name, olddate);
logwtmp(otime, time);
spreadtime();
pollingtime = 0;
}
break;
case TSP_MSITE:
case TSP_MSITEREQ:
break;
case TSP_TRACEON:
if (!(trace)) {
fd = fopen(tracefile, "w");
setlinebuf(fd);
fprintf(fd, "Tracing started on: %s\n\n",
date());
}
trace = ON;
break;
case TSP_TRACEOFF:
if (trace) {
fprintf(fd, "Tracing ended on: %s\n", date());
(void)fclose(fd);
}
#ifdef GPROF
moncontrol(0);
_mcleanup();
moncontrol(1);
#endif
trace = OFF;
break;
case TSP_ELECTION:
to.tsp_type = TSP_QUIT;
(void)strcpy(to.tsp_name, hostname);
server = from;
answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
(struct netinfo *)NULL);
if (answer == NULL) {
syslog(LOG_ERR, "election error");
} else {
(void) addmach(msg->tsp_name, &from);
}
pollingtime = 0;
break;
case TSP_CONFLICT:
/*
* 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);
if (fromnet == NULL)
break;
for(;;) {
to.tsp_type = TSP_RESOLVE;
answer = acksend(&to, &fromnet->dest_addr,
(char *)ANYADDR, TSP_MASTERACK, fromnet);
if (answer == NULL)
break;
to.tsp_type = TSP_QUIT;
server = from;
msg = acksend(&to, &server, answer->tsp_name,
TSP_ACK, (struct netinfo *)NULL);
if (msg == NULL) {
syslog(LOG_ERR, "error on sending QUIT");
} else {
(void) addmach(answer->tsp_name, &from);
}
}
masterup(fromnet);
pollingtime = 0;
break;
case TSP_RESOLVE:
/*
* do not want to call synch() while waiting
* to be killed!
*/
(void)gettimeofday(&time, (struct timezone *)0);
pollingtime = time.tv_sec + SAMPLEINTVL;
break;
case TSP_QUIT:
/* become slave */
#ifdef MEASURE
if (fp != NULL) {
(void)fclose(fp);
fp = NULL;
}
#endif
longjmp(jmpenv, 2);
break;
case TSP_LOOP:
/*
* We should not have received this from a net
* we are master on. There must be two masters
* in this case.
*/
to.tsp_type = TSP_QUIT;
(void)strcpy(to.tsp_name, hostname);
server = from;
answer = acksend(&to, &server, msg->tsp_name, TSP_ACK,
(struct netinfo *)NULL);
if (answer == NULL) {
syslog(LOG_WARNING,
"loop breakage: no reply to QUIT");
} else {
(void)addmach(msg->tsp_name, &from);
}
default:
if (trace) {
fprintf(fd, "garbage: ");
print(msg, &from);
}
break;
}
}
goto loop;
}
/*
* `synch' synchronizes all the slaves by calling measure,
* networkdelta and correct
*/
synch(mydelta)
long mydelta;
{
int i;
int measure_status;
long netdelta;
struct timeval tack;
#ifdef MEASURE
#define MAXLINES 8
static int lines = 1;
struct timeval start, end;
#endif
int measure();
int correct();
long networkdelta();
char *date();
if (slvcount > 1) {
#ifdef MEASURE
(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);
fprintf(fp, "\n");
lines = MAXLINES;
header = OFF;
}
#endif
machup = 1;
hp[0].delta = 0;
for(i=1; i<slvcount; i++) {
tack.tv_sec = 0;
tack.tv_usec = 500000;
if ((measure_status = measure(&tack, &hp[i].addr)) <0) {
syslog(LOG_ERR, "measure: %m");
exit(1);
}
hp[i].delta = measure_delta;
if (measure_status == GOOD)
machup++;
}
if (status & SLAVE) {
/* called by a submaster */
if (trace)
fprintf(fd, "submaster correct: %d ms.\n",
mydelta);
correct(mydelta);
} else {
if (machup > 1) {
netdelta = networkdelta();
if (trace)
fprintf(fd,
"master correct: %d ms.\n",
mydelta);
correct(netdelta);
}
}
#ifdef MEASURE
gettimeofday(&end, 0);
end.tv_sec -= start.tv_sec;
end.tv_usec -= start.tv_usec;
if (end.tv_usec < 0) {
end.tv_sec -= 1;
end.tv_usec += 1000000;
}
fprintf(fp, "%d ms.\n", (end.tv_sec*1000+end.tv_usec/1000));
#endif
for(i=1; i<slvcount; i++) {
if (hp[i].delta == HOSTDOWN) {
rmmach(i);
#ifdef MEASURE
header = ON;
#endif
}
}
} else {
if (status & SLAVE) {
correct(mydelta);
}
}
}
/*
* 'spreadtime' sends the time to each slave after the master
* has received the command to set the network time
*/
spreadtime()
{
int i;
struct tsp to;
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,
(struct netinfo *)NULL);
if (answer == NULL) {
syslog(LOG_WARNING,
"no reply to SETTIME from: %s", hp[i].name);
}
}
}
findhost(name)
char *name;
{
int i;
int ind;
ind = -1;
for (i=1; i<slvcount; i++) {
if (strcmp(name, hp[i].name) == 0) {
ind = i;
break;
}
}
return(ind);
}
/*
* 'addmach' adds a host to the list of controlled machines
* if not already there
*/
addmach(name, addr)
char *name;
struct sockaddr_in *addr;
{
int ret;
int findhost();
ret = findhost(name);
if (ret < 0) {
hp[slvcount].addr = *addr;
hp[slvcount].name = (char *)malloc(MAXHOSTNAMELEN);
(void)strcpy(hp[slvcount].name, name);
hp[slvcount].seq = 0;
ret = slvcount;
if (slvcount < NHOSTS)
slvcount++;
else {
syslog(LOG_ERR, "no more slots in host table");
}
} else {
/* need to clear sequence number anyhow */
hp[ret].seq = 0;
}
#ifdef MEASURE
header = ON;
#endif
return(ret);
}
/*
* 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
* given network.
*/
rmnetmachs(ntp)
register struct netinfo *ntp;
{
int i;
if (trace)
prthp();
for (i = 1; i < slvcount; i++)
if ((hp[i].addr.sin_addr.s_addr & ntp->mask) == ntp->net)
rmmach(i--);
if (trace)
prthp();
}
/*
* remove the machine with the given index in the host table.
*/
rmmach(ind)
int ind;
{
if (trace)
fprintf(fd, "rmmach: %s\n", hp[ind].name);
free(hp[ind].name);
hp[ind] = hp[--slvcount];
}
prthp()
{
int i;
fprintf(fd, "host table:");
for (i=1; i<slvcount; i++)
fprintf(fd, " %s", hp[i].name);
fprintf(fd, "\n");
}
masterup(net)
struct netinfo *net;
{
struct timeval wait;
struct tsp to, *msg, *readmsg();
to.tsp_type = TSP_MASTERUP;
to.tsp_vers = TSPVERSION;
(void)strcpy(to.tsp_name, hostname);
bytenetorder(&to);
if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, &net->dest_addr,
sizeof(struct sockaddr_in)) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
for (;;) {
wait.tv_sec = 1;
wait.tv_usec = 0;
msg = readmsg(TSP_SLAVEUP, (char *)ANYADDR, &wait, net);
if (msg != NULL) {
(void) addmach(msg->tsp_name, &from);
} else
break;
}
}
newslave(ind, seq)
u_short seq;
{
struct tsp to;
struct tsp *answer, *acksend();
if (trace)
prthp();
if (seq == 0 || hp[ind].seq != seq) {
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
* setting the time
*/
sleep(1);
(void) gettimeofday(&to.tsp_time,
(struct timezone *)0);
answer = acksend(&to, &hp[ind].addr,
hp[ind].name, TSP_ACK,
(struct netinfo *)NULL);
if (answer == NULL) {
syslog(LOG_WARNING,
"no reply to initial SETTIME from: %s",
hp[ind].name);
rmmach(ind);
}
}
}
char *wtmpfile = "/usr/adm/wtmp";
struct utmp wtmp[2] = {
{ "|", "", "", 0 },
{ "{", "", "", 0 }
};
logwtmp(otime, ntime)
struct timeval otime, ntime;
{
int f;
wtmp[0].ut_time = otime.tv_sec + (otime.tv_usec + 500000) / 1000000;
wtmp[1].ut_time = ntime.tv_sec + (ntime.tv_usec + 500000) / 1000000;
if (wtmp[0].ut_time == wtmp[1].ut_time)
return;
if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
(void) write(f, (char *)wtmp, sizeof(wtmp));
(void) close(f);
}
}