diff options
Diffstat (limited to 'contrib/ntp/libntp/systime.c')
-rw-r--r-- | contrib/ntp/libntp/systime.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/contrib/ntp/libntp/systime.c b/contrib/ntp/libntp/systime.c new file mode 100644 index 000000000000..5e50fc52d6cd --- /dev/null +++ b/contrib/ntp/libntp/systime.c @@ -0,0 +1,404 @@ +/* + * systime -- routines to fiddle a UNIX clock. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif +#ifdef HAVE_UTMP_H +# include <utmp.h> +#endif /* HAVE_UTMP_H */ +#ifdef HAVE_UTMPX_H +# include <utmpx.h> +#endif /* HAVE_UTMPX_H */ + +#include "ntp_machine.h" +#include "ntp_fp.h" +#include "ntp_syslog.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" + +#if defined SYS_WINNT +#include "ntp_timer.h" +extern DWORD units_per_tick; +static long last_Adj = 0; +#endif + +int systime_10ms_ticks = 0; /* adj sysclock in 10ms increments */ + +#define MAXFREQ 500e-6 + +/* + * These routines (init_systime, get_systime, step_systime, adj_systime) + * implement an interface between the (more or less) system independent + * bits of NTP and the peculiarities of dealing with the Unix system + * clock. + */ +double sys_residual = 0; /* residual from previous adjustment */ +double sys_maxfreq = MAXFREQ; /* max frequency correction */ + + +/* + * get_systime - return the system time in timestamp format biased by + * the current time offset. + */ +void +get_systime( + l_fp *now + ) +{ +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) + struct timespec ts; +#else + struct timeval tv; +#endif + double dtemp; + + /* + * We use nanosecond time if we can get it. Watch out for + * rounding wiggles, which may overflow the fraction. + */ +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) +# ifdef HAVE_CLOCK_GETTIME + (void) clock_gettime(CLOCK_REALTIME, &ts); +# else + (void) getclock(TIMEOFDAY, &ts); +# endif + now->l_i = ts.tv_sec + JAN_1970; + dtemp = ts.tv_nsec * FRAC / 1e9; + if (dtemp >= FRAC) + now->l_i++; + now->l_uf = (u_int32)dtemp; +#else /* HAVE_CLOCK_GETTIME */ + (void) GETTIMEOFDAY(&tv, (struct timezone *)0); + now->l_i = tv.tv_sec + JAN_1970; + + if (systime_10ms_ticks) { + /* fake better than 10ms resolution by interpolating + accumulated residual (in adj_systime(), see below) */ + dtemp = tv.tv_usec / 1e6; + if (sys_residual < 5000e-6 && sys_residual > -5000e-6) { + dtemp += sys_residual; + if (dtemp < 0) { + now->l_i--; + dtemp++; + } + } + dtemp *= FRAC; + } else { + dtemp = tv.tv_usec * FRAC / 1e6; + } + + if (dtemp >= FRAC) + now->l_i++; + now->l_uf = (u_int32)dtemp; +#endif /* HAVE_CLOCK_GETTIME */ + +} + + +/* + * adj_systime - called once every second to make system time adjustments. + * Returns 1 if okay, 0 if trouble. + */ +int +adj_systime( + double now + ) +{ + double dtemp; + struct timeval adjtv; + u_char isneg = 0; +#if !defined SYS_WINNT && !defined SYS_CYGWIN32 + struct timeval oadjtv; +#else + int rc; + long dwTimeAdjustment; +#endif + + /* + * Add the residual from the previous adjustment to the new + * adjustment, bound and round. + */ + dtemp = sys_residual + now; + sys_residual = 0; + if (dtemp < 0) { + isneg = 1; + dtemp = -dtemp; + } + + if (systime_10ms_ticks) { + /* accumulate changes until we have enough to adjust a tick */ + if (dtemp < 5000e-6) { + if (isneg) sys_residual = -dtemp; + else sys_residual = dtemp; + dtemp = 0; + } else { + if (isneg) sys_residual = 10000e-6 - dtemp; + else sys_residual = dtemp - 10000e-6; + dtemp = 10000e-6; + } + } else { + if (dtemp > sys_maxfreq) + dtemp = sys_maxfreq; + } + +#ifdef SYS_WINNT + dtemp = dtemp * 1000000.0; +#else + dtemp = dtemp * 1e6 + .5; +#endif + if (isneg) + dtemp = -dtemp; + adjtv.tv_sec = 0; + adjtv.tv_usec = (int32)dtemp; + +#if defined SYS_WINNT || defined SYS_CYGWIN32 + /* dtemp is in micro seconds. NT uses 100 ns units, + * so a unit change in dwTimeAdjustment corresponds + * to slewing 10 ppm. + * Calculate the number of 100ns units to add, + * and leave the remainder in dtemp */ + dwTimeAdjustment = dtemp / 10; + dtemp += (double) -dwTimeAdjustment * 10.0; + dwTimeAdjustment += units_per_tick; + + /* only adjust the clock if adjustment changes */ + if (last_Adj != dwTimeAdjustment) { + last_Adj = dwTimeAdjustment; +# ifdef DEBUG + if (debug > 1) + printf("SetSystemTimeAdjustment( %ld)\n", dwTimeAdjustment); +# endif + rc = !SetSystemTimeAdjustment(dwTimeAdjustment, FALSE); + } + else rc = 0; + if (rc) +#else + /* + * Here we do the actual adjustment. If for some reason the adjtime() + * call fails, like it is not implemented or something like that, + * we honk to the log. If the previous adjustment did not complete, + * we correct the residual offset. + */ + /* casey - we need a posix type thang here */ + if (adjtime(&adjtv, &oadjtv) < 0) +#endif /* SYS_WINNT */ + { + msyslog(LOG_ERR, "Can't adjust time: %m"); + return 0; + } + else { +#if !defined (SYS_WINNT) && !defined (SYS_CYGWIN32) + sys_residual += oadjtv.tv_usec / 1e6; +#else + sys_residual = dtemp / 1000000.0; +#endif /* SYS_WINNT */ + } +#ifdef DEBUG + if (debug > 6) + printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual); +#endif + return 1; +} + + +/* + * step_systime - step the system clock. + */ +int +step_systime( + double now + ) +{ + struct timeval timetv, adjtv, oldtimetv; + int isneg = 0; + double dtemp; +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) + struct timespec ts; +#endif + + 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); + } +#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 + 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; + } + } + if (ntp_set_tod(&timetv, (struct timezone *)0) != 0) { + msyslog(LOG_ERR, "Can't set time of day: %m"); + return (0); + } + sys_residual = 0; + +#ifdef NEED_HPUX_ADJTIME + /* + * CHECKME: is this correct when called by ntpdate????? + */ + _clear_adjtime(); +#endif + + /* + * FreeBSD, for example, has: + * struct utmp { + * char ut_line[UT_LINESIZE]; + * char ut_name[UT_NAMESIZE]; + * char ut_host[UT_HOSTSIZE]; + * long ut_time; + * }; + * and appends line="|", name="date", host="", time for the OLD + * and appends line="{", name="date", host="", time for the NEW + * to _PATH_WTMP . + * + * Some OSes have utmp, some have utmpx. + */ + + /* + * Write old and new time entries in utmp and wtmp if step adjustment + * is greater than one second. + * + * This might become even Uglier... + */ + if (oldtimetv.tv_sec != timetv.tv_sec) + { +#ifdef HAVE_UTMP_H + struct utmp ut; +#endif +#ifdef HAVE_UTMPX_H + struct utmpx utx; +#endif + +#ifdef HAVE_UTMP_H + memset((char *)&ut, 0, sizeof(ut)); +#endif +#ifdef HAVE_UTMPX_H + memset((char *)&utx, 0, sizeof(utx)); +#endif + + /* UTMP */ + +#ifdef UPDATE_UTMP +# ifdef HAVE_PUTUTLINE + ut.ut_type = OLD_TIME; + (void)strcpy(ut.ut_line, OTIME_MSG); + ut.ut_time = oldtimetv.tv_sec; + pututline(&ut); + setutent(); + ut.ut_type = NEW_TIME; + (void)strcpy(ut.ut_line, NTIME_MSG); + ut.ut_time = timetv.tv_sec; + pututline(&ut); + endutent(); +# else /* not HAVE_PUTUTLINE */ +# endif /* not HAVE_PUTUTLINE */ +#endif /* UPDATE_UTMP */ + + /* UTMPX */ + +#ifdef UPDATE_UTMPX +# ifdef HAVE_PUTUTXLINE + utx.ut_type = OLD_TIME; + (void)strcpy(utx.ut_line, OTIME_MSG); + utx.ut_tv = oldtimetv; + pututxline(&utx); + setutxent(); + utx.ut_type = NEW_TIME; + (void)strcpy(utx.ut_line, NTIME_MSG); + utx.ut_tv = timetv; + pututxline(&utx); + endutxent(); +# else /* not HAVE_PUTUTXLINE */ +# endif /* not HAVE_PUTUTXLINE */ +#endif /* UPDATE_UTMPX */ + + /* WTMP */ + +#ifdef UPDATE_WTMP +# ifdef HAVE_PUTUTLINE + utmpname(WTMP_FILE); + ut.ut_type = OLD_TIME; + (void)strcpy(ut.ut_line, OTIME_MSG); + ut.ut_time = oldtimetv.tv_sec; + pututline(&ut); + ut.ut_type = NEW_TIME; + (void)strcpy(ut.ut_line, NTIME_MSG); + ut.ut_time = timetv.tv_sec; + pututline(&ut); + endutent(); +# else /* not HAVE_PUTUTLINE */ +# endif /* not HAVE_PUTUTLINE */ +#endif /* UPDATE_WTMP */ + + /* WTMPX */ + +#ifdef UPDATE_WTMPX +# ifdef HAVE_PUTUTXLINE + utx.ut_type = OLD_TIME; + utx.ut_tv = oldtimetv; + (void)strcpy(utx.ut_line, OTIME_MSG); +# ifdef HAVE_UPDWTMPX + updwtmpx(WTMPX_FILE, &utx); +# else /* not HAVE_UPDWTMPX */ +# endif /* not HAVE_UPDWTMPX */ +# else /* not HAVE_PUTUTXLINE */ +# endif /* not HAVE_PUTUTXLINE */ +# ifdef HAVE_PUTUTXLINE + utx.ut_type = NEW_TIME; + utx.ut_tv = timetv; + (void)strcpy(utx.ut_line, NTIME_MSG); +# ifdef HAVE_UPDWTMPX + updwtmpx(WTMPX_FILE, &utx); +# else /* not HAVE_UPDWTMPX */ +# endif /* not HAVE_UPDWTMPX */ +# else /* not HAVE_PUTUTXLINE */ +# endif /* not HAVE_PUTUTXLINE */ +#endif /* UPDATE_WTMPX */ + + } + return (1); +} |