+* [Bug 1995] add compile time stamp based era unfolding for
+ 'step_systime()' and needed support to 'ntp-calendar.c'.
(4.2.7p212) 2011/09/07 Released by Harlan Stenn <stenn@ntp.org>
* [Bug 2003] from 4.2.6p4-RC3: ntpq_read_assoc_peervars() broken.
(4.2.7p211) 2011/09/01 Released by Harlan Stenn <stenn@ntp.org>
extern void caljulian (u_int32, struct calendar *);
extern u_int32 caltontp (const struct calendar *);
+/*
+ * Convert between 'time_t' and 'vint64'
+ */
+extern vint64 time_to_vint64(const time_t *);
+extern time_t vint64_to_time(const vint64 *);
+
+/*
+ * Get the build date & time. ATTENTION: The time zone is not specified!
+ * This depends entirely on the C compilers' capabilities to properly
+ * expand the '__TIME__' and '__DATE__' macros, as required by the C
+ * standard.
+ */
+extern int
+ntpcal_get_build_date(struct calendar * jd);
+
/*
* Convert a timestamp in NTP scale to a time_t value in the UN*X
* scale with proper epoch unfolding around a given pivot or the
extern u_int32
ntpcal_date_to_ntp(const struct calendar *jd);
+extern time_t
+ntpcal_date_to_time(const struct calendar *jd);
+
/*
* ISO week-calendar conversions
*/
return (*systime_func)(NULL);
}
+/*
+ *---------------------------------------------------------------------
+ * Convert between 'time_t' and 'vint64'
+ *---------------------------------------------------------------------
+ */
+vint64
+time_to_vint64(
+ const time_t * tv
+ )
+{
+ vint64 res;
+ time_t tmp;
+
+ /*
+ * shifting negative signed quantities is compiler-dependent, so
+ * we better avoid it and do it all manually. And shifting more
+ * than the width of a quantity is undefined. Also a don't do!
+ */
+
+#if SIZEOF_TIME_T <= 4
+
+ res.D_s.hi = 0;
+ if ((tmp = *tv) < 0) {
+ res.D_s.lo = (u_int32) -tmp;
+ M_NEG(res.D_s.hi, res.D_s.lo);
+ } else {
+ res.D_s.lo = (u_int32) tmp;
+ }
+
+#elif defined (HAVE_INT64)
+
+ (void)tmp; /* touch it to avoid warnings... */
+ res.q_s = *tv;
+
+#else
+ time_t tmp = *tv;
+ if ((tmp = *tv) < 0) {
+ tmp = -tmp;
+ res.D_s.lo = (u_int32)tmp;
+ res.D_s.hi = (u_int32)(tmp >> 32);
+ M_NEG(res.D_s.hi, res.D_s.lo);
+ } else {
+ res.D_s.lo = (u_int32)tmp;
+ res.D_s.hi = (u_int32)(tmp >> 32);
+ }
+
+#endif
+
+ return res;
+}
+
+
+time_t
+vint64_to_time(
+ const vint64 *tv
+ )
+{
+ time_t res;
+
+#if SIZEOF_TIME_T <= 4
+
+ res = (time_t)tv->D_s.lo;
+
+#elif defined (HAVE_INT64)
+
+ res = (time_t)tv->q_s;
+
+#else
+
+ res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo;
+
+#endif
+
+ return res;
+}
+
+/*
+ *---------------------------------------------------------------------
+ * Get the build date & time
+ *---------------------------------------------------------------------
+ */
+int
+ntpcal_get_build_date(
+ struct calendar * jd
+ )
+{
+ /* The C standard tells us the format of '__DATE__':
+ *
+ * __DATE__ The date of translation of the preprocessing
+ * translation unit: a character string literal of the form "Mmm
+ * dd yyyy", where the names of the months are the same as those
+ * generated by the asctime function, and the first character of
+ * dd is a space character if the value is less than 10. If the
+ * date of translation is not available, an
+ * implementation-defined valid date shall be supplied.
+ *
+ * __TIME__ The time of translation of the preprocessing
+ * translation unit: a character string literal of the form
+ * "hh:mm:ss" as in the time generated by the asctime
+ * function. If the time of translation is not available, an
+ * implementation-defined valid time shall be supplied.
+ *
+ * Note that MSVC declares DATE and TIME to be in the local time
+ * zone, while neither the C standard nor the GCC docs make any
+ * statement about this. As a result, we may be +/-12hrs off
+ * UTC. But for practical purposes, this should not be a
+ * problem.
+ *
+ */
+ static const char build[] = __TIME__ "/" __DATE__;
+ static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+ char month[4];
+ const char *cp;
+ u_short hour, minute, second, year, day;
+
+ memset(jd, 0, sizeof(struct calendar));
+ jd->year = 1970;
+ jd->month = 1;
+ jd->monthday = 1;
+
+ if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu",
+ &hour, &minute, &second, month, &day, &year)) {
+ cp = strstr(mlist, month);
+ if (NULL != cp) {
+ jd->year = year;
+ jd->month = (cp - mlist) / 3 + 1;
+ jd->monthday = day;
+ jd->hour = hour;
+ jd->minute = minute;
+ jd->second = second;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
/*
*---------------------------------------------------------------------
* basic calendar stuff
res.Q_s -= 0x80000000u; /* unshift of half range */
ntp -= (u_int32)JAN_1970; /* warp into UN*X domain */
ntp -= res.D_s.lo; /* cycle difference */
- res.Q_s += (u_int64)ntp; /* get expanded time */
+ res.Q_s += (u_int64)ntp; /* get expanded time */
#else /* no 64bit scalars */
time_t tmp = pivot ? *pivot : now();
- /*
- * shifting negative signed quantities is compiler-dependent, so
- * we better avoid it and do it all manually. And shifting more
- * than the rgisterwidth is undefined. Also a don't do!
- */
-# if SIZEOF_TIME_T > 4
- if (tmp < 0) {
- tmp = -tmp;
- res.D_s.lo = (u_int32)tmp;
- res.D_s.hi = (u_int32)(tmp >> 32);
- M_NEG(res.D_s.hi, res.D_s.lo);
- } else {
- res.D_s.lo = (u_int32)tmp;
- res.D_s.hi = (u_int32)(tmp >> 32);
- }
-# else
- res.D_s.lo = (u_int32)(int32)tmp;
- res.D_s.hi = (tmp < 0) ? ~(u_int32)0 : 0;
-# endif /* sizeof time_t <= 4 */
+ res = time_to_vint64(&tmp);
M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
ntp -= (u_int32)JAN_1970; /* warp into UN*X domain */
ntp -= res.D_s.lo; /* cycle difference */
M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
+
#endif /* no 64bit scalars */
return res;
time_t tmp = pivot ? *pivot : now();
- /*
- * shifting negative signed quantities is compiler-dependent, so
- * we better avoid it and do it all manually. And shifting more
- * than the rgisterwidth is undefined. Also a don't do!
- */
-# if SIZEOF_TIME_T > 4
- if (tmp < 0) {
- tmp = -tmp;
- res.D_s.lo = (u_int32)tmp;
- res.D_s.hi = (u_int32)(tmp >> 32);
- M_NEG(res.D_s.hi, res.D_s.lo);
- } else {
- res.D_s.lo = (u_int32)tmp;
- res.D_s.hi = (u_int32)(tmp >> 32);
- }
-# else
- res.D_s.lo = (u_int32)(int32)tmp;
- res.D_s.hi = (tmp < 0) ? ~(u_int32)0 : 0;
-# endif
+ res = time_to_vint64(&tmp);
M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
M_ADD(res.D_s.hi, res.D_s.lo, 0, (u_int32)JAN_1970);/*into NTP */
ntp -= res.D_s.lo; /* cycle difference */
M_NEG(res.D_s.hi, res.D_s.lo);
/* properly add seconds */
- p1 = secs;
- p2 = (secs < 0) ? ~(u_int32)0 : 0;
+ p2 = 0;
+ if (secs < 0) {
+ p1 = (u_int32) -secs;
+ M_NEG(p2, p1);
+ } else {
+ p1 = (u_int32) secs;
+ }
M_ADD(res.D_s.hi, res.D_s.lo, p2, p1);
#endif
return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, utm->tm_sec);
}
+/*
+ *---------------------------------------------------------------------
+ * take a 'struct calendar' and convert it to a 'time_t'
+ *---------------------------------------------------------------------
+ */
+time_t
+ntpcal_date_to_time(
+ const struct calendar *jd)
+{
+ vint64 join;
+ int32 days, secs;
+
+ days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS;
+ secs = ntpcal_date_to_daysec(jd);
+ join = ntpcal_dayjoin(days, secs);
+ return vint64_to_time(&join);
+}
+
+
/*
* ==================================================================
*
#include "ntp_stdlib.h"
#include "ntp_random.h"
#include "ntpd.h" /* for sys_precision */
+#include "timevalops.h"
+#include "timespecops.h"
+#include "ntp_calendar.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#define FUZZ 500e-6 /* fuzz pivot */
+#ifndef USE_COMPILETIME_PIVOT
+# define USE_COMPILETIME_PIVOT 1
+#endif
+
+#if defined(HAVE_CLOCK_GETTIME)
+# define GET_SYSTIME_AS_TIMESPEC(tsp) clock_gettime(CLOCK_REALTIME, tsp)
+#elif defined(HAVE_GETCLOCK)
+# define GET_SYSTIME_AS_TIMESPEC(tsp) getclock(TIMEOFDAY, tsp)
+#elif !defined(GETTIMEOFDAY)
+# include "bletch: cannot get system time?"
+#endif
+
+
/*
* These routines (get_systime, step_systime, adj_systime) implement an
* interface between the system independent NTP clock and the Unix
{
double dtemp;
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
+#if defined(GET_SYSTIME_AS_TIMESPEC)
+
struct timespec ts; /* seconds and nanoseconds */
/*
* Convert Unix timespec from seconds and nanoseconds to NTP
* seconds and fraction.
*/
-# ifdef HAVE_CLOCK_GETTIME
- clock_gettime(CLOCK_REALTIME, &ts);
-# else
- getclock(TIMEOFDAY, &ts);
-# endif
+ GET_SYSTIME_AS_TIMESPEC(&ts);
now->l_i = (int32)ts.tv_sec + JAN_1970;
dtemp = 0;
if (sys_tick > FUZZ)
}
now->l_uf = (u_int32)(dtemp * FRAC);
-#else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
+#else /* have GETTIMEOFDAY */
+
struct timeval tv; /* seconds and microseconds */
/*
}
now->l_uf = (u_int32)(dtemp * FRAC);
-#endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
+#endif /* have GETTIMEOFDAY */
}
/*
* step_systime - step the system clock.
*/
+
int
step_systime(
- double now
+ double step
)
{
- struct timeval timetv, adjtv, oldtimetv;
- int isneg = 0;
- double dtemp;
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
- struct timespec ts;
-#endif
+ time_t pivot; /* for ntp era unfolding */
+ struct timeval timetv, tvlast, tvdiff;
+ l_fp fp_ofs, fp_sys; /* offset and target system time in FP */
- dtemp = sys_residual + now;
- if (dtemp < 0) {
- isneg = 1;
- dtemp = - dtemp;
- adjtv.tv_sec = (int32)dtemp;
- adjtv.tv_usec = (u_int32)((dtemp -
- (double)adjtv.tv_sec) * 1e6 + .5);
- } else {
- adjtv.tv_sec = (int32)dtemp;
- adjtv.tv_usec = (u_int32)((dtemp -
- (double)adjtv.tv_sec) * 1e6 + .5);
+ /* Get pivot time for NTP era unfolding. Since we don't step
+ * very often, we can afford to do the whole calculation from
+ * scratch. And we're not in the time-critical path yet.
+ */
+#if SIZEOF_TIME_T > 4
+ /*
+ * This code makes sure the resulting time stamp for the new
+ * system time is in the 2^32 seconds starting at 1970-01-01,
+ * 00:00:00 UTC.
+ */
+ pivot = 0x80000000U;
+#if USE_COMPILETIME_PIVOT
+ /*
+ * Add the compile time minus 10 years to get a possible target
+ * area of (compile time - 10 years) to (compile time + 126
+ * years). This should be sufficient for a given binary of
+ * NTPD.
+ */
+ {
+ struct calendar jd;
+
+ if (ntpcal_get_build_date(&jd)) {
+ jd.year -= 10;
+ pivot += ntpcal_date_to_time(&jd);
+ } else {
+ msyslog(LOG_ERR,
+ "step-systime: assume 1970-01-01 as build date");
+ }
}
-#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
-# ifdef HAVE_CLOCK_GETTIME
- (void) clock_gettime(CLOCK_REALTIME, &ts);
-# else
- (void) getclock(TIMEOFDAY, &ts);
-# endif
- timetv.tv_sec = ts.tv_sec;
- timetv.tv_usec = ts.tv_nsec / 1000;
-#else /* not HAVE_GETCLOCK */
- (void) GETTIMEOFDAY(&timetv, (struct timezone *)0);
-#endif /* not HAVE_GETCLOCK */
-
- oldtimetv = timetv;
-
-#ifdef DEBUG
- if (debug)
- printf("step_systime: step %.6f residual %.6f\n", now, sys_residual);
+#endif /* USE_COMPILETIME_PIVOT */
+#else
+ /* This makes sure the resulting time stamp is on or after
+ * 1969-12-31/23:59:59 UTC and gives us additional two years,
+ * from the change of NTP era in 2036 to the UNIX rollover in
+ * 2038. (Minus one second, but that won't hurt.) We *really*
+ * need a longer 'time_t' after that! Or a different baseline,
+ * but that would cause other serious trouble, too.
+ */
+ pivot = 0x7FFFFFFFU;
#endif
- if (isneg) {
- timetv.tv_sec -= adjtv.tv_sec;
- timetv.tv_usec -= adjtv.tv_usec;
- if (timetv.tv_usec < 0) {
- timetv.tv_sec--;
- timetv.tv_usec += 1000000;
- }
- } else {
- timetv.tv_sec += adjtv.tv_sec;
- timetv.tv_usec += adjtv.tv_usec;
- if (timetv.tv_usec >= 1000000) {
- timetv.tv_sec++;
- timetv.tv_usec -= 1000000;
- }
+
+ /* get the complete jump distance as l_fp */
+ DTOLFP(sys_residual, &fp_sys);
+ DTOLFP(step, &fp_ofs);
+ L_ADD(&fp_ofs, &fp_sys);
+
+ /* ---> time-critical path starts ---> */
+
+ /* get the current time as l_fp (without fuzz) and as struct timeval */
+#if defined(GET_SYSTIME_AS_TIMESPEC)
+ {
+ struct timespec timets;
+ (void) GET_SYSTIME_AS_TIMESPEC(&timets);
+ timespec_abstolfp(&fp_sys, &timets);
+ tvlast.tv_sec = timets.tv_sec;
+ tvlast.tv_usec = (timets.tv_nsec + 500) / 1000;
}
+#else /* have GETTIMEOFDAY */
+ {
+ (void) GETTIMEOFDAY(&tvlast, NULL);
+ timeval_abstolfp(&fp_sys, &tvlast);
+ }
+#endif
+
+ /* get the target time as l_fp */
+ L_ADD(&fp_sys, &fp_ofs);
+
+ /* unfold the new system time */
+ timeval_absfromlfp(&timetv, &fp_sys, &pivot);
+
+ /* now set new system time */
if (ntp_set_tod(&timetv, NULL) != 0) {
msyslog(LOG_ERR, "step-systime: %m");
return (0);
}
+
+ /* <--- time-critical path ended with 'ntp_set_tod()' <--- */
+
sys_residual = 0;
if (step_callback)
(*step_callback)();
*
* This might become even Uglier...
*/
- if (oldtimetv.tv_sec != timetv.tv_sec)
+ timeval_sub(&tvdiff, &timetv, &tvlast);
+ timeval_abs(&tvdiff, &tvdiff);
+ if (tvdiff.tv_sec > 0)
{
#ifdef HAVE_UTMP_H
struct utmp ut;
utmpname(_PATH_UTMP);
ut.ut_type = OLD_TIME;
strlcpy(ut.ut_line, OTIME_MSG, sizeof(ut.ut_line));
- ut.ut_time = oldtimetv.tv_sec;
+ ut.ut_time = tvlast.tv_sec;
setutent();
pututline(&ut);
ut.ut_type = NEW_TIME;
# ifdef HAVE_PUTUTXLINE
utx.ut_type = OLD_TIME;
strlcpy(utx.ut_line, OTIME_MSG, sizeof(utx.ut_line));
- utx.ut_tv = oldtimetv;
+ utx.ut_tv = tvlast;
setutxent();
pututxline(&utx);
utx.ut_type = NEW_TIME;
utmpname(_PATH_WTMP);
ut.ut_type = OLD_TIME;
strlcpy(ut.ut_line, OTIME_MSG, sizeof(ut.ut_line));
- ut.ut_time = oldtimetv.tv_sec;
+ ut.ut_time = tvlast.tv_sec;
setutent();
pututline(&ut);
ut.ut_type = NEW_TIME;
#ifdef UPDATE_WTMPX
# ifdef HAVE_PUTUTXLINE
utx.ut_type = OLD_TIME;
- utx.ut_tv = oldtimetv;
+ utx.ut_tv = tvlast;
strlcpy(utx.ut_line, OTIME_MSG, sizeof(utx.ut_line));
# ifdef HAVE_UPDWTMPX
updwtmpx(WTMPX_FILE, &utx);