/* * Acksend implements reliable datagram transmission by using sequence * numbers and retransmission when necessary. * If `name' is ANYADDR, this routine implements reliable broadcast. * * Because this function calls readmsg(), none of its args may be in * a message provided by readmsg(). */ struct tsp * acksend(struct tsp *message, /* this message */ struct sockaddr_in *addr, /* to here */ char *name, int ack, /* look for this ack */ struct netinfo *net, /* receive from this network */ int bad) /* 1=losing patience */ { struct timeval twait; int count; long msec; message->tsp_vers = TSPVERSION; message->tsp_seq = sequence; if (trace) { fprintf(fd, "acksend: to %s: ", (name == ANYADDR ? "broadcast" : name)); print(message, addr); } bytenetorder(message); msec = 200; count = bad ? 1 : 5; /* 5 packets in 6.4 seconds */ answer = NULL; do { if (!answer) { /* do not go crazy transmitting just because the * other guy cannot keep our sequence numbers * straight. */ if (sendto(sock, (char *)message, sizeof(struct tsp), 0, (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) { trace_sendto_err(addr->sin_addr); break; } } mstotvround(&twait, msec); answer = readmsg(ack, name, &twait, net); if (answer != NULL) { if (answer->tsp_seq != sequence) { if (trace) fprintf(fd,"acksend: seq # %u!=%u\n", answer->tsp_seq, sequence); continue; } break; } msec *= 2; } while (--count > 0); sequence++; return(answer); }
void xmit(int type, u_int seq, struct sockaddr_in *addr) { static struct tsp msg; msg.tsp_type = type; msg.tsp_seq = seq; msg.tsp_vers = TSPVERSION; (void)strcpy(msg.tsp_name, hostname); bytenetorder(&msg); if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0, (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) { trace_sendto_err(addr->sin_addr); } }
/* * Causes the election timer to expire on the selected hosts * It sends just one udp message per machine, relying on * reliability of communication channel. */ void testing(int argc, char *argv[]) { struct servent *srvp; struct sockaddr_in sin; struct tsp msg; if (argc < 2) { printf("usage: timedc election host1 [host2 ...]\n"); return; } srvp = getservbyname("timed", "udp"); if (srvp == NULL) { warnx("timed/udp: unknown service"); return; } while (argc > 1) { argc--; argv++; hp = gethostbyname(*argv); if (hp == NULL) { warnx("%s: %s", *argv, hstrerror(h_errno)); argc--; argv++; continue; } sin.sin_port = srvp->s_port; sin.sin_family = hp->h_addrtype; bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length); msg.tsp_type = TSP_TEST; msg.tsp_vers = TSPVERSION; if (gethostname(myname, sizeof(myname) - 1) < 0) err(1, "gethostname"); (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); bytenetorder(&msg); if (sendto(sock, &msg, sizeof(struct tsp), 0, (struct sockaddr*)&sin, sizeof(struct sockaddr)) < 0) { warn("sendto"); } } }
/* * Enables or disables tracing on local timedaemon */ void tracing(int argc, char *argv[]) { int onflag; int length; ssize_t cc; fd_set ready; struct sockaddr_in dest; struct sockaddr_in from; struct timeval tout; struct tsp msg; struct servent *srvp; if (argc != 2) { printf("usage: timedc trace { on | off }\n"); return; } srvp = getservbyname("timed", "udp"); if (srvp == NULL) { warnx("timed/udp: unknown service"); return; } dest.sin_port = srvp->s_port; dest.sin_family = AF_INET; if (gethostname(myname, sizeof(myname) - 1) < 0) err(1, "gethostname"); hp = gethostbyname(myname); bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); if (strcmp(argv[1], "on") == 0) { msg.tsp_type = TSP_TRACEON; onflag = ON; } else { msg.tsp_type = TSP_TRACEOFF; onflag = OFF; } (void)strcpy(msg.tsp_name, myname); msg.tsp_vers = TSPVERSION; bytenetorder(&msg); if (sendto(sock, &msg, sizeof(struct tsp), 0, (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) { warn("sendto"); return; } tout.tv_sec = 5; tout.tv_usec = 0; FD_ZERO(&ready); FD_SET(sock, &ready); if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { length = sizeof(from); cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, (struct sockaddr *)&from, &length); if (cc < 0) { warn("recvfrom"); return; } /* * The 4.3BSD protocol spec had a 32-byte tsp_name field, and * this is still OS-dependent. Demand that the packet is at * least long enough to hold a 4.3BSD packet. */ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) { fprintf(stderr, "short packet (%zd/%zu bytes) from %s\n", cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32, inet_ntoa(from.sin_addr)); return; } bytehostorder(&msg); if (msg.tsp_type == TSP_ACK) if (onflag) printf("timed tracing enabled\n"); else printf("timed tracing disabled\n"); else { if (msg.tsp_type >= TSPTYPENUMBER) printf("unknown ack received: %u\n", msg.tsp_type); else printf("wrong ack received: %s\n", tsptype[msg.tsp_type]); } } else printf("communication error\n"); }
/* * finds location of master timedaemon */ void msite(int argc, char *argv[]) { ssize_t cc; fd_set ready; struct sockaddr_in dest; int i, length; struct sockaddr_in from; struct timeval tout; struct tsp msg; struct servent *srvp; char *tgtname; if (argc < 1) { printf("usage: timedc msite [host ...]\n"); return; } srvp = getservbyname("timed", "udp"); if (srvp == NULL) { warnx("timed/udp: unknown service"); return; } dest.sin_port = srvp->s_port; dest.sin_family = AF_INET; if (gethostname(myname, sizeof(myname) - 1) < 0) err(1, "gethostname"); i = 1; do { tgtname = (i >= argc) ? myname : argv[i]; hp = gethostbyname(tgtname); if (hp == NULL) { warnx("%s: %s", tgtname, hstrerror(h_errno)); continue; } bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); msg.tsp_type = TSP_MSITE; msg.tsp_vers = TSPVERSION; bytenetorder(&msg); if (sendto(sock, &msg, sizeof(struct tsp), 0, (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) { warn("sendto"); continue; } tout.tv_sec = 15; tout.tv_usec = 0; FD_ZERO(&ready); FD_SET(sock, &ready); if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { length = sizeof(from); cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, (struct sockaddr *)&from, &length); if (cc < 0) { warn("recvfrom"); continue; } /* * The 4.3BSD protocol spec had a 32-byte tsp_name field, and * this is still OS-dependent. Demand that the packet is at * least long enough to hold a 4.3BSD packet. */ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) { fprintf(stderr, "short packet (%zd/%zu bytes) from %s\n", cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32, inet_ntoa(from.sin_addr)); continue; } bytehostorder(&msg); if (msg.tsp_type == TSP_ACK) { printf("master timedaemon at %s is %s\n", tgtname, msg.tsp_name); } else { if (msg.tsp_type >= TSPTYPENUMBER) printf("unknown ack received: %u\n", msg.tsp_type); else printf("wrong ack received: %s\n", tsptype[msg.tsp_type]); } } else { printf("communication error with %s\n", tgtname); } } while (++i < argc); }
int slave() { int tries; long electiontime, refusetime, looktime, looptime, adjtime; u_short seq; long fastelection; #define FASTTOUT 3 struct in_addr cadr; struct timeval otime; struct sockaddr_in taddr; char tname[MAXHOSTNAMELEN]; struct tsp *msg, to; struct timeval ntime, wait, tmptv; time_t tsp_time_sec; struct tsp *answer; int timeout(); char olddate[32]; char newdate[32]; struct netinfo *ntp; struct hosttbl *htp; struct utmpx utx; old_slavenet = 0; seq = 0; refusetime = 0; adjtime = 0; (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; if (justquit) looktime = electiontime; else looktime = fastelection; looptime = fastelection; if (slavenet) xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr); if (status & MASTER) { for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == MASTER) masterup(ntp); } } loop: get_goodgroup(0); (void)gettimeofday(&ntime, NULL); if (ntime.tv_sec > electiontime) { if (trace) fprintf(fd, "election timer expired\n"); longjmp(jmpenv, 1); } if (ntime.tv_sec >= looktime) { if (trace) fprintf(fd, "Looking for nets to master\n"); if (Mflag && nignorednets > 0) { for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == IGNORE || ntp->status == NOMASTER) { lookformaster(ntp); if (ntp->status == MASTER) { masterup(ntp); } else if (ntp->status == MASTER) { ntp->status = NOMASTER; } } if (ntp->status == MASTER && --ntp->quit_count < 0) ntp->quit_count = 0; } makeslave(slavenet); /* prune extras */ setstatus(); } (void)gettimeofday(&ntime, NULL); looktime = ntime.tv_sec + delay2; } if (ntime.tv_sec >= looptime) { if (trace) fprintf(fd, "Looking for loops\n"); for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == MASTER) { to.tsp_type = TSP_LOOP; to.tsp_vers = TSPVERSION; to.tsp_seq = sequence++; to.tsp_hopcnt = MAX_HOPCNT; (void)strcpy(to.tsp_name, hostname); bytenetorder(&to); if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, (struct sockaddr*)&ntp->dest_addr, sizeof(ntp->dest_addr)) < 0) { trace_sendto_err(ntp->dest_addr.sin_addr); } } } (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + delay2; } wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec; if (wait.tv_sec < 0) wait.tv_sec = 0; wait.tv_sec += FASTTOUT; wait.tv_usec = 0; msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); if (msg != NULL) { /* * filter stuff not for us */ switch (msg->tsp_type) { case TSP_SETDATE: case TSP_TRACEOFF: case TSP_TRACEON: /* * XXX check to see they are from ourself */ break; case TSP_TEST: case TSP_MSITE: break; case TSP_MASTERUP: if (!fromnet) { if (trace) { fprintf(fd, "slave ignored: "); print(msg, &from); } goto loop; } break; default: if (!fromnet || fromnet->status == IGNORE || fromnet->status == NOMASTER) { if (trace) { fprintf(fd, "slave ignored: "); print(msg, &from); } goto loop; } break; } /* * now process the message */ switch (msg->tsp_type) { case TSP_ADJTIME: if (fromnet != slavenet) break; if (!good_host_name(msg->tsp_name)) { syslog(LOG_NOTICE, "attempted time adjustment by %s", msg->tsp_name); suppress(&from, msg->tsp_name, fromnet); break; } /* * Speed up loop detection in case we have a loop. * Otherwise the clocks can race until the loop * is found. */ (void)gettimeofday(&otime, NULL); if (adjtime < otime.tv_sec) looptime -= (looptime-otime.tv_sec)/2 + 1; setmaster(msg); if (seq != msg->tsp_seq) { seq = msg->tsp_seq; synch(tvtomsround(msg->tsp_time)); } (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; adjtime = ntime.tv_sec + SAMPLEINTVL*2; break; case TSP_SETTIME: if (fromnet != slavenet) break; if (seq == msg->tsp_seq) break; seq = msg->tsp_seq; /* adjust time for residence on the queue */ (void)gettimeofday(&otime, NULL); adj_msg_time(msg,&otime); /* * the following line is necessary due to syslog * calling ctime() which clobbers the static buffer */ (void)strcpy(olddate, date()); tsp_time_sec = msg->tsp_time.tv_sec; (void)strcpy(newdate, ctime(&tsp_time_sec)); if (!good_host_name(msg->tsp_name)) { syslog(LOG_NOTICE, "attempted time setting by untrusted %s to %s", msg->tsp_name, newdate); suppress(&from, msg->tsp_name, fromnet); break; } setmaster(msg); tmptv.tv_sec = msg->tsp_time.tv_sec; tmptv.tv_usec = msg->tsp_time.tv_usec; timevalsub(&ntime, &tmptv, &otime); if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) { /* * do not change the clock if we can adjust it */ synch(tvtomsround(ntime)); } else { utx.ut_type = OLD_TIME; gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); (void)settimeofday(&tmptv, 0); utx.ut_type = NEW_TIME; gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); syslog(LOG_NOTICE, "date changed by %s from %s", msg->tsp_name, olddate); if (status & MASTER) spreadtime(); } (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; /* This patches a bad protocol bug. Imagine a system with several networks, * where there are a pair of redundant gateways between a pair of networks, * each running timed. Assume that we start with a third machine mastering * one of the networks, and one of the gateways mastering the other. * Imagine that the third machine goes away and the non-master gateway * decides to replace it. If things are timed just 'right,' we will have * each gateway mastering one network for a little while. If a SETTIME * message gets into the network at that time, perhaps from the newly * masterful gateway as it was taking control, the SETTIME will loop * forever. Each time a gateway receives it on its slave side, it will * call spreadtime to forward it on its mastered network. We are now in * a permanent loop, since the SETTIME msgs will keep any clock * in the network from advancing. Normally, the 'LOOP' stuff will detect * and correct the situation. However, with the clocks stopped, the * 'looptime' timer cannot expire. While they are in this state, the * masters will try to saturate the network with SETTIME packets. */ looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1; break; case TSP_MASTERUP: if (slavenet && fromnet != slavenet) break; if (!good_host_name(msg->tsp_name)) { suppress(&from, msg->tsp_name, fromnet); if (electiontime > fastelection) electiontime = fastelection; break; } makeslave(fromnet); setmaster(msg); setstatus(); answerdelay(); xmit(TSP_SLAVEUP, 0, &from); (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; refusetime = 0; break; case TSP_MASTERREQ: if (fromnet->status != SLAVE) break; (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; break; case TSP_SETDATE: tsp_time_sec = msg->tsp_time.tv_sec; (void)strcpy(newdate, ctime(&tsp_time_sec)); schgdate(msg, newdate); break; case TSP_SETDATEREQ: if (fromnet->status != MASTER) break; tsp_time_sec = msg->tsp_time.tv_sec; (void)strcpy(newdate, ctime(&tsp_time_sec)); htp = findhost(msg->tsp_name); if (0 == htp) { syslog(LOG_WARNING, "DATEREQ from uncontrolled machine"); break; } if (!htp->good) { syslog(LOG_WARNING, "attempted date change by untrusted %s to %s", htp->name, newdate); spreadtime(); break; } schgdate(msg, newdate); break; case TSP_TRACEON: traceon(); break; case TSP_TRACEOFF: traceoff("Tracing ended at %s\n"); break; case TSP_SLAVEUP: newslave(msg); break; case TSP_ELECTION: if (fromnet->status == SLAVE) { (void)gettimeofday(&ntime, NULL); electiontime = ntime.tv_sec + delay2; fastelection = ntime.tv_sec + FASTTOUT; seq = 0; if (!good_host_name(msg->tsp_name)) { syslog(LOG_NOTICE, "suppress election of %s", msg->tsp_name); to.tsp_type = TSP_QUIT; electiontime = fastelection; } else if (cadr.s_addr != from.sin_addr.s_addr && ntime.tv_sec < refusetime) { /* if the candidate has to repeat itself, the old code would refuse it * the second time. That would prevent elections. */ to.tsp_type = TSP_REFUSE; } else { cadr.s_addr = from.sin_addr.s_addr; to.tsp_type = TSP_ACCEPT; refusetime = ntime.tv_sec + 30; } taddr = from; (void)strcpy(tname, msg->tsp_name); (void)strcpy(to.tsp_name, hostname); answerdelay(); if (!acksend(&to, &taddr, tname, TSP_ACK, 0, 0)) syslog(LOG_WARNING, "no answer from candidate %s\n", tname); } else { /* fromnet->status == MASTER */ htp = addmach(msg->tsp_name, &from,fromnet); to.tsp_type = TSP_QUIT; (void)strcpy(to.tsp_name, hostname); if (!acksend(&to, &htp->addr, htp->name, TSP_ACK, 0, htp->noanswer)) { syslog(LOG_ERR, "no reply from %s to ELECTION-QUIT", htp->name); (void)remmach(htp); } } 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); /* The other master often gets into the same state, * with boring results. */ ntp = fromnet; /* (acksend() can leave fromnet=0 */ for (tries = 0; tries < 3; tries++) { to.tsp_type = TSP_RESOLVE; answer = acksend(&to, &ntp->dest_addr, ANYADDR, TSP_MASTERACK, ntp, 0); if (answer == NULL) break; htp = addmach(answer->tsp_name,&from,ntp); to.tsp_type = TSP_QUIT; answer = acksend(&to, &htp->addr, htp->name, TSP_ACK, 0, htp->noanswer); if (!answer) { syslog(LOG_WARNING, "conflict error: no reply from %s to QUIT", htp->name); (void)remmach(htp); } } masterup(ntp); break; case TSP_MSITE: if (!slavenet) break; taddr = from; to.tsp_type = TSP_MSITEREQ; to.tsp_vers = TSPVERSION; to.tsp_seq = 0; (void)strcpy(to.tsp_name, hostname); answer = acksend(&to, &slavenet->dest_addr, ANYADDR, TSP_ACK, slavenet, 0); if (answer != NULL && good_host_name(answer->tsp_name)) { setmaster(answer); to.tsp_type = TSP_ACK; (void)strcpy(to.tsp_name, answer->tsp_name); bytenetorder(&to); if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, (struct sockaddr*)&taddr, sizeof(taddr)) < 0) { trace_sendto_err(taddr.sin_addr); } } break; case TSP_MSITEREQ: break; case TSP_ACCEPT: case TSP_REFUSE: case TSP_RESOLVE: break; case TSP_QUIT: doquit(msg); /* become a slave */ break; case TSP_TEST: electiontime = 0; break; case TSP_LOOP: /* looking for loops of masters */ if (!(status & MASTER)) break; if (fromnet->status == SLAVE) { if (!strcmp(msg->tsp_name, hostname)) { /* * Someone forwarded our message back to * us. There must be a loop. Tell the * master of this network to quit. * * The other master often gets into * the same state, with boring results. */ ntp = fromnet; for (tries = 0; tries < 3; tries++) { to.tsp_type = TSP_RESOLVE; answer = acksend(&to, &ntp->dest_addr, ANYADDR, TSP_MASTERACK, ntp,0); if (answer == NULL) break; taddr = from; (void)strcpy(tname, answer->tsp_name); to.tsp_type = TSP_QUIT; (void)strcpy(to.tsp_name, hostname); if (!acksend(&to, &taddr, tname, TSP_ACK, 0, 1)) { syslog(LOG_ERR, "no reply from %s to slave LOOP-QUIT", tname); } else { electiontime = 0; } } (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + FASTTOUT; } else { if (msg->tsp_hopcnt-- < 1) break; bytenetorder(msg); for (ntp = nettab; ntp != 0; ntp = ntp->next) { if (ntp->status == MASTER && 0 > sendto(sock, (char *)msg, sizeof(struct tsp), 0, (struct sockaddr*)&ntp->dest_addr, sizeof(ntp->dest_addr))) trace_sendto_err(ntp->dest_addr.sin_addr); } } } else { /* fromnet->status == MASTER */ /* * We should not have received this from a net * we are master on. There must be two masters, * unless the packet was really from us. */ if (from.sin_addr.s_addr == fromnet->my_addr.s_addr) { if (trace) fprintf(fd,"discarding forwarded LOOP\n"); break; } /* * The other master often gets into the same * state, with boring results. */ ntp = fromnet; for (tries = 0; tries < 3; tries++) { to.tsp_type = TSP_RESOLVE; answer = acksend(&to, &ntp->dest_addr, ANYADDR, TSP_MASTERACK, ntp,0); if (!answer) break; htp = addmach(answer->tsp_name, &from,ntp); to.tsp_type = TSP_QUIT; (void)strcpy(to.tsp_name, hostname); if (!acksend(&to,&htp->addr,htp->name, TSP_ACK, 0, htp->noanswer)) { syslog(LOG_ERR, "no reply from %s to master LOOP-QUIT", htp->name); (void)remmach(htp); } } (void)gettimeofday(&ntime, NULL); looptime = ntime.tv_sec + FASTTOUT; } break; default: if (trace) { fprintf(fd, "garbage message: "); print(msg, &from); } break; } } goto loop; }
/* * 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. */ int master() { struct hosttbl *htp; long pollingtime; #define POLLRATE 4 int polls; struct timeval wait, ntime; time_t tsp_time_sec; struct tsp *msg, *answer, to; char newdate[32]; struct sockaddr_in taddr; char tname[MAXHOSTNAMELEN]; struct netinfo *ntp; int i; syslog(LOG_NOTICE, "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); } (void)gettimeofday(&ntime, 0); pollingtime = ntime.tv_sec+3; if (justquit) polls = 0; else polls = POLLRATE-1; /* Process all outstanding messages before spending the long time necessary * to update all timers. */ loop: (void)gettimeofday(&ntime, 0); wait.tv_sec = pollingtime - ntime.tv_sec; if (wait.tv_sec < 0) wait.tv_sec = 0; wait.tv_usec = 0; msg = readmsg(TSP_ANY, ANYADDR, &wait, 0); if (!msg) { (void)gettimeofday(&ntime, 0); if (ntime.tv_sec >= pollingtime) { pollingtime = ntime.tv_sec + SAMPLEINTVL; get_goodgroup(0); /* If a bogus master told us to quit, we can have decided to ignore a * network. Therefore, periodically try to take over everything. */ polls = (polls + 1) % POLLRATE; if (0 == polls && nignorednets > 0) { trace_msg("Looking for nets to re-master\n"); for (ntp = nettab; ntp; ntp = ntp->next) { if (ntp->status == IGNORE || ntp->status == NOMASTER) { lookformaster(ntp); if (ntp->status == MASTER) { masterup(ntp); polls = POLLRATE-1; } } if (ntp->status == MASTER && --ntp->quit_count < 0) ntp->quit_count = 0; } if (polls != 0) setstatus(); } 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 = MAX_HOPCNT; (void)strcpy(to.tsp_name, hostname); bytenetorder(&to); if (sendto(sock, (char *)&to, sizeof(struct tsp), 0, (struct sockaddr*)&ntp->dest_addr, sizeof(ntp->dest_addr)) < 0) { trace_sendto_err(ntp->dest_addr.sin_addr); } } } } else { switch (msg->tsp_type) { case TSP_MASTERREQ: break; case TSP_SLAVEUP: newslave(msg); break; case TSP_SETDATE: /* * XXX check to see it is from ourself */ tsp_time_sec = msg->tsp_time.tv_sec; (void)strcpy(newdate, ctime(&tsp_time_sec)); if (!good_host_name(msg->tsp_name)) { syslog(LOG_NOTICE, "attempted date change by %s to %s", msg->tsp_name, newdate); spreadtime(); break; } mchgdate(msg); (void)gettimeofday(&ntime, 0); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; case TSP_SETDATEREQ: if (!fromnet || fromnet->status != MASTER) break; tsp_time_sec = msg->tsp_time.tv_sec; (void)strcpy(newdate, ctime(&tsp_time_sec)); htp = findhost(msg->tsp_name); if (htp == 0) { syslog(LOG_ERR, "attempted SET DATEREQ by uncontrolled %s to %s", msg->tsp_name, newdate); break; } if (htp->seq == msg->tsp_seq) break; htp->seq = msg->tsp_seq; if (!htp->good) { syslog(LOG_NOTICE, "attempted SET DATEREQ by untrusted %s to %s", msg->tsp_name, newdate); spreadtime(); break; } mchgdate(msg); (void)gettimeofday(&ntime, 0); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; case TSP_MSITE: xmit(TSP_ACK, msg->tsp_seq, &from); break; case TSP_MSITEREQ: break; case TSP_TRACEON: traceon(); break; case TSP_TRACEOFF: traceoff("Tracing ended at %s\n"); break; case TSP_ELECTION: if (!fromnet) break; if (fromnet->status == MASTER) { pollingtime = 0; (void)addmach(msg->tsp_name, &from,fromnet); } taddr = from; (void)strcpy(tname, msg->tsp_name); to.tsp_type = TSP_QUIT; (void)strcpy(to.tsp_name, hostname); answer = acksend(&to, &taddr, tname, TSP_ACK, 0, 1); if (answer == NULL) { syslog(LOG_ERR, "election error by %s", tname); } 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. */ if (!fromnet || fromnet->status != MASTER) break; (void)strcpy(to.tsp_name, hostname); /* The other master often gets into the same state, * with boring results if we stay at it forever. */ ntp = fromnet; /* (acksend() can leave fromnet=0 */ for (i = 0; i < 3; i++) { to.tsp_type = TSP_RESOLVE; (void)strcpy(to.tsp_name, hostname); answer = acksend(&to, &ntp->dest_addr, ANYADDR, TSP_MASTERACK, ntp, 0); if (!answer) break; htp = addmach(answer->tsp_name,&from,ntp); to.tsp_type = TSP_QUIT; msg = acksend(&to, &htp->addr, htp->name, TSP_ACK, 0, htp->noanswer); if (msg == NULL) { syslog(LOG_ERR, "no response from %s to CONFLICT-QUIT", htp->name); } } masterup(ntp); pollingtime = 0; break; case TSP_RESOLVE: if (!fromnet || fromnet->status != MASTER) break; /* * do not want to call synch() while waiting * to be killed! */ (void)gettimeofday(&ntime, (struct timezone *)0); pollingtime = ntime.tv_sec + SAMPLEINTVL; break; case TSP_QUIT: doquit(msg); /* become a slave */ break; case TSP_LOOP: if (!fromnet || fromnet->status != MASTER || !strcmp(msg->tsp_name, hostname)) break; /* * We should not have received this from a net * we are master on. There must be two masters. */ htp = addmach(msg->tsp_name, &from,fromnet); to.tsp_type = TSP_QUIT; (void)strcpy(to.tsp_name, hostname); answer = acksend(&to, &htp->addr, htp->name, TSP_ACK, 0, 1); if (!answer) { syslog(LOG_WARNING, "loop breakage: no reply from %s=%s to QUIT", htp->name, inet_ntoa(htp->addr.sin_addr)); (void)remmach(htp); } case TSP_TEST: if (trace) { fprintf(fd, "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n", nnets, nmasternets, nslavenets, nignorednets); setstatus(); } pollingtime = 0; polls = POLLRATE-1; break; default: if (trace) { fprintf(fd, "garbage message: "); print(msg, &from); } break; } } goto loop; }
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, otime; 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; extern int justquit; #ifdef MEASURE 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 if (fp == NULL) { fp = fopen("/usr/adm/timed.masterlog", "w"); setlinebuf(fp); } #endif syslog(LOG_INFO, "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_INFO, "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) if (justquit) looktime = time.tv_sec + delay2; else looktime = 1; 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 and loops\n"); if (nignorednets > 0) { for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == IGNORE) { lookformaster(ntp); if (ntp->status == MASTER) masterup(ntp); else ntp->status = IGNORE; } } setstatus(); #ifdef MEASURE /* * Check to see if we just became master * (file not open) */ if (fp == NULL) { fp = fopen("/usr/adm/timed.masterlog", "w"); setlinebuf(fp); } #endif } for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == MASTER) { 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); } } } (void)gettimeofday(&time, (struct timezone *)0); looktime = time.tv_sec + delay2; } 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_SETDATE: #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)gettimeofday(&otime, (struct timezone *)0); (void)settimeofday(&msg->tsp_time, (struct timezone *)0); syslog(LOG_NOTICE, "date changed by %s from: %s", msg->tsp_name, olddate); logwtmp(otime, msg->tsp_time); if ((status & SUBMASTER) == SUBMASTER) spreadtime(); (void)gettimeofday(&time, (struct timezone *)0); 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_SETDATE: saveaddr = from; msg->tsp_type = TSP_SETDATEREQ; 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_SETDATEREQ: saveaddr = from; if (status != SUBMASTER || fromnet->status != MASTER) break; for (ntp = nettab; ntp != NULL; ntp = ntp->next) { if (ntp->status == SLAVE) break; } ind = findhost(msg->tsp_name); if (ind < 0) { syslog(LOG_WARNING, "DATEREQ from uncontrolled machine"); break; } syslog(LOG_DEBUG, "forwarding date change request for %s", msg->tsp_name); (void)strcpy(msg->tsp_name, hostname); 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_WARNING, "no answer from master candidate\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_WARNING, "election error: no reply to QUIT"); } 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_ACK, (struct netinfo *)NULL); if (msg == NULL) { syslog(LOG_WARNING, "conflict error: no reply to 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 if (fp != NULL) { (void)fclose(fp); fp = NULL; } #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; case TSP_LOOP: /* looking for loops of masters */ if ( !(status & MASTER)) break; if (fromnet->status == SLAVE) { if ( !strcmp(msg->tsp_name, hostname)) { 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; (void)strcpy(to.tsp_name, hostname); server = from; answer = acksend(&to, &server, answer->tsp_name, TSP_ACK, (struct netinfo *)NULL); if (answer == NULL) { syslog(LOG_ERR, "loop kill error"); } else { electiontime = 0; } } } else { if (msg->tsp_hopcnt-- <= 0) break; bytenetorder(msg); ntp = nettab; for (; ntp != NULL; ntp = ntp->next) if (ntp->status == MASTER) if (sendto(sock, (char *)msg, sizeof(struct tsp), 0, &ntp->dest_addr, length) < 0) { syslog(LOG_ERR, "sendto: %m"); exit(1); } } } else { /* * We should not have received this from a net * we are master on. There must be two masters * in this case. */ if (fromnet->my_addr.s_addr == from.sin_addr.s_addr) 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; (void)strcpy(to.tsp_name, hostname); server = from; answer = acksend(&to, &server, answer->tsp_name, TSP_ACK, (struct netinfo *)NULL); if (answer == NULL) { syslog(LOG_ERR, "loop kill error2"); } else { (void)addmach(msg->tsp_name, &from); } } } break; default: if (trace) { fprintf(fd, "garbage: "); print(msg, &from); } break; } } goto loop; }
master() { int ind; long pollingtime; struct timeval wait; struct timeval time; 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(_PATH_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, (struct sockaddr *)&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); 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); bytenetorder(msg); if (sendto(sock, (char *)msg, sizeof(struct tsp), 0, (struct sockaddr *)&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); 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 by %s from: %s", msg->tsp_name, olddate); 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; }
/* * `election' candidates a host as master: it is called by a slave * which runs with the -M option set when its election timeout expires. * Note the conservative approach: if a new timed comes up, or another * candidate sends an election request, the candidature is withdrawn. */ int election(struct netinfo *net) { struct tsp *resp, msg; struct timeval then, wait; struct tsp *answer; struct hosttbl *htp; char loop_lim = 0; /* This code can get totally confused if it gets slightly behind. For * example, if readmsg() has some QUIT messages waiting from the last * round, we would send an ELECTION message, get the stale QUIT, * and give up. This results in network storms when several machines * do it at once. */ wait.tv_sec = 0; wait.tv_usec = 0; while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) { if (trace) fprintf(fd, "election: discarded stale REFUSE\n"); } while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) { if (trace) fprintf(fd, "election: discarded stale QUIT\n"); } again: syslog(LOG_INFO, "This machine is a candidate time master"); if (trace) fprintf(fd, "This machine is a candidate time master\n"); msg.tsp_type = TSP_ELECTION; msg.tsp_vers = TSPVERSION; (void)strcpy(msg.tsp_name, hostname); bytenetorder(&msg); if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0, (struct sockaddr*)&net->dest_addr, sizeof(struct sockaddr)) < 0) { trace_sendto_err(net->dest_addr.sin_addr); return(SLAVE); } (void)gettimeofday(&then, 0); then.tv_sec += 3; for (;;) { (void)gettimeofday(&wait, 0); timevalsub(&wait,&then,&wait); resp = readmsg(TSP_ANY, ANYADDR, &wait, net); if (!resp) return(MASTER); switch (resp->tsp_type) { case TSP_ACCEPT: (void)addmach(resp->tsp_name, &from,fromnet); break; case TSP_MASTERUP: case TSP_MASTERREQ: /* * If another timedaemon is coming up at the same * time, give up, and let it be the master. */ if (++loop_lim < 5 && !good_host_name(resp->tsp_name)) { (void)addmach(resp->tsp_name, &from,fromnet); suppress(&from, resp->tsp_name, net); goto again; } rmnetmachs(net); return(SLAVE); case TSP_QUIT: case TSP_REFUSE: /* * Collision: change value of election timer * using exponential backoff. * * Fooey. * An exponential backoff on a delay starting at * 6 to 15 minutes for a process that takes * milliseconds is silly. It is particularly * strange that the original code would increase * the backoff without bound. */ rmnetmachs(net); return(SLAVE); case TSP_ELECTION: /* no master for another round */ htp = addmach(resp->tsp_name,&from,fromnet); msg.tsp_type = TSP_REFUSE; (void)strcpy(msg.tsp_name, hostname); answer = acksend(&msg, &htp->addr, htp->name, TSP_ACK, 0, htp->noanswer); if (!answer) { syslog(LOG_ERR, "error in election from %s", htp->name); } break; case TSP_SLAVEUP: (void)addmach(resp->tsp_name, &from,fromnet); break; case TSP_SETDATE: case TSP_SETDATEREQ: break; default: if (trace) { fprintf(fd, "candidate: "); print(resp, &from); } break; } } }