1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * systime -- routines to fiddle a UNIX clock. 39c2daa00SOllivier Robert * 49c2daa00SOllivier Robert * ATTENTION: Get approval from Dave Mills on all changes to this file! 59c2daa00SOllivier Robert * 6c0b746e5SOllivier Robert */ 7224ba2bdSOllivier Robert #include "ntp_machine.h" 8224ba2bdSOllivier Robert #include "ntp_fp.h" 9224ba2bdSOllivier Robert #include "ntp_syslog.h" 10224ba2bdSOllivier Robert #include "ntp_unixtime.h" 11224ba2bdSOllivier Robert #include "ntp_stdlib.h" 12ea906c41SOllivier Robert #include "ntp_random.h" 13ea906c41SOllivier Robert #include "ntpd.h" /* for sys_precision */ 14c0b746e5SOllivier Robert 159c2daa00SOllivier Robert #ifdef SIM 169c2daa00SOllivier Robert # include "ntpsim.h" 179c2daa00SOllivier Robert #endif /*SIM */ 189c2daa00SOllivier Robert 19c0b746e5SOllivier Robert #ifdef HAVE_SYS_PARAM_H 20c0b746e5SOllivier Robert # include <sys/param.h> 21c0b746e5SOllivier Robert #endif 22c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H 23c0b746e5SOllivier Robert # include <utmp.h> 24c0b746e5SOllivier Robert #endif /* HAVE_UTMP_H */ 25c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H 26c0b746e5SOllivier Robert # include <utmpx.h> 27c0b746e5SOllivier Robert #endif /* HAVE_UTMPX_H */ 28c0b746e5SOllivier Robert 29c0b746e5SOllivier Robert /* 309c2daa00SOllivier Robert * These routines (get_systime, step_systime, adj_systime) implement an 319c2daa00SOllivier Robert * interface between the system independent NTP clock and the Unix 329c2daa00SOllivier Robert * system clock in various architectures and operating systems. 339c2daa00SOllivier Robert * 349c2daa00SOllivier Robert * Time is a precious quantity in these routines and every effort is 359c2daa00SOllivier Robert * made to minimize errors by always rounding toward zero and amortizing 369c2daa00SOllivier Robert * adjustment residues. By default the adjustment quantum is 1 us for 379c2daa00SOllivier Robert * the usual Unix tickadj() system call, but this can be increased if 38ea906c41SOllivier Robert * necessary by the tick configuration command. For instance, when the 399c2daa00SOllivier Robert * adjtime() quantum is a clock tick for a 100-Hz clock, the quantum 409c2daa00SOllivier Robert * should be 10 ms. 41c0b746e5SOllivier Robert */ 42ea906c41SOllivier Robert #if defined RELIANTUNIX_CLOCK || defined SCO5_CLOCK 43ea906c41SOllivier Robert double sys_tick = 10e-3; /* 10 ms tickadj() */ 44ea906c41SOllivier Robert #else 45ea906c41SOllivier Robert double sys_tick = 1e-6; /* 1 us tickadj() */ 46ea906c41SOllivier Robert #endif 479c2daa00SOllivier Robert double sys_residual = 0; /* adjustment residue (s) */ 48c0b746e5SOllivier Robert 499c2daa00SOllivier Robert #ifndef SIM 50c0b746e5SOllivier Robert 51c0b746e5SOllivier Robert /* 529c2daa00SOllivier Robert * get_systime - return system time in NTP timestamp format. 53c0b746e5SOllivier Robert */ 54c0b746e5SOllivier Robert void 55c0b746e5SOllivier Robert get_systime( 569c2daa00SOllivier Robert l_fp *now /* system time */ 57c0b746e5SOllivier Robert ) 58c0b746e5SOllivier Robert { 59c0b746e5SOllivier Robert double dtemp; 60c0b746e5SOllivier Robert 61c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) 629c2daa00SOllivier Robert struct timespec ts; /* seconds and nanoseconds */ 639c2daa00SOllivier Robert 649c2daa00SOllivier Robert /* 659c2daa00SOllivier Robert * Convert Unix clock from seconds and nanoseconds to seconds. 66ea906c41SOllivier Robert * The bottom is only two bits down, so no need for fuzz. 67ea906c41SOllivier Robert * Some systems don't have that level of precision, however... 689c2daa00SOllivier Robert */ 69c0b746e5SOllivier Robert # ifdef HAVE_CLOCK_GETTIME 709c2daa00SOllivier Robert clock_gettime(CLOCK_REALTIME, &ts); 71c0b746e5SOllivier Robert # else 729c2daa00SOllivier Robert getclock(TIMEOFDAY, &ts); 73c0b746e5SOllivier Robert # endif 74c0b746e5SOllivier Robert now->l_i = ts.tv_sec + JAN_1970; 759c2daa00SOllivier Robert dtemp = ts.tv_nsec / 1e9; 76c0b746e5SOllivier Robert 779c2daa00SOllivier Robert #else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */ 789c2daa00SOllivier Robert struct timeval tv; /* seconds and microseconds */ 799c2daa00SOllivier Robert 809c2daa00SOllivier Robert /* 819c2daa00SOllivier Robert * Convert Unix clock from seconds and microseconds to seconds. 82ea906c41SOllivier Robert * Add in unbiased random fuzz beneath the microsecond. 839c2daa00SOllivier Robert */ 849c2daa00SOllivier Robert GETTIMEOFDAY(&tv, NULL); 859c2daa00SOllivier Robert now->l_i = tv.tv_sec + JAN_1970; 86c0b746e5SOllivier Robert dtemp = tv.tv_usec / 1e6; 879c2daa00SOllivier Robert 889c2daa00SOllivier Robert #endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */ 899c2daa00SOllivier Robert 909c2daa00SOllivier Robert /* 91ea906c41SOllivier Robert * ntp_random() produces 31 bits (always nonnegative). 92ea906c41SOllivier Robert * This bit is done only after the precision has been 93ea906c41SOllivier Robert * determined. 94ea906c41SOllivier Robert */ 95ea906c41SOllivier Robert if (sys_precision != 0) 96ea906c41SOllivier Robert dtemp += (ntp_random() / FRAC - .5) / (1 << 97ea906c41SOllivier Robert -sys_precision); 98ea906c41SOllivier Robert 99ea906c41SOllivier Robert /* 1009c2daa00SOllivier Robert * Renormalize to seconds past 1900 and fraction. 1019c2daa00SOllivier Robert */ 102c0b746e5SOllivier Robert dtemp += sys_residual; 1039c2daa00SOllivier Robert if (dtemp >= 1) { 1049c2daa00SOllivier Robert dtemp -= 1; 1059c2daa00SOllivier Robert now->l_i++; 106ea906c41SOllivier Robert } else if (dtemp < 0) { 1079c2daa00SOllivier Robert dtemp += 1; 108c0b746e5SOllivier Robert now->l_i--; 109c0b746e5SOllivier Robert } 110c0b746e5SOllivier Robert dtemp *= FRAC; 111c0b746e5SOllivier Robert now->l_uf = (u_int32)dtemp; 112c0b746e5SOllivier Robert } 113c0b746e5SOllivier Robert 114c0b746e5SOllivier Robert 115c0b746e5SOllivier Robert /* 1169c2daa00SOllivier Robert * adj_systime - adjust system time by the argument. 117c0b746e5SOllivier Robert */ 118a151a66cSOllivier Robert #if !defined SYS_WINNT 1199c2daa00SOllivier Robert int /* 0 okay, 1 error */ 120c0b746e5SOllivier Robert adj_systime( 1219c2daa00SOllivier Robert double now /* adjustment (s) */ 122c0b746e5SOllivier Robert ) 123c0b746e5SOllivier Robert { 1249c2daa00SOllivier Robert struct timeval adjtv; /* new adjustment */ 1259c2daa00SOllivier Robert struct timeval oadjtv; /* residual adjustment */ 126c0b746e5SOllivier Robert double dtemp; 1279c2daa00SOllivier Robert long ticks; 1289c2daa00SOllivier Robert int isneg = 0; 129c0b746e5SOllivier Robert 130c0b746e5SOllivier Robert /* 1319c2daa00SOllivier Robert * Most Unix adjtime() implementations adjust the system clock 1329c2daa00SOllivier Robert * in microsecond quanta, but some adjust in 10-ms quanta. We 1339c2daa00SOllivier Robert * carefully round the adjustment to the nearest quantum, then 1349c2daa00SOllivier Robert * adjust in quanta and keep the residue for later. 135c0b746e5SOllivier Robert */ 1369c2daa00SOllivier Robert dtemp = now + sys_residual; 137c0b746e5SOllivier Robert if (dtemp < 0) { 138c0b746e5SOllivier Robert isneg = 1; 139c0b746e5SOllivier Robert dtemp = -dtemp; 140c0b746e5SOllivier Robert } 1419c2daa00SOllivier Robert adjtv.tv_sec = (long)dtemp; 1429c2daa00SOllivier Robert dtemp -= adjtv.tv_sec; 1439c2daa00SOllivier Robert ticks = (long)(dtemp / sys_tick + .5); 1449c2daa00SOllivier Robert adjtv.tv_usec = (long)(ticks * sys_tick * 1e6); 1459c2daa00SOllivier Robert dtemp -= adjtv.tv_usec / 1e6; 1469c2daa00SOllivier Robert sys_residual = dtemp; 147c0b746e5SOllivier Robert 148c0b746e5SOllivier Robert /* 1499c2daa00SOllivier Robert * Convert to signed seconds and microseconds for the Unix 1509c2daa00SOllivier Robert * adjtime() system call. Note we purposely lose the adjtime() 1519c2daa00SOllivier Robert * leftover. 152c0b746e5SOllivier Robert */ 1539c2daa00SOllivier Robert if (isneg) { 1549c2daa00SOllivier Robert adjtv.tv_sec = -adjtv.tv_sec; 1559c2daa00SOllivier Robert adjtv.tv_usec = -adjtv.tv_usec; 156ea906c41SOllivier Robert sys_residual = -sys_residual; 157c0b746e5SOllivier Robert } 158ea906c41SOllivier Robert if (adjtv.tv_sec != 0 || adjtv.tv_usec != 0) { 1599c2daa00SOllivier Robert if (adjtime(&adjtv, &oadjtv) < 0) { 1609c2daa00SOllivier Robert msyslog(LOG_ERR, "adj_systime: %m"); 1619c2daa00SOllivier Robert return (0); 162c0b746e5SOllivier Robert } 163ea906c41SOllivier Robert } 1649c2daa00SOllivier Robert return (1); 165c0b746e5SOllivier Robert } 166a151a66cSOllivier Robert #endif 167c0b746e5SOllivier Robert 168c0b746e5SOllivier Robert 169c0b746e5SOllivier Robert /* 170c0b746e5SOllivier Robert * step_systime - step the system clock. 171c0b746e5SOllivier Robert */ 172c0b746e5SOllivier Robert int 173c0b746e5SOllivier Robert step_systime( 174c0b746e5SOllivier Robert double now 175c0b746e5SOllivier Robert ) 176c0b746e5SOllivier Robert { 177c0b746e5SOllivier Robert struct timeval timetv, adjtv, oldtimetv; 178c0b746e5SOllivier Robert int isneg = 0; 179c0b746e5SOllivier Robert double dtemp; 180c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) 181c0b746e5SOllivier Robert struct timespec ts; 182c0b746e5SOllivier Robert #endif 183c0b746e5SOllivier Robert 184c0b746e5SOllivier Robert dtemp = sys_residual + now; 185c0b746e5SOllivier Robert if (dtemp < 0) { 186c0b746e5SOllivier Robert isneg = 1; 187c0b746e5SOllivier Robert dtemp = - dtemp; 188c0b746e5SOllivier Robert adjtv.tv_sec = (int32)dtemp; 1899c2daa00SOllivier Robert adjtv.tv_usec = (u_int32)((dtemp - 1909c2daa00SOllivier Robert (double)adjtv.tv_sec) * 1e6 + .5); 191c0b746e5SOllivier Robert } else { 192c0b746e5SOllivier Robert adjtv.tv_sec = (int32)dtemp; 1939c2daa00SOllivier Robert adjtv.tv_usec = (u_int32)((dtemp - 1949c2daa00SOllivier Robert (double)adjtv.tv_sec) * 1e6 + .5); 195c0b746e5SOllivier Robert } 196c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) 197c0b746e5SOllivier Robert # ifdef HAVE_CLOCK_GETTIME 198c0b746e5SOllivier Robert (void) clock_gettime(CLOCK_REALTIME, &ts); 199c0b746e5SOllivier Robert # else 200c0b746e5SOllivier Robert (void) getclock(TIMEOFDAY, &ts); 201c0b746e5SOllivier Robert # endif 202c0b746e5SOllivier Robert timetv.tv_sec = ts.tv_sec; 203c0b746e5SOllivier Robert timetv.tv_usec = ts.tv_nsec / 1000; 204c0b746e5SOllivier Robert #else /* not HAVE_GETCLOCK */ 205c0b746e5SOllivier Robert (void) GETTIMEOFDAY(&timetv, (struct timezone *)0); 206c0b746e5SOllivier Robert #endif /* not HAVE_GETCLOCK */ 207c0b746e5SOllivier Robert 208c0b746e5SOllivier Robert oldtimetv = timetv; 209c0b746e5SOllivier Robert 210c0b746e5SOllivier Robert #ifdef DEBUG 211c0b746e5SOllivier Robert if (debug) 212c0b746e5SOllivier Robert printf("step_systime: step %.6f residual %.6f\n", now, sys_residual); 213c0b746e5SOllivier Robert #endif 214c0b746e5SOllivier Robert if (isneg) { 215c0b746e5SOllivier Robert timetv.tv_sec -= adjtv.tv_sec; 216c0b746e5SOllivier Robert timetv.tv_usec -= adjtv.tv_usec; 217c0b746e5SOllivier Robert if (timetv.tv_usec < 0) { 218c0b746e5SOllivier Robert timetv.tv_sec--; 219c0b746e5SOllivier Robert timetv.tv_usec += 1000000; 220c0b746e5SOllivier Robert } 221c0b746e5SOllivier Robert } else { 222c0b746e5SOllivier Robert timetv.tv_sec += adjtv.tv_sec; 223c0b746e5SOllivier Robert timetv.tv_usec += adjtv.tv_usec; 224c0b746e5SOllivier Robert if (timetv.tv_usec >= 1000000) { 225c0b746e5SOllivier Robert timetv.tv_sec++; 226c0b746e5SOllivier Robert timetv.tv_usec -= 1000000; 227c0b746e5SOllivier Robert } 228c0b746e5SOllivier Robert } 2299c2daa00SOllivier Robert if (ntp_set_tod(&timetv, NULL) != 0) { 2309c2daa00SOllivier Robert msyslog(LOG_ERR, "step-systime: %m"); 231c0b746e5SOllivier Robert return (0); 232c0b746e5SOllivier Robert } 233c0b746e5SOllivier Robert sys_residual = 0; 234c0b746e5SOllivier Robert 235c0b746e5SOllivier Robert #ifdef NEED_HPUX_ADJTIME 236c0b746e5SOllivier Robert /* 237c0b746e5SOllivier Robert * CHECKME: is this correct when called by ntpdate????? 238c0b746e5SOllivier Robert */ 239c0b746e5SOllivier Robert _clear_adjtime(); 240c0b746e5SOllivier Robert #endif 241c0b746e5SOllivier Robert 242c0b746e5SOllivier Robert /* 243c0b746e5SOllivier Robert * FreeBSD, for example, has: 244c0b746e5SOllivier Robert * struct utmp { 245c0b746e5SOllivier Robert * char ut_line[UT_LINESIZE]; 246c0b746e5SOllivier Robert * char ut_name[UT_NAMESIZE]; 247c0b746e5SOllivier Robert * char ut_host[UT_HOSTSIZE]; 248c0b746e5SOllivier Robert * long ut_time; 249c0b746e5SOllivier Robert * }; 250c0b746e5SOllivier Robert * and appends line="|", name="date", host="", time for the OLD 251c0b746e5SOllivier Robert * and appends line="{", name="date", host="", time for the NEW 252c0b746e5SOllivier Robert * to _PATH_WTMP . 253c0b746e5SOllivier Robert * 254c0b746e5SOllivier Robert * Some OSes have utmp, some have utmpx. 255c0b746e5SOllivier Robert */ 256c0b746e5SOllivier Robert 257c0b746e5SOllivier Robert /* 2589c2daa00SOllivier Robert * Write old and new time entries in utmp and wtmp if step 2599c2daa00SOllivier Robert * adjustment is greater than one second. 260c0b746e5SOllivier Robert * 261c0b746e5SOllivier Robert * This might become even Uglier... 262c0b746e5SOllivier Robert */ 263c0b746e5SOllivier Robert if (oldtimetv.tv_sec != timetv.tv_sec) 264c0b746e5SOllivier Robert { 265c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H 266c0b746e5SOllivier Robert struct utmp ut; 267c0b746e5SOllivier Robert #endif 268c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H 269c0b746e5SOllivier Robert struct utmpx utx; 270c0b746e5SOllivier Robert #endif 271c0b746e5SOllivier Robert 272c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H 273c0b746e5SOllivier Robert memset((char *)&ut, 0, sizeof(ut)); 274c0b746e5SOllivier Robert #endif 275c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H 276c0b746e5SOllivier Robert memset((char *)&utx, 0, sizeof(utx)); 277c0b746e5SOllivier Robert #endif 278c0b746e5SOllivier Robert 279c0b746e5SOllivier Robert /* UTMP */ 280c0b746e5SOllivier Robert 281c0b746e5SOllivier Robert #ifdef UPDATE_UTMP 282c0b746e5SOllivier Robert # ifdef HAVE_PUTUTLINE 283c0b746e5SOllivier Robert ut.ut_type = OLD_TIME; 284c0b746e5SOllivier Robert (void)strcpy(ut.ut_line, OTIME_MSG); 285c0b746e5SOllivier Robert ut.ut_time = oldtimetv.tv_sec; 286c0b746e5SOllivier Robert pututline(&ut); 287c0b746e5SOllivier Robert setutent(); 288c0b746e5SOllivier Robert ut.ut_type = NEW_TIME; 289c0b746e5SOllivier Robert (void)strcpy(ut.ut_line, NTIME_MSG); 290c0b746e5SOllivier Robert ut.ut_time = timetv.tv_sec; 291c0b746e5SOllivier Robert pututline(&ut); 292c0b746e5SOllivier Robert endutent(); 293c0b746e5SOllivier Robert # else /* not HAVE_PUTUTLINE */ 294c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTLINE */ 295c0b746e5SOllivier Robert #endif /* UPDATE_UTMP */ 296c0b746e5SOllivier Robert 297c0b746e5SOllivier Robert /* UTMPX */ 298c0b746e5SOllivier Robert 299c0b746e5SOllivier Robert #ifdef UPDATE_UTMPX 300c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE 301c0b746e5SOllivier Robert utx.ut_type = OLD_TIME; 302c0b746e5SOllivier Robert (void)strcpy(utx.ut_line, OTIME_MSG); 303c0b746e5SOllivier Robert utx.ut_tv = oldtimetv; 304c0b746e5SOllivier Robert pututxline(&utx); 305c0b746e5SOllivier Robert setutxent(); 306c0b746e5SOllivier Robert utx.ut_type = NEW_TIME; 307c0b746e5SOllivier Robert (void)strcpy(utx.ut_line, NTIME_MSG); 308c0b746e5SOllivier Robert utx.ut_tv = timetv; 309c0b746e5SOllivier Robert pututxline(&utx); 310c0b746e5SOllivier Robert endutxent(); 311c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */ 312c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */ 313c0b746e5SOllivier Robert #endif /* UPDATE_UTMPX */ 314c0b746e5SOllivier Robert 315c0b746e5SOllivier Robert /* WTMP */ 316c0b746e5SOllivier Robert 317c0b746e5SOllivier Robert #ifdef UPDATE_WTMP 318c0b746e5SOllivier Robert # ifdef HAVE_PUTUTLINE 319c0b746e5SOllivier Robert utmpname(WTMP_FILE); 320c0b746e5SOllivier Robert ut.ut_type = OLD_TIME; 321c0b746e5SOllivier Robert (void)strcpy(ut.ut_line, OTIME_MSG); 322c0b746e5SOllivier Robert ut.ut_time = oldtimetv.tv_sec; 323c0b746e5SOllivier Robert pututline(&ut); 324c0b746e5SOllivier Robert ut.ut_type = NEW_TIME; 325c0b746e5SOllivier Robert (void)strcpy(ut.ut_line, NTIME_MSG); 326c0b746e5SOllivier Robert ut.ut_time = timetv.tv_sec; 327c0b746e5SOllivier Robert pututline(&ut); 328c0b746e5SOllivier Robert endutent(); 329c0b746e5SOllivier Robert # else /* not HAVE_PUTUTLINE */ 330c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTLINE */ 331c0b746e5SOllivier Robert #endif /* UPDATE_WTMP */ 332c0b746e5SOllivier Robert 333c0b746e5SOllivier Robert /* WTMPX */ 334c0b746e5SOllivier Robert 335c0b746e5SOllivier Robert #ifdef UPDATE_WTMPX 336c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE 337c0b746e5SOllivier Robert utx.ut_type = OLD_TIME; 338c0b746e5SOllivier Robert utx.ut_tv = oldtimetv; 339c0b746e5SOllivier Robert (void)strcpy(utx.ut_line, OTIME_MSG); 340c0b746e5SOllivier Robert # ifdef HAVE_UPDWTMPX 341c0b746e5SOllivier Robert updwtmpx(WTMPX_FILE, &utx); 342c0b746e5SOllivier Robert # else /* not HAVE_UPDWTMPX */ 343c0b746e5SOllivier Robert # endif /* not HAVE_UPDWTMPX */ 344c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */ 345c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */ 346c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE 347c0b746e5SOllivier Robert utx.ut_type = NEW_TIME; 348c0b746e5SOllivier Robert utx.ut_tv = timetv; 349c0b746e5SOllivier Robert (void)strcpy(utx.ut_line, NTIME_MSG); 350c0b746e5SOllivier Robert # ifdef HAVE_UPDWTMPX 351c0b746e5SOllivier Robert updwtmpx(WTMPX_FILE, &utx); 352c0b746e5SOllivier Robert # else /* not HAVE_UPDWTMPX */ 353c0b746e5SOllivier Robert # endif /* not HAVE_UPDWTMPX */ 354c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */ 355c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */ 356c0b746e5SOllivier Robert #endif /* UPDATE_WTMPX */ 357c0b746e5SOllivier Robert 358c0b746e5SOllivier Robert } 359c0b746e5SOllivier Robert return (1); 360c0b746e5SOllivier Robert } 3619c2daa00SOllivier Robert 3629c2daa00SOllivier Robert #else /* SIM */ 3639c2daa00SOllivier Robert /* 3649c2daa00SOllivier Robert * Clock routines for the simulator - Harish Nair, with help 3659c2daa00SOllivier Robert */ 3669c2daa00SOllivier Robert /* 3679c2daa00SOllivier Robert * get_systime - return the system time in NTP timestamp format 3689c2daa00SOllivier Robert */ 3699c2daa00SOllivier Robert void 3709c2daa00SOllivier Robert get_systime( 3719c2daa00SOllivier Robert l_fp *now /* current system time in l_fp */ ) 3729c2daa00SOllivier Robert { 3739c2daa00SOllivier Robert /* 3749c2daa00SOllivier Robert * To fool the code that determines the local clock precision, 3759c2daa00SOllivier Robert * we advance the clock a minimum of 200 nanoseconds on every 3769c2daa00SOllivier Robert * clock read. This is appropriate for a typical modern machine 3779c2daa00SOllivier Robert * with nanosecond clocks. Note we make no attempt here to 3789c2daa00SOllivier Robert * simulate reading error, since the error is so small. This may 3799c2daa00SOllivier Robert * change when the need comes to implement picosecond clocks. 3809c2daa00SOllivier Robert */ 3819c2daa00SOllivier Robert if (ntp_node.ntp_time == ntp_node.last_time) 3829c2daa00SOllivier Robert ntp_node.ntp_time += 200e-9; 3839c2daa00SOllivier Robert ntp_node.last_time = ntp_node.ntp_time; 3849c2daa00SOllivier Robert DTOLFP(ntp_node.ntp_time, now); 3859c2daa00SOllivier Robert } 3869c2daa00SOllivier Robert 3879c2daa00SOllivier Robert 3889c2daa00SOllivier Robert /* 3899c2daa00SOllivier Robert * adj_systime - advance or retard the system clock exactly like the 3909c2daa00SOllivier Robert * real thng. 3919c2daa00SOllivier Robert */ 3929c2daa00SOllivier Robert int /* always succeeds */ 3939c2daa00SOllivier Robert adj_systime( 3949c2daa00SOllivier Robert double now /* time adjustment (s) */ 3959c2daa00SOllivier Robert ) 3969c2daa00SOllivier Robert { 3979c2daa00SOllivier Robert struct timeval adjtv; /* new adjustment */ 3989c2daa00SOllivier Robert double dtemp; 3999c2daa00SOllivier Robert long ticks; 4009c2daa00SOllivier Robert int isneg = 0; 4019c2daa00SOllivier Robert 4029c2daa00SOllivier Robert /* 4039c2daa00SOllivier Robert * Most Unix adjtime() implementations adjust the system clock 4049c2daa00SOllivier Robert * in microsecond quanta, but some adjust in 10-ms quanta. We 4059c2daa00SOllivier Robert * carefully round the adjustment to the nearest quantum, then 4069c2daa00SOllivier Robert * adjust in quanta and keep the residue for later. 4079c2daa00SOllivier Robert */ 4089c2daa00SOllivier Robert dtemp = now + sys_residual; 4099c2daa00SOllivier Robert if (dtemp < 0) { 4109c2daa00SOllivier Robert isneg = 1; 4119c2daa00SOllivier Robert dtemp = -dtemp; 4129c2daa00SOllivier Robert } 4139c2daa00SOllivier Robert adjtv.tv_sec = (long)dtemp; 4149c2daa00SOllivier Robert dtemp -= adjtv.tv_sec; 4159c2daa00SOllivier Robert ticks = (long)(dtemp / sys_tick + .5); 4169c2daa00SOllivier Robert adjtv.tv_usec = (long)(ticks * sys_tick * 1e6); 4179c2daa00SOllivier Robert dtemp -= adjtv.tv_usec / 1e6; 4189c2daa00SOllivier Robert sys_residual = dtemp; 4199c2daa00SOllivier Robert 4209c2daa00SOllivier Robert /* 4219c2daa00SOllivier Robert * Convert to signed seconds and microseconds for the Unix 4229c2daa00SOllivier Robert * adjtime() system call. Note we purposely lose the adjtime() 4239c2daa00SOllivier Robert * leftover. 4249c2daa00SOllivier Robert */ 4259c2daa00SOllivier Robert if (isneg) { 4269c2daa00SOllivier Robert adjtv.tv_sec = -adjtv.tv_sec; 4279c2daa00SOllivier Robert adjtv.tv_usec = -adjtv.tv_usec; 4289c2daa00SOllivier Robert sys_residual = -sys_residual; 4299c2daa00SOllivier Robert } 430ea906c41SOllivier Robert ntp_node.adj = now; 4319c2daa00SOllivier Robert return (1); 4329c2daa00SOllivier Robert } 4339c2daa00SOllivier Robert 4349c2daa00SOllivier Robert 4359c2daa00SOllivier Robert /* 4369c2daa00SOllivier Robert * step_systime - step the system clock. We are religious here. 4379c2daa00SOllivier Robert */ 4389c2daa00SOllivier Robert int /* always succeeds */ 4399c2daa00SOllivier Robert step_systime( 4409c2daa00SOllivier Robert double now /* step adjustment (s) */ 4419c2daa00SOllivier Robert ) 4429c2daa00SOllivier Robert { 443ea906c41SOllivier Robert #ifdef DEBUG 444ea906c41SOllivier Robert if (debug) 445ea906c41SOllivier Robert printf("step_systime: time %.6f adj %.6f\n", 446ea906c41SOllivier Robert ntp_node.ntp_time, now); 447ea906c41SOllivier Robert #endif 448ea906c41SOllivier Robert ntp_node.ntp_time += now; 4499c2daa00SOllivier Robert return (1); 4509c2daa00SOllivier Robert } 4519c2daa00SOllivier Robert 4529c2daa00SOllivier Robert /* 4539c2daa00SOllivier Robert * node_clock - update the clocks 4549c2daa00SOllivier Robert */ 4559c2daa00SOllivier Robert int /* always succeeds */ 4569c2daa00SOllivier Robert node_clock( 4579c2daa00SOllivier Robert Node *n, /* global node pointer */ 4589c2daa00SOllivier Robert double t /* node time */ 4599c2daa00SOllivier Robert ) 4609c2daa00SOllivier Robert { 4619c2daa00SOllivier Robert double dtemp; 4629c2daa00SOllivier Robert 4639c2daa00SOllivier Robert /* 4649c2daa00SOllivier Robert * Advance client clock (ntp_time). Advance server clock 4659c2daa00SOllivier Robert * (clk_time) adjusted for systematic and random frequency 4669c2daa00SOllivier Robert * errors. The random error is a random walk computed as the 4679c2daa00SOllivier Robert * integral of samples from a Gaussian distribution. 4689c2daa00SOllivier Robert */ 4699c2daa00SOllivier Robert dtemp = t - n->ntp_time; 4709c2daa00SOllivier Robert n->time = t; 4719c2daa00SOllivier Robert n->ntp_time += dtemp; 4729c2daa00SOllivier Robert n->ferr += gauss(0, dtemp * n->fnse); 4739c2daa00SOllivier Robert n->clk_time += dtemp * (1 + n->ferr); 4749c2daa00SOllivier Robert 4759c2daa00SOllivier Robert /* 4769c2daa00SOllivier Robert * Perform the adjtime() function. If the adjustment completed 4779c2daa00SOllivier Robert * in the previous interval, amortize the entire amount; if not, 4789c2daa00SOllivier Robert * carry the leftover to the next interval. 4799c2daa00SOllivier Robert */ 4809c2daa00SOllivier Robert dtemp *= n->slew; 4819c2daa00SOllivier Robert if (dtemp < fabs(n->adj)) { 4829c2daa00SOllivier Robert if (n->adj < 0) { 4839c2daa00SOllivier Robert n->adj += dtemp; 4849c2daa00SOllivier Robert n->ntp_time -= dtemp; 4859c2daa00SOllivier Robert } else { 4869c2daa00SOllivier Robert n->adj -= dtemp; 4879c2daa00SOllivier Robert n->ntp_time += dtemp; 4889c2daa00SOllivier Robert } 4899c2daa00SOllivier Robert } else { 4909c2daa00SOllivier Robert n->ntp_time += n->adj; 4919c2daa00SOllivier Robert n->adj = 0; 4929c2daa00SOllivier Robert } 4939c2daa00SOllivier Robert return (0); 4949c2daa00SOllivier Robert } 4959c2daa00SOllivier Robert 4969c2daa00SOllivier Robert 4979c2daa00SOllivier Robert /* 4989c2daa00SOllivier Robert * gauss() - returns samples from a gaussion distribution 4999c2daa00SOllivier Robert */ 5009c2daa00SOllivier Robert double /* Gaussian sample */ 5019c2daa00SOllivier Robert gauss( 5029c2daa00SOllivier Robert double m, /* sample mean */ 5039c2daa00SOllivier Robert double s /* sample standard deviation (sigma) */ 5049c2daa00SOllivier Robert ) 5059c2daa00SOllivier Robert { 5069c2daa00SOllivier Robert double q1, q2; 5079c2daa00SOllivier Robert 5089c2daa00SOllivier Robert /* 5099c2daa00SOllivier Robert * Roll a sample from a Gaussian distribution with mean m and 5109c2daa00SOllivier Robert * standard deviation s. For m = 0, s = 1, mean(y) = 0, 5119c2daa00SOllivier Robert * std(y) = 1. 5129c2daa00SOllivier Robert */ 5139c2daa00SOllivier Robert if (s == 0) 5149c2daa00SOllivier Robert return (m); 5159c2daa00SOllivier Robert while ((q1 = drand48()) == 0); 5169c2daa00SOllivier Robert q2 = drand48(); 5179c2daa00SOllivier Robert return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2)); 5189c2daa00SOllivier Robert } 5199c2daa00SOllivier Robert 5209c2daa00SOllivier Robert 5219c2daa00SOllivier Robert /* 5229c2daa00SOllivier Robert * poisson() - returns samples from a network delay distribution 5239c2daa00SOllivier Robert */ 5249c2daa00SOllivier Robert double /* delay sample (s) */ 5259c2daa00SOllivier Robert poisson( 5269c2daa00SOllivier Robert double m, /* fixed propagation delay (s) */ 5279c2daa00SOllivier Robert double s /* exponential parameter (mu) */ 5289c2daa00SOllivier Robert ) 5299c2daa00SOllivier Robert { 5309c2daa00SOllivier Robert double q1; 5319c2daa00SOllivier Robert 5329c2daa00SOllivier Robert /* 5339c2daa00SOllivier Robert * Roll a sample from a composite distribution with propagation 5349c2daa00SOllivier Robert * delay m and exponential distribution time with parameter s. 5359c2daa00SOllivier Robert * For m = 0, s = 1, mean(y) = std(y) = 1. 5369c2daa00SOllivier Robert */ 5379c2daa00SOllivier Robert if (s == 0) 5389c2daa00SOllivier Robert return (m); 5399c2daa00SOllivier Robert while ((q1 = drand48()) == 0); 5409c2daa00SOllivier Robert return (m - s * log(q1 * s)); 5419c2daa00SOllivier Robert } 5429c2daa00SOllivier Robert #endif /* SIM */ 543