/* $NetBSD: correct.c,v 1.14 2007/05/17 00:36:12 christos Exp $ */ /*- * Copyright (c) 1985, 1993 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 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. 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 * SUCH DAMAGE. */ #include #ifndef lint #if 0 static char sccsid[] = "@(#)correct.c 8.1 (Berkeley) 6/6/93"; #else __RCSID("$NetBSD: correct.c,v 1.14 2007/05/17 00:36:12 christos Exp $"); #endif #endif /* not lint */ #include "globals.h" #include #include #include static void adjclock(struct timeval*); /* * sends to the slaves the corrections for their clocks after fixing our * own */ void correct(long avdelta) { struct hosttbl *htp; long corr; struct timeval adjlocal, tmptv; struct tsp to; struct tsp *answer; mstotvround(&adjlocal, avdelta); for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) { if (htp->delta != HOSTDOWN) { corr = avdelta - htp->delta; /* If the other machine is off in the weeds, set its time directly. * If a slave gets the wrong day, the original code would simply * fix the minutes. If you fix a network partition, you can get * into such situations. */ if (htp->need_set || corr >= MAXADJ*1000 || corr <= -MAXADJ*1000) { htp->need_set = 0; (void)gettimeofday(&tmptv,0); timeradd(&tmptv, &adjlocal, &tmptv); to.tsp_time.tv_sec = tmptv.tv_sec; to.tsp_time.tv_usec = tmptv.tv_usec; to.tsp_type = TSP_SETTIME; } else { tmptv.tv_sec = to.tsp_time.tv_sec ; tmptv.tv_usec = to.tsp_time.tv_usec ; mstotvround(&tmptv, corr); to.tsp_time.tv_sec = tmptv.tv_sec; to.tsp_time.tv_usec = tmptv.tv_usec; to.tsp_type = TSP_ADJTIME; } set_tsp_name(&to, hostname); answer = acksend(&to, &htp->addr, htp->name, TSP_ACK, 0, 0); if (!answer) { htp->delta = HOSTDOWN; syslog(LOG_WARNING, "no reply to time correction from %s", htp->name); if (++htp->noanswer >= LOSTHOST) { if (trace) { fprintf(fd, "purging %s for not answering\n", htp->name); (void)fflush(fd); } htp = remmach(htp); } } } } /* * adjust our own clock now that we are not sending it out */ adjclock(&adjlocal); } static void adjclock(struct timeval *corr) { static int passes = 0; static int smoother = 0; long delta; /* adjustment in usec */ long ndelta; struct timeval now; struct timeval adj; if (!timerisset(corr)) return; adj = *corr; if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) { delta = adj.tv_sec*1000000 + adj.tv_usec; /* If the correction is less than the minimum round * trip time for an ICMP packet, and thus * less than the likely error in the measurement, * do not do the entire correction. Do half * or a quarter of it. */ if (delta > -MIN_ROUND*1000 && delta < MIN_ROUND*1000) { if (smoother <= 4) smoother++; ndelta = (unsigned long)delta >> smoother; if (delta < 0) { long mask = (long)~0 & ~((1 << ((sizeof(long) * NBBY) - smoother)) - 1); ndelta |= mask; } if (trace) fprintf(fd, "trimming delta %ld usec to %ld\n", delta, ndelta); adj.tv_usec = ndelta; adj.tv_sec = 0; } else if (smoother > 0) { smoother--; } if (0 > adjtime(corr, 0)) { syslog(LOG_ERR, "adjtime: %m"); } if (passes > 1 && (delta < -BIG_ADJ || delta > BIG_ADJ)) { smoother = 0; passes = 0; syslog(LOG_WARNING, "large time adjustment of %+.3f sec", delta/1000000.0); } } else { syslog(LOG_WARNING, "clock correction %ld sec too large to adjust", (long)adj.tv_sec); (void) gettimeofday(&now, 0); timeradd(&now, corr, &now); if (settimeofday(&now, 0) < 0) syslog(LOG_ERR, "settimeofday: %m"); } } /* adjust the time in a message by the time it * spent in the queue */ void adj_msg_time(struct tsp *msg, struct timeval *now) { struct timeval diff; timersub(now, &from_when, &diff); timeradd(&msg->tsp_time, &diff, &msg->tsp_time); }