fix core dumps when using MEASURE
[unix-history] / usr / src / usr.sbin / timed / timed / slave.c
/*
* Copyright (c) 1983 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[] = "@(#)slave.c 2.5 (Berkeley) %G%";
#endif not lint
#include "globals.h"
#include <protocols/timed.h>
#include <setjmp.h>
extern jmp_buf jmpenv;
slave()
{
int length;
int senddateack;
long electiontime, refusetime, looktime;
u_short seq;
char candidate[MAXHOSTNAMELEN];
struct tsp *msg, to, *readmsg();
struct sockaddr_in saveaddr, msaveaddr;
struct timeval wait;
struct timeval time, mytime;
struct tsp *answer, *acksend();
int timeout();
char *date();
long casual();
int bytenetorder();
char olddate[32];
struct sockaddr_in server;
register struct netinfo *ntp;
int ind;
struct tsp resp;
extern int Mflag;
#ifdef MEASURE
int tempstat;
extern FILE *fp;
#endif
if (slavenet) {
resp.tsp_type = TSP_SLAVEUP;
resp.tsp_vers = TSPVERSION;
(void)strcpy(resp.tsp_name, hostname);
bytenetorder(&resp);
if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0,
&slavenet->dest_addr, sizeof(struct sockaddr_in)) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
}
if (status & MASTER) {
#ifdef MEASURE
fp = fopen("/usr/adm/timed.masterlog", "w");
setlinebuf(fp);
#endif
syslog(LOG_NOTICE, "THIS MACHINE IS A SUBMASTER");
if (trace) {
fprintf(fd, "THIS MACHINE IS A SUBMASTER\n");
}
for (ntp = nettab; ntp != NULL; ntp = ntp->next)
if (ntp->status == MASTER)
masterup(ntp);
} else {
syslog(LOG_NOTICE, "THIS MACHINE IS A SLAVE");
if (trace) {
fprintf(fd, "THIS MACHINE IS A SLAVE\n");
}
}
seq = 0;
senddateack = OFF;
refusetime = 0;
(void)gettimeofday(&time, (struct timezone *)0);
electiontime = time.tv_sec + delay2;
if (Mflag && nignorednets > 0)
looktime = time.tv_sec + delay2;
else
looktime = 0;
loop:
length = sizeof(struct sockaddr_in);
(void)gettimeofday(&time, (struct timezone *)0);
if (time.tv_sec > electiontime) {
if (trace)
fprintf(fd, "election timer expired\n");
longjmp(jmpenv, 1);
}
if (looktime && time.tv_sec > looktime) {
if (trace)
fprintf(fd, "Looking for nets to master\n");
for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
if (ntp->status == IGNORE) {
lookformaster(ntp);
if (ntp->status == MASTER)
masterup(ntp);
else
ntp->status = IGNORE;
}
}
#ifdef MEASURE
tempstat = status;
#endif
setstatus();
#ifdef MEASURE
/*
* Check to see if we just became master
*/
if ((status & MASTER) && !(tempstat & MASTER)) {
fp = fopen("/usr/adm/timed.masterlog", "w");
setlinebuf(fp);
}
#endif
if (nignorednets > 0) {
(void)gettimeofday(&time, (struct timezone *)0);
looktime = time.tv_sec + delay2;
} else
looktime = 0;
}
wait.tv_sec = electiontime - time.tv_sec + 10;
wait.tv_usec = 0;
msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
if (msg != NULL) {
switch (msg->tsp_type) {
case TSP_DATE:
#ifdef TESTING
case TSP_TEST:
#endif
case TSP_MSITE:
case TSP_TRACEOFF:
case TSP_TRACEON:
break;
case TSP_MASTERUP:
if (fromnet == NULL) {
if (trace) {
fprintf(fd, "slave ignored: ");
print(msg, &from);
}
goto loop;
}
break;
default:
if (fromnet == NULL || fromnet->status == IGNORE) {
if (trace) {
fprintf(fd, "slave ignored: ");
print(msg, &from);
}
goto loop;
}
break;
}
switch (msg->tsp_type) {
case TSP_ADJTIME:
if (fromnet->status != SLAVE)
break;
(void)gettimeofday(&time, (struct timezone *)0);
electiontime = time.tv_sec + delay2;
if (seq != msg->tsp_seq) {
seq = msg->tsp_seq;
if ((status & SUBMASTER) == SUBMASTER) {
synch((msg->tsp_time.tv_sec * 1000) +
(msg->tsp_time.tv_usec / 1000));
} else {
adjclock(&(msg->tsp_time));
}
}
break;
case TSP_SETTIME:
if (fromnet->status != SLAVE)
break;
if (seq == msg->tsp_seq)
break;
seq = msg->tsp_seq;
(void)strcpy(olddate, date());
(void)settimeofday(&msg->tsp_time,
(struct timezone *)0);
syslog(LOG_NOTICE, "date changed by %s from: %s",
msg->tsp_name, olddate);
if ((status & SUBMASTER) == SUBMASTER)
spreadtime();
electiontime = time.tv_sec + delay2;
if (senddateack == ON) {
senddateack = OFF;
msg->tsp_type = TSP_DATEACK;
(void)strcpy(msg->tsp_name, hostname);
bytenetorder(msg);
length = sizeof(struct sockaddr_in);
if (sendto(sock, (char *)msg,
sizeof(struct tsp), 0,
&saveaddr, length) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
}
break;
case TSP_MASTERUP:
if (slavenet && fromnet != slavenet)
break;
makeslave(fromnet);
setstatus();
msg->tsp_type = TSP_SLAVEUP;
msg->tsp_vers = TSPVERSION;
(void)strcpy(msg->tsp_name, hostname);
bytenetorder(msg);
answerdelay();
length = sizeof(struct sockaddr_in);
if (sendto(sock, (char *)msg, sizeof(struct tsp), 0,
&from, length) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
backoff = 1;
delay2 = casual((long)MINTOUT, (long)MAXTOUT);
(void)gettimeofday(&time, (struct timezone *)0);
electiontime = time.tv_sec + delay2;
refusetime = 0;
break;
case TSP_MASTERREQ:
if (fromnet->status != SLAVE)
break;
(void)gettimeofday(&time, (struct timezone *)0);
electiontime = time.tv_sec + delay2;
break;
case TSP_DATE:
saveaddr = from;
msg->tsp_time.tv_usec = 0;
msg->tsp_type = TSP_DATEREQ;
msg->tsp_vers = TSPVERSION;
(void)strcpy(msg->tsp_name, hostname);
for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
if (ntp->status == SLAVE)
break;
}
if (ntp == NULL)
break;
answer = acksend(msg, &ntp->dest_addr, (char *)ANYADDR,
TSP_DATEACK, ntp);
if (answer != NULL) {
msg->tsp_type = TSP_ACK;
bytenetorder(msg);
length = sizeof(struct sockaddr_in);
if (sendto(sock, (char *)msg,
sizeof(struct tsp), 0, &saveaddr,
length) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
senddateack = ON;
}
break;
case TSP_DATEREQ:
saveaddr = from;
if (status != SUBMASTER || fromnet->status != MASTER)
break;
for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
if (ntp->status == SLAVE)
break;
}
answer = acksend(msg, &ntp->dest_addr, (char *)ANYADDR,
TSP_DATEACK, ntp);
if (answer != NULL) {
msg->tsp_type = TSP_DATEACK;
bytenetorder(msg);
length = sizeof(struct sockaddr_in);
if (sendto(sock, (char *)msg,
sizeof(struct tsp), 0, &saveaddr,
length) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
}
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_SLAVEUP:
if ((status & MASTER) && fromnet->status == MASTER) {
ind = addmach(msg->tsp_name, &from);
newslave(ind, msg->tsp_seq);
}
break;
case TSP_ELECTION:
if (fromnet->status == SLAVE) {
(void)gettimeofday(&time, (struct timezone *)0);
electiontime = time.tv_sec + delay2;
seq = 0; /* reset sequence number */
if (time.tv_sec < refusetime)
msg->tsp_type = TSP_REFUSE;
else {
msg->tsp_type = TSP_ACCEPT;
refusetime = time.tv_sec + 30;
}
(void)strcpy(candidate, msg->tsp_name);
(void)strcpy(msg->tsp_name, hostname);
answerdelay();
server = from;
answer = acksend(msg, &server, candidate, TSP_ACK,
(struct netinfo *)NULL);
if (answer == NULL) {
syslog(LOG_ERR, "problem in election\n");
}
}
else { /* fromnet->status == MASTER */
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);
}
}
break;
case TSP_CONFLICT:
if (fromnet->status != MASTER)
break;
/*
* 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_MASTERACK, (struct netinfo *)NULL);
if (msg == NULL) {
syslog(LOG_ERR, "error on sending QUIT");
} else {
(void) addmach(answer->tsp_name, &from); }
}
masterup(fromnet);
break;
case TSP_MSITE:
if (!slavenet)
break;
msaveaddr = from;
msg->tsp_type = TSP_MSITEREQ;
msg->tsp_vers = TSPVERSION;
(void)strcpy(msg->tsp_name, hostname);
answer = acksend(msg, &slavenet->dest_addr,
(char *)ANYADDR, TSP_ACK, slavenet);
if (answer != NULL) {
msg->tsp_type = TSP_ACK;
length = sizeof(struct sockaddr_in);
bytenetorder(msg);
if (sendto(sock, (char *)msg,
sizeof(struct tsp), 0,
&msaveaddr, length) < 0) {
syslog(LOG_ERR, "sendto: %m");
exit(1);
}
}
break;
case TSP_ACCEPT:
case TSP_REFUSE:
break;
case TSP_RESOLVE:
break;
case TSP_QUIT:
/* become slave */
#ifdef MEASURE
(void)fclose(fp);
#endif
longjmp(jmpenv, 2);
break;
#ifdef TESTING
case TSP_TEST:
electiontime = 0;
break;
#endif
case TSP_MSITEREQ:
if (status & MASTER)
break;
if (trace) {
fprintf(fd, "garbage: ");
print(msg, &from);
}
break;
default:
if (trace) {
fprintf(fd, "garbage: ");
print(msg, &from);
}
break;
}
}
goto loop;
}
/*
* Used before answering a broadcast message to avoid network
* contention and likely collisions.
*/
answerdelay()
{
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = delay1;
(void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
return;
}