/* * 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) 6/5/86"; #endif not lint #include "globals.h" #include #include #include #include extern int machup; extern long 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 1) { netdelta = networkdelta(); if (trace) fprintf(fd, "master correct: %ld 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; imask) == 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; idest_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); } }