/* * systime -- routines to fiddle a UNIX clock. */ #include #include #include #if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD) #include #include #endif #ifdef SYS_LINUX #include "sys/timex.h" #endif #include "ntp_fp.h" #include "ntp_syslog.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #if defined(STEP_SLEW) #define SLEWALWAYS #endif extern int debug; /* * 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. These routines will run with good precision fairly independently * of your kernel's value of tickadj. I couldn't tell the difference * between tickadj==40 and tickadj==5 on a microvax, though I prefer * to set tickadj == 500/hz when in doubt. At your option you * may compile this so that your system's clock is always slewed to the * correct time even for large corrections. Of course, all of this takes * a lot of code which wouldn't be needed with a reasonable tickadj and * a willingness to let the clock be stepped occasionally. Oh well. */ /* * Clock variables. We round calls to adjtime() to adj_precision * microseconds, and limit the adjustment to tvu_maxslew microseconds * (tsf_maxslew fractional sec) in one adjustment interval. As we are * thus limited in the speed and precision with which we can adjust the * clock, we compensate by keeping the known "error" in the system time * in sys_clock_offset. This is added to timestamps returned by get_systime(). * We also remember the clock precision we computed from the kernel in * case someone asks us. */ long sys_clock; long adj_precision; /* adj precision in usec (tickadj) */ long tvu_maxslew; /* maximum adjust doable in 1<l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */ #else /* * Get the time of day, convert to time stamp format * and add in the current time offset. Then round * appropriately. */ (void) GETTIMEOFDAY(&tv, (struct timezone *)0); TVTOTS(&tv, ts); L_ADD(ts, &sys_clock_offset); if (ts->l_uf & TS_ROUNDBIT) L_ADDUF(ts, TS_ROUNDBIT); #endif /* !defined(SLEWALWAYS) */ ts->l_ui += JAN_1970; ts->l_uf &= TS_MASK; sys_clock = ts->l_ui; } /* * step_systime - do a step adjustment in the system time (at least from * NTP's point of view. */ int step_systime(ts) l_fp *ts; { #ifdef SLEWALWAYS #ifdef STEP_SLEW register u_long tmp_ui; register u_long tmp_uf; int isneg; int n; /* * Take the absolute value of the offset */ tmp_ui = ts->l_ui; tmp_uf = ts->l_uf; if (M_ISNEG(tmp_ui, tmp_uf)) { M_NEG(tmp_ui, tmp_uf); isneg = 1; } else isneg = 0; if (tmp_ui >= 3) { /* Step it and slew we might win */ n = step_systime_real(ts); if (!n) return n; if (isneg) ts->l_ui = ~0; else ts->l_ui = ~0; } #endif /* * Just add adjustment into the current offset. The update * routine will take care of bringing the system clock into * line. */ L_ADD(&sys_clock_offset, ts); return 1; #else /* SLEWALWAYS */ return step_systime_real(ts); #endif /* SLEWALWAYS */ } int max_no_complete = 20; /* * adj_systime - called once every 1<l_f; int rval; adjtv.tv_sec = adjtv.tv_usec = 0; /* * Move the current offset into the registers */ offset_i = sys_clock_offset.l_ui; offset_f = sys_clock_offset.l_uf; /* * Add the new adjustment into the system offset. Adjust the * system clock to minimize this. */ M_ADDF(offset_i, offset_f, adj); if (M_ISNEG(offset_i, offset_f)) { isneg = 1; M_NEG(offset_i, offset_f); } #ifdef DEBUG if (debug > 4) syslog(LOG_DEBUG, "adj_systime(%s): offset = %s%s\n", mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"", umfptoa(offset_i, offset_f, 9)); #endif adjtv.tv_sec = 0; if (offset_i > 0 || offset_f >= tsf_maxslew) { /* * Slew is bigger than we can complete in * the adjustment interval. Make a maximum * sized slew and reduce sys_clock_offset by this * much. */ M_SUBUF(offset_i, offset_f, tsf_maxslew); if (!isneg) { adjtv.tv_usec = tvu_maxslew; } else { adjtv.tv_usec = -tvu_maxslew; M_NEG(offset_i, offset_f); } #ifdef DEBUG if (debug > 4) printf("systime: maximum slew: %s%s, remainder = %s\n", isneg?"-":"", umfptoa(0, tsf_maxslew, 9), mfptoa(offset_i, offset_f, 9)); #endif } else { /* * We can do this slew in the time period. Do our * best approximation (rounded), save residual for * next adjustment. * * Note that offset_i is guaranteed to be 0 here. */ TSFTOTVU(offset_f, temp); #ifndef ADJTIME_IS_ACCURATE /* * Round value to be an even multiple of adj_precision */ residual = temp % adj_precision; temp -= residual; if (residual << 1 >= adj_precision) temp += adj_precision; #endif /* ADJTIME_IS_ACCURATE */ TVUTOTSF(temp, residual); M_SUBUF(offset_i, offset_f, residual); if (isneg) { adjtv.tv_usec = -temp; M_NEG(offset_i, offset_f); } else { adjtv.tv_usec = temp; } #ifdef DEBUG if (debug > 4) printf( "systime: adjtv = %s, adjts = %s, sys_clock_offset = %s\n", tvtoa(&adjtv), umfptoa(0, residual, 9), mfptoa(offset_i, offset_f, 9)); #endif } if (adjtime(&adjtv, &oadjtv) < 0) { syslog(LOG_ERR, "Can't do time adjustment: %m"); rval = 0; } else { sys_clock_offset.l_ui = offset_i; sys_clock_offset.l_uf = offset_f; rval = 1; #ifdef DEBUGRS6000 syslog(LOG_ERR, "adj_systime(%s): offset = %s%s\n", mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"", umfptoa(offset_i, offset_f, 9)); syslog(LOG_ERR, "%d %d %d %d\n", (int) adjtv.tv_sec, (int) adjtv.tv_usec, (int) oadjtv.tv_sec, (int) oadjtv.tv_usec); #endif /* DEBUGRS6000 */ if (oadjtv.tv_sec != 0 || oadjtv.tv_usec != 0) { sTVTOTS(&oadjtv, &oadjts); L_ADD(&sys_clock_offset, &oadjts); if (max_no_complete > 0) { syslog(LOG_WARNING, "Previous time adjustment didn't complete"); #ifdef DEBUG if (debug > 4) syslog(LOG_DEBUG, "Previous adjtime() incomplete, residual = %s\n", tvtoa(&oadjtv)); #endif if (--max_no_complete == 0) syslog(LOG_WARNING, "*** No more 'Prev time adj didn't complete'"); } } } return(rval); } /* * This is used by ntpdate even when xntpd does not use it! WLJ */ int step_systime_real(ts) l_fp *ts; { struct timeval timetv, adjtv; int isneg = 0; #if defined(SYS_HPUX) struct utmp ut; time_t oldtime; #endif /* * We can afford to be sloppy here since if this is called * the time is really screwed and everything is being reset. */ L_ADD(&sys_clock_offset, ts); if (L_ISNEG(&sys_clock_offset)) { isneg = 1; L_NEG(&sys_clock_offset); } TSTOTV(&sys_clock_offset, &adjtv); (void) GETTIMEOFDAY(&timetv, (struct timezone *)0); #if defined(SYS_HPUX) oldtime = timetv.tv_sec; #endif #ifdef DEBUG if (debug > 3) syslog(LOG_DEBUG, "step: %s, sys_clock_offset = %s, adjtv = %s, timetv = %s\n", lfptoa(ts, 9), lfptoa(&sys_clock_offset, 9), tvtoa(&adjtv), utvtoa(&timetv)); #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 (SETTIMEOFDAY(&timetv, (struct timezone *)0) != 0) { syslog(LOG_ERR, "Can't set time of day: %m"); return 0; } #ifdef DEBUG if (debug > 3) syslog(LOG_DEBUG, "step: new timetv = %s\n", utvtoa(&timetv)); #endif sys_clock_offset.l_ui = sys_clock_offset.l_uf = 0; #if defined(SYS_HPUX) #if (SYS_HPUX < 10) /* * CHECKME: is this correct when called by ntpdate????? */ _clear_adjtime(); #endif /* * Write old and new time entries in utmp and wtmp if step adjustment * is greater than one second. */ if (oldtime != timetv.tv_sec) { memset((char *)&ut, 0, sizeof(ut)); ut.ut_type = OLD_TIME; ut.ut_time = oldtime; (void)strcpy(ut.ut_line, OTIME_MSG); pututline(&ut); setutent(); ut.ut_type = NEW_TIME; ut.ut_time = timetv.tv_sec; (void)strcpy(ut.ut_line, NTIME_MSG); pututline(&ut); utmpname(WTMP_FILE); ut.ut_type = OLD_TIME; ut.ut_time = oldtime; (void)strcpy(ut.ut_line, OTIME_MSG); pututline(&ut); ut.ut_type = NEW_TIME; ut.ut_time = timetv.tv_sec; (void)strcpy(ut.ut_line, NTIME_MSG); pututline(&ut); endutent(); } #endif return 1; }