/* $Header: /cvs/src/chrony/util.c,v 1.22 2003/09/28 22:21:17 richard Exp $ ======================================================================= chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * ********************************************************************** ======================================================================= Various utility functions */ #include "sysincl.h" #include "util.h" #include "logging.h" /* ================================================== */ INLINE_STATIC void UTI_TimevalToDouble(struct timeval *a, double *b) { *b = (double)(a->tv_sec) + 1.0e-6 * (double)(a->tv_usec); } /* ================================================== */ INLINE_STATIC void UTI_DoubleToTimeval(double a, struct timeval *b) { long int_part, frac_part; int_part = (long)(a); frac_part = (long)(0.5 + 1.0e6 * (a - (double)(int_part))); b->tv_sec = int_part; b->tv_usec = frac_part; UTI_NormaliseTimeval(b); } /* ================================================== */ INLINE_STATIC int UTI_CompareTimevals(struct timeval *a, struct timeval *b) { if (a->tv_sec < b->tv_sec) { return -1; } else if (a->tv_sec > b->tv_sec) { return +1; } else { if (a->tv_sec != b->tv_sec) { CROAK("a->tv_sec != b->tv_sec"); } if (a->tv_usec < b->tv_usec) { return -1; } else if (a->tv_usec > b->tv_usec) { return +1; } else { if (a->tv_usec != b->tv_usec) { CROAK("a->tv_usec != b->tv_usec"); } return 0; } } CROAK("Impossible"); /* Shouldn't be able to fall through. */ } /* ================================================== */ INLINE_STATIC void UTI_NormaliseTimeval(struct timeval *x) { while (x->tv_usec >= 1000000) { ++x->tv_sec; x->tv_usec -= 1000000; } while (x->tv_usec < 0) { --x->tv_sec; x->tv_usec += 1000000; } } /* ================================================== */ INLINE_STATIC void UTI_DiffTimevals(struct timeval *result, struct timeval *a, struct timeval *b) { result->tv_sec = a->tv_sec - b->tv_sec; result->tv_usec = a->tv_usec - b->tv_usec; /* Correct microseconds field to bring it into the range [0,1000000) */ while (result->tv_usec < 0) { result->tv_usec += 1000000; --result->tv_sec; } while (result->tv_usec > 999999) { result->tv_usec -= 1000000; ++result->tv_sec; } return; } /* ================================================== */ /* Calculate result = a - b and return as a double */ INLINE_STATIC void UTI_DiffTimevalsToDouble(double *result, struct timeval *a, struct timeval *b) { *result = (double)(a->tv_sec - b->tv_sec) + (double)(a->tv_usec - b->tv_usec) * 1.0e-6; } /* ================================================== */ INLINE_STATIC void UTI_AddDoubleToTimeval(struct timeval *start, double increment, struct timeval *end) { long int_part, frac_part; /* Don't want to do this by using (long)(1000000 * increment), since that will only cope with increments up to +/- 2148 seconds, which is too marginal here. */ int_part = (long) increment; frac_part = (long) (0.5 + 1.0e6 * (increment - (double)int_part)); end->tv_sec = int_part + start->tv_sec; end->tv_usec = frac_part + start->tv_usec; UTI_NormaliseTimeval(end); } /* ================================================== */ /* Calculate the average and difference (as a double) of two timevals */ INLINE_STATIC void UTI_AverageDiffTimevals (struct timeval *earlier, struct timeval *later, struct timeval *average, double *diff) { struct timeval tvdiff; struct timeval tvhalf; UTI_DiffTimevals(&tvdiff, later, earlier); *diff = (double)tvdiff.tv_sec + 1.0e-6 * (double)tvdiff.tv_usec; if (*diff < 0.0) { /* Either there's a bug elsewhere causing 'earlier' and 'later' to be backwards, or something wierd has happened. Maybe when we change the frequency on Linux? */ /* This seems to be fairly benign, so don't bother logging it */ #if 0 LOG(LOGS_INFO, LOGF_Util, "Earlier=[%s] Later=[%s]", UTI_TimevalToString(earlier), UTI_TimevalToString(later)); #endif /* Assume the required behaviour is to treat it as zero */ *diff = 0.0; } tvhalf.tv_sec = tvdiff.tv_sec / 2; tvhalf.tv_usec = tvdiff.tv_usec / 2 + (tvdiff.tv_sec % 2); average->tv_sec = earlier->tv_sec + tvhalf.tv_sec; average->tv_usec = earlier->tv_usec + tvhalf.tv_usec; /* Bring into range */ UTI_NormaliseTimeval(average); while (average->tv_usec >= 1000000) { ++average->tv_sec; average->tv_usec -= 1000000; } while (average->tv_usec < 0) { --average->tv_sec; average->tv_usec += 1000000; } } /* ================================================== */ #define POOL_ENTRIES 16 #define BUFFER_LENGTH 64 static char buffer_pool[POOL_ENTRIES][BUFFER_LENGTH]; static int pool_ptr = 0; #define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)]) /* ================================================== */ /* Convert a timeval into a temporary string, largely for diagnostic display */ char * UTI_TimevalToString(struct timeval *tv) { char buffer[64], *result; struct tm stm; stm = *gmtime((time_t *) &(tv->tv_sec)); strftime(buffer, sizeof(buffer), "%a %x %X", &stm); result = NEXT_BUFFER; snprintf(result, BUFFER_LENGTH, "%s.%06ld", buffer, (unsigned long)(tv->tv_usec)); return result; } /* ================================================== */ #define JAN_1970 0x83aa7e80UL inline static void int64_to_timeval(NTP_int64 *src, struct timeval *dest) { dest->tv_sec = ntohl(src->hi) - JAN_1970; /* Until I invent a slick way to do this, just do it the obvious way */ dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296); } /* ================================================== */ /* Convert an NTP timestamp into a temporary string, largely for diagnostic display */ char * UTI_TimestampToString(NTP_int64 *ts) { struct timeval tv; int64_to_timeval(ts, &tv); return UTI_TimevalToString(&tv); } /* ================================================== */ char * UTI_IPToDottedQuad(unsigned long ip) { unsigned long a, b, c, d; char *result; a = (ip>>24) & 0xff; b = (ip>>16) & 0xff; c = (ip>> 8) & 0xff; d = (ip>> 0) & 0xff; result = NEXT_BUFFER; snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d); return result; } /* ================================================== */ char * UTI_TimeToLogForm(time_t t) { struct tm stm; char *result; result = NEXT_BUFFER; stm = *gmtime(&t); strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", &stm); return result; } /* ================================================== */ void UTI_AdjustTimeval(struct timeval *old_tv, struct timeval *when, struct timeval *new_tv, double dfreq, double doffset) { double elapsed, delta_time; UTI_DiffTimevalsToDouble(&elapsed, when, old_tv); delta_time = elapsed * dfreq - doffset; UTI_AddDoubleToTimeval(old_tv, delta_time, new_tv); } /* ================================================== */ /* Seconds part of RFC1305 timestamp correponding to the origin of the struct timeval format. */ #define JAN_1970 0x83aa7e80UL void UTI_TimevalToInt64(struct timeval *src, NTP_int64 *dest) { unsigned long usec = src->tv_usec; unsigned long sec = src->tv_sec; /* Recognize zero as a special case - it always signifies an 'unknown' value */ if (!usec && !sec) { dest->hi = dest->lo = 0; } else { dest->hi = htonl(src->tv_sec + JAN_1970); /* This formula gives an error of about 0.1us worst case */ dest->lo = htonl(4295 * usec - (usec>>5) - (usec>>9)); } } /* ================================================== */ void UTI_Int64ToTimeval(NTP_int64 *src, struct timeval *dest) { /* As yet, there is no need to check for zero - all processing that has to detect that case is in the NTP layer */ dest->tv_sec = ntohl(src->hi) - JAN_1970; /* Until I invent a slick way to do this, just do it the obvious way */ dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296); } /* ================================================== */ /* Force a core dump and exit without doing abort() or assert(0). These do funny things with the call stack in the core file that is generated, which makes diagnosis difficult. */ int croak(const char *file, int line, const char *msg) { int a; LOG(LOGS_ERR, LOGF_Util, "Unexpected condition [%s] at %s:%d, core dumped", msg, file, line); a = * (int *) 0; return a; /* Can't happen - this stops the optimiser optimising the line above */ } /* ================================================== */