1 /* $NetBSD: systime.c,v 1.8 2024/08/18 20:47:13 christos Exp $ */ 2 3 /* 4 * systime -- routines to fiddle a UNIX clock. 5 * 6 * ATTENTION: Get approval from Dave Mills on all changes to this file! 7 * 8 */ 9 #include <config.h> 10 #include <math.h> 11 12 #include "ntp.h" 13 #include "ntpd.h" 14 #include "ntp_syslog.h" 15 #include "ntp_stdlib.h" 16 #include "ntp_random.h" 17 #include "iosignal.h" 18 #include "timevalops.h" 19 #include "timespecops.h" 20 #include "ntp_calendar.h" 21 22 #ifdef HAVE_SYS_PARAM_H 23 # include <sys/param.h> 24 #endif 25 #ifdef HAVE_UTMP_H 26 # include <utmp.h> 27 #endif /* HAVE_UTMP_H */ 28 #ifdef HAVE_UTMPX_H 29 # include <utmpx.h> 30 #endif /* HAVE_UTMPX_H */ 31 32 int allow_panic = FALSE; /* allow panic correction (-g) */ 33 int enable_panic_check = TRUE; /* Can we check allow_panic's state? */ 34 35 u_long sys_lamport; /* Lamport violation */ 36 u_long sys_tsrounding; /* timestamp rounding errors */ 37 38 #ifndef USE_COMPILETIME_PIVOT 39 # define USE_COMPILETIME_PIVOT 1 40 #endif 41 42 /* 43 * These routines (get_systime, step_systime, adj_systime) implement an 44 * interface between the system independent NTP clock and the Unix 45 * system clock in various architectures and operating systems. Time is 46 * a precious quantity in these routines and every effort is made to 47 * minimize errors by unbiased rounding and amortizing adjustment 48 * residues. 49 * 50 * In order to improve the apparent resolution, provide unbiased 51 * rounding and most importantly ensure that the readings cannot be 52 * predicted, the low-order unused portion of the time below the minimum 53 * time to read the clock is filled with an unbiased random fuzz. 54 * 55 * The sys_tick variable specifies the system clock tick interval in 56 * seconds, for stepping clocks, defined as those which return times 57 * less than MINSTEP greater than the previous reading. For systems that 58 * use a high-resolution counter such that each clock reading is always 59 * at least MINSTEP greater than the prior, sys_tick is the time to read 60 * the system clock. 61 * 62 * The sys_fuzz variable measures the minimum time to read the system 63 * clock, regardless of its precision. When reading the system clock 64 * using get_systime() after sys_tick and sys_fuzz have been determined, 65 * ntpd ensures each unprocessed clock reading is no less than sys_fuzz 66 * later than the prior unprocessed reading, and then fuzzes the bits 67 * below sys_fuzz in the timestamp returned, ensuring each of its 68 * resulting readings is strictly later than the previous. 69 * 70 * When slewing the system clock using adj_systime() (with the kernel 71 * loop discipline unavailable or disabled), adjtime() offsets are 72 * quantized to sys_tick, if sys_tick is greater than sys_fuzz, which 73 * is to say if the OS presents a stepping clock. Otherwise, offsets 74 * are quantized to the microsecond resolution of adjtime()'s timeval 75 * input. The remaining correction sys_residual is carried into the 76 * next adjtime() and meanwhile is also factored into get_systime() 77 * readings. 78 */ 79 double sys_tick = 0; /* tick size or time to read (s) */ 80 double sys_fuzz = 0; /* min. time to read the clock (s) */ 81 long sys_fuzz_nsec = 0; /* min. time to read the clock (ns) */ 82 double measured_tick; /* non-overridable sys_tick (s) */ 83 double sys_residual = 0; /* adjustment residue (s) */ 84 int trunc_os_clock; /* sys_tick > measured_tick */ 85 time_stepped_callback step_callback; 86 87 #ifndef SIM 88 /* perlinger@ntp.org: As 'get_sysime()' does it's own check for clock 89 * backstepping, this could probably become a local variable in 90 * 'get_systime()' and the cruft associated with communicating via a 91 * static value could be removed after the v4.2.8 release. 92 */ 93 static int lamport_violated; /* clock was stepped back */ 94 #endif /* !SIM */ 95 96 #ifdef DEBUG 97 static int systime_init_done; 98 # define DONE_SYSTIME_INIT() systime_init_done = TRUE 99 #else 100 # define DONE_SYSTIME_INIT() do {} while (FALSE) 101 #endif 102 103 #ifdef HAVE_SIGNALED_IO 104 int using_sigio; 105 #endif 106 107 #ifdef SYS_WINNT 108 CRITICAL_SECTION get_systime_cs; 109 #endif 110 111 112 void 113 set_sys_fuzz( 114 double fuzz_val 115 ) 116 { 117 sys_fuzz = fuzz_val; 118 INSIST(sys_fuzz >= 0); 119 INSIST(sys_fuzz <= 1.0); 120 /* [Bug 3450] ensure nsec fuzz >= sys_fuzz to reduce chance of 121 * short-falling fuzz advance 122 */ 123 sys_fuzz_nsec = (long)ceil(sys_fuzz * 1e9); 124 } 125 126 127 void 128 init_systime(void) 129 { 130 INIT_GET_SYSTIME_CRITSEC(); 131 INIT_WIN_PRECISE_TIME(); 132 DONE_SYSTIME_INIT(); 133 } 134 135 136 #ifndef SIM /* ntpsim.c has get_systime() and friends for sim */ 137 138 static inline void 139 get_ostime( 140 struct timespec * tsp 141 ) 142 { 143 int rc; 144 long ticks; 145 146 #if defined(HAVE_CLOCK_GETTIME) 147 rc = clock_gettime(CLOCK_REALTIME, tsp); 148 #elif defined(HAVE_GETCLOCK) 149 rc = getclock(TIMEOFDAY, tsp); 150 #else 151 struct timeval tv; 152 153 rc = GETTIMEOFDAY(&tv, NULL); 154 tsp->tv_sec = tv.tv_sec; 155 tsp->tv_nsec = tv.tv_usec * 1000; 156 #endif 157 if (rc < 0) { 158 msyslog(LOG_ERR, "read system clock failed: %m (%d)", 159 errno); 160 exit(1); 161 } 162 163 if (trunc_os_clock) { 164 ticks = (long)((tsp->tv_nsec * 1e-9) / sys_tick); 165 tsp->tv_nsec = (long)(ticks * 1e9 * sys_tick); 166 } 167 } 168 169 170 /* 171 * get_systime - return system time in NTP timestamp format. 172 */ 173 void 174 get_systime( 175 l_fp *now /* system time */ 176 ) 177 { 178 static struct timespec ts_last; /* last sampled os time */ 179 static struct timespec ts_prev; /* prior os time */ 180 static l_fp lfp_prev; /* prior result */ 181 struct timespec ts; /* seconds and nanoseconds */ 182 struct timespec ts_min; /* earliest permissible */ 183 struct timespec ts_lam; /* lamport fictional increment */ 184 double dfuzz; 185 l_fp result; 186 l_fp lfpfuzz; 187 l_fp lfpdelta; 188 189 get_ostime(&ts); 190 DEBUG_REQUIRE(systime_init_done); 191 ENTER_GET_SYSTIME_CRITSEC(); 192 193 /* First check if here was a Lamport violation, that is, two 194 * successive calls to 'get_ostime()' resulted in negative 195 * time difference. Use a few milliseconds of permissible 196 * tolerance -- being too sharp can hurt here. (This is intented 197 * for the Win32 target, where the HPC interpolation might 198 * introduce small steps backward. It should not be an issue on 199 * systems where get_ostime() results in a true syscall.) 200 */ 201 if (cmp_tspec(add_tspec_ns(ts, 50000000), ts_last) < 0) { 202 lamport_violated = 1; 203 sys_lamport++; 204 } 205 ts_last = ts; 206 207 /* 208 * After default_get_precision() has set a nonzero sys_fuzz, 209 * ensure every reading of the OS clock advances by at least 210 * sys_fuzz over the prior reading, thereby assuring each 211 * fuzzed result is strictly later than the prior. Limit the 212 * necessary fiction to 1 second. 213 */ 214 if (!USING_SIGIO()) { 215 ts_min = add_tspec_ns(ts_prev, sys_fuzz_nsec); 216 if (cmp_tspec(ts, ts_min) < 0) { 217 ts_lam = sub_tspec(ts_min, ts); 218 if (ts_lam.tv_sec > 0 && !lamport_violated) { 219 msyslog(LOG_ERR, 220 "get_systime Lamport advance exceeds one second (%.9f)", 221 ts_lam.tv_sec + 222 1e-9 * ts_lam.tv_nsec); 223 exit(1); 224 } 225 if (!lamport_violated) 226 ts = ts_min; 227 } 228 ts_prev = ts; 229 } 230 231 /* convert from timespec to l_fp fixed-point */ 232 result = tspec_stamp_to_lfp(ts); 233 234 /* 235 * Add in the fuzz. 'ntp_random()' returns [0..2**31-1] so we 236 * must scale up the result by 2.0 to cover the full fractional 237 * range. 238 */ 239 dfuzz = ntp_uurandom() * sys_fuzz; 240 DTOLFP(dfuzz, &lfpfuzz); 241 L_ADD(&result, &lfpfuzz); 242 243 /* 244 * Ensure result is strictly greater than prior result (ignoring 245 * sys_residual's effect for now) once sys_fuzz has been 246 * determined. 247 * 248 * [Bug 3450] Rounding errors and time slew can lead to a 249 * violation of the expected postcondition. This is bound to 250 * happen from time to time (depending on state of the random 251 * generator, the current slew and the closeness of system time 252 * stamps drawn) and does not warrant a syslog entry. Instead it 253 * makes much more sense to ensure the postcondition and hop 254 * along silently. 255 */ 256 if (!USING_SIGIO()) { 257 if ( !L_ISZERO(&lfp_prev) 258 && !lamport_violated 259 && (sys_fuzz > 0.0) 260 ) { 261 lfpdelta = result; 262 L_SUB(&lfpdelta, &lfp_prev); 263 L_SUBUF(&lfpdelta, 1); 264 if (lfpdelta.l_i < 0) 265 { 266 L_NEG(&lfpdelta); 267 DPRINTF(1, ("get_systime: postcond failed by %s secs, fixed\n", 268 lfptoa(&lfpdelta, 9))); 269 result = lfp_prev; 270 L_ADDUF(&result, 1); 271 sys_tsrounding++; 272 } 273 } 274 lfp_prev = result; 275 if (lamport_violated) 276 lamport_violated = FALSE; 277 } 278 LEAVE_GET_SYSTIME_CRITSEC(); 279 *now = result; 280 } 281 282 283 /* 284 * adj_systime - adjust system time by the argument. 285 */ 286 #if !defined SYS_WINNT 287 int /* 0 okay, 1 error */ 288 adj_systime( 289 double now /* adjustment (s) */ 290 ) 291 { 292 struct timeval adjtv; /* new adjustment */ 293 struct timeval oadjtv; /* residual adjustment */ 294 double quant; /* quantize to multiples of */ 295 double dtemp; 296 long ticks; 297 int isneg = 0; 298 299 /* 300 * The Windows port adj_systime() depends on being called each 301 * second even when there's no additional correction, to allow 302 * emulation of adjtime() behavior on top of an API that simply 303 * sets the current rate. This POSIX implementation needs to 304 * ignore invocations with zero correction, otherwise ongoing 305 * EVNT_NSET adjtime() can be aborted by a tiny adjtime() 306 * triggered by sys_residual. 307 */ 308 if (0. == now) { 309 if (enable_panic_check && allow_panic) { 310 msyslog(LOG_ERR, "adj_systime: allow_panic is TRUE!"); 311 INSIST(!allow_panic); 312 } 313 return TRUE; 314 } 315 316 /* 317 * Most Unix adjtime() implementations adjust the system clock 318 * in microsecond quanta, but some adjust in 10-ms quanta. We 319 * carefully round the adjustment to the nearest quantum, then 320 * adjust in quanta and keep the residue for later. 321 */ 322 dtemp = now + sys_residual; 323 if (dtemp < 0) { 324 isneg = 1; 325 dtemp = -dtemp; 326 } 327 adjtv.tv_sec = (long)dtemp; 328 dtemp -= adjtv.tv_sec; 329 if (sys_tick > sys_fuzz) 330 quant = sys_tick; 331 else 332 quant = 1e-6; 333 ticks = (long)(dtemp / quant + .5); 334 adjtv.tv_usec = (long)(ticks * quant * 1.e6 + .5); 335 /* The rounding in the conversions could us push over the 336 * limits: make sure the result is properly normalised! 337 * note: sign comes later, all numbers non-negative here. 338 */ 339 if (adjtv.tv_usec >= 1000000) { 340 adjtv.tv_sec += 1; 341 adjtv.tv_usec -= 1000000; 342 dtemp -= 1.; 343 } 344 /* set the new residual with leftover from correction */ 345 sys_residual = dtemp - adjtv.tv_usec * 1.e-6; 346 347 /* 348 * Convert to signed seconds and microseconds for the Unix 349 * adjtime() system call. Note we purposely lose the adjtime() 350 * leftover. 351 */ 352 if (isneg) { 353 adjtv.tv_sec = -adjtv.tv_sec; 354 adjtv.tv_usec = -adjtv.tv_usec; 355 sys_residual = -sys_residual; 356 } 357 if (adjtv.tv_sec != 0 || adjtv.tv_usec != 0) { 358 if (adjtime(&adjtv, &oadjtv) < 0) { 359 msyslog(LOG_ERR, "adj_systime: %m"); 360 if (enable_panic_check && allow_panic) { 361 msyslog(LOG_ERR, "adj_systime: allow_panic is TRUE!"); 362 } 363 return FALSE; 364 } 365 } 366 if (enable_panic_check && allow_panic) { 367 msyslog(LOG_ERR, "adj_systime: allow_panic is TRUE!"); 368 } 369 return TRUE; 370 } 371 #endif 372 373 /* 374 * helper to keep utmp/wtmp up to date 375 */ 376 static void 377 update_uwtmp( 378 struct timeval timetv, 379 struct timeval tvlast 380 ) 381 { 382 struct timeval tvdiff; 383 /* 384 * FreeBSD, for example, has: 385 * struct utmp { 386 * char ut_line[UT_LINESIZE]; 387 * char ut_name[UT_NAMESIZE]; 388 * char ut_host[UT_HOSTSIZE]; 389 * long ut_time; 390 * }; 391 * and appends line="|", name="date", host="", time for the OLD 392 * and appends line="{", name="date", host="", time for the NEW // } 393 * to _PATH_WTMP . 394 * 395 * Some OSes have utmp, some have utmpx. 396 */ 397 398 /* 399 * Write old and new time entries in utmp and wtmp if step 400 * adjustment is greater than one second. 401 * 402 * This might become even Uglier... 403 */ 404 tvdiff = abs_tval(sub_tval(timetv, tvlast)); 405 if (tvdiff.tv_sec > 0) { 406 #ifdef HAVE_UTMP_H 407 struct utmp ut; 408 #endif 409 #ifdef HAVE_UTMPX_H 410 struct utmpx utx; 411 #endif 412 413 #ifdef HAVE_UTMP_H 414 ZERO(ut); 415 #endif 416 #ifdef HAVE_UTMPX_H 417 ZERO(utx); 418 #endif 419 420 /* UTMP */ 421 422 #ifdef UPDATE_UTMP 423 # ifdef HAVE_PUTUTLINE 424 # ifndef _PATH_UTMP 425 # define _PATH_UTMP UTMP_FILE 426 # endif 427 utmpname(_PATH_UTMP); 428 ut.ut_type = OLD_TIME; 429 strlcpy(ut.ut_line, OTIME_MSG, sizeof(ut.ut_line)); 430 ut.ut_time = tvlast.tv_sec; 431 setutent(); 432 pututline(&ut); 433 ut.ut_type = NEW_TIME; 434 strlcpy(ut.ut_line, NTIME_MSG, sizeof(ut.ut_line)); 435 ut.ut_time = timetv.tv_sec; 436 setutent(); 437 pututline(&ut); 438 endutent(); 439 # else /* not HAVE_PUTUTLINE */ 440 # endif /* not HAVE_PUTUTLINE */ 441 #endif /* UPDATE_UTMP */ 442 443 /* UTMPX */ 444 445 #ifdef UPDATE_UTMPX 446 # ifdef HAVE_PUTUTXLINE 447 utx.ut_type = OLD_TIME; 448 strlcpy(utx.ut_line, OTIME_MSG, sizeof(utx.ut_line)); 449 utx.ut_tv = tvlast; 450 setutxent(); 451 pututxline(&utx); 452 utx.ut_type = NEW_TIME; 453 strlcpy(utx.ut_line, NTIME_MSG, sizeof(utx.ut_line)); 454 utx.ut_tv = timetv; 455 setutxent(); 456 pututxline(&utx); 457 endutxent(); 458 # else /* not HAVE_PUTUTXLINE */ 459 # endif /* not HAVE_PUTUTXLINE */ 460 #endif /* UPDATE_UTMPX */ 461 462 /* WTMP */ 463 464 #ifdef UPDATE_WTMP 465 # ifdef HAVE_PUTUTLINE 466 # ifndef _PATH_WTMP 467 # define _PATH_WTMP WTMP_FILE 468 # endif 469 utmpname(_PATH_WTMP); 470 ut.ut_type = OLD_TIME; 471 strlcpy(ut.ut_line, OTIME_MSG, sizeof(ut.ut_line)); 472 ut.ut_time = tvlast.tv_sec; 473 setutent(); 474 pututline(&ut); 475 ut.ut_type = NEW_TIME; 476 strlcpy(ut.ut_line, NTIME_MSG, sizeof(ut.ut_line)); 477 ut.ut_time = timetv.tv_sec; 478 setutent(); 479 pututline(&ut); 480 endutent(); 481 # else /* not HAVE_PUTUTLINE */ 482 # endif /* not HAVE_PUTUTLINE */ 483 #endif /* UPDATE_WTMP */ 484 485 /* WTMPX */ 486 487 #ifdef UPDATE_WTMPX 488 # ifdef HAVE_PUTUTXLINE 489 utx.ut_type = OLD_TIME; 490 utx.ut_tv = tvlast; 491 strlcpy(utx.ut_line, OTIME_MSG, sizeof(utx.ut_line)); 492 # ifdef HAVE_UPDWTMPX 493 updwtmpx(WTMPX_FILE, &utx); 494 # else /* not HAVE_UPDWTMPX */ 495 # endif /* not HAVE_UPDWTMPX */ 496 # else /* not HAVE_PUTUTXLINE */ 497 # endif /* not HAVE_PUTUTXLINE */ 498 # ifdef HAVE_PUTUTXLINE 499 utx.ut_type = NEW_TIME; 500 utx.ut_tv = timetv; 501 strlcpy(utx.ut_line, NTIME_MSG, sizeof(utx.ut_line)); 502 # ifdef HAVE_UPDWTMPX 503 updwtmpx(WTMPX_FILE, &utx); 504 # else /* not HAVE_UPDWTMPX */ 505 # endif /* not HAVE_UPDWTMPX */ 506 # else /* not HAVE_PUTUTXLINE */ 507 # endif /* not HAVE_PUTUTXLINE */ 508 #endif /* UPDATE_WTMPX */ 509 510 } 511 } 512 513 /* 514 * step_systime - step the system clock. 515 */ 516 517 int 518 step_systime( 519 double step 520 ) 521 { 522 time_t pivot; /* for ntp era unfolding */ 523 struct timeval timetv, tvlast; 524 struct timespec timets; 525 l_fp fp_ofs, fp_sys; /* offset and target system time in FP */ 526 527 /* 528 * Get pivot time for NTP era unfolding. Since we don't step 529 * very often, we can afford to do the whole calculation from 530 * scratch. And we're not in the time-critical path yet. 531 */ 532 #if SIZEOF_TIME_T > 4 533 pivot = basedate_get_eracenter(); 534 #else 535 /* This makes sure the resulting time stamp is on or after 536 * 1969-12-31/23:59:59 UTC and gives us additional two years, 537 * from the change of NTP era in 2036 to the UNIX rollover in 538 * 2038. (Minus one second, but that won't hurt.) We *really* 539 * need a longer 'time_t' after that! Or a different baseline, 540 * but that would cause other serious trouble, too. 541 */ 542 pivot = 0x7FFFFFFF; 543 #endif 544 545 /* get the complete jump distance as l_fp */ 546 DTOLFP(sys_residual, &fp_sys); 547 DTOLFP(step, &fp_ofs); 548 L_ADD(&fp_ofs, &fp_sys); 549 550 /* ---> time-critical path starts ---> */ 551 552 /* get the current time as l_fp (without fuzz) and as struct timeval */ 553 get_ostime(&timets); 554 fp_sys = tspec_stamp_to_lfp(timets); 555 tvlast.tv_sec = timets.tv_sec; 556 tvlast.tv_usec = (timets.tv_nsec + 500) / 1000; 557 558 /* get the target time as l_fp */ 559 L_ADD(&fp_sys, &fp_ofs); 560 561 /* unfold the new system time */ 562 timetv = lfp_stamp_to_tval(fp_sys, &pivot); 563 564 /* now set new system time */ 565 if (ntp_set_tod(&timetv, NULL) != 0) { 566 msyslog(LOG_ERR, "step-systime: %m"); 567 if (enable_panic_check && allow_panic) { 568 msyslog(LOG_ERR, "step_systime: allow_panic is TRUE!"); 569 } 570 return FALSE; 571 } 572 573 /* <--- time-critical path ended with 'ntp_set_tod()' <--- */ 574 575 sys_residual = 0; 576 lamport_violated = (step < 0); 577 if (step_callback) 578 (*step_callback)(); 579 580 #ifdef NEED_HPUX_ADJTIME 581 /* 582 * CHECKME: is this correct when called by ntpdate????? 583 */ 584 _clear_adjtime(); 585 #endif 586 587 update_uwtmp(timetv, tvlast); 588 if (enable_panic_check && allow_panic) { 589 msyslog(LOG_ERR, "step_systime: allow_panic is TRUE!"); 590 INSIST(!allow_panic); 591 } 592 return TRUE; 593 } 594 595 596 #if SIZEOF_TIME_T > 4 597 static const char * 598 tv_fmt_libbuf( 599 const struct timeval * ptv 600 ) 601 { 602 char * retv; 603 vint64 secs; 604 ntpcal_split dds; 605 struct calendar jd; 606 607 secs = time_to_vint64(&ptv->tv_sec); 608 dds = ntpcal_daysplit(&secs); 609 ntpcal_daysplit_to_date(&jd, &dds, DAY_UNIX_STARTS); 610 LIB_GETBUF(retv); 611 snprintf(retv, LIB_BUFLENGTH, 612 "%04hu-%02hu-%02hu/%02hu:%02hu:%02hu.%06u", 613 jd.year, (u_short)jd.month, (u_short)jd.monthday, 614 (u_short)jd.hour, (u_short)jd.minute, (u_short)jd.second, 615 (u_int)ptv->tv_usec); 616 return retv; 617 } 618 #endif /* SIZEOF_TIME_T > 4 */ 619 620 621 int /*BOOL*/ 622 clamp_systime(void) 623 { 624 #if SIZEOF_TIME_T > 4 625 626 struct timeval tvbase, tvlast; 627 struct timespec timets; 628 629 tvbase.tv_sec = basedate_get_erabase(); 630 tvbase.tv_usec = 0; 631 632 /* ---> time-critical path starts ---> */ 633 634 /* get the current time as l_fp (without fuzz) and as struct timeval */ 635 get_ostime(&timets); 636 tvlast.tv_sec = timets.tv_sec; 637 tvlast.tv_usec = (timets.tv_nsec + 500) / 1000; 638 if (tvlast.tv_usec >= 1000000) { 639 tvlast.tv_usec -= 1000000; 640 tvlast.tv_sec += 1; 641 } 642 643 if (tvbase.tv_sec > tvlast.tv_sec) { 644 /* now set new system time */ 645 if (ntp_set_tod(&tvbase, NULL) != 0) { 646 msyslog(LOG_ERR, "clamp-systime: %m"); 647 return FALSE; 648 } 649 } else { 650 msyslog(LOG_INFO, 651 "clamp-systime: clock (%s) in allowed range", 652 tv_fmt_libbuf(&tvlast)); 653 return FALSE; 654 } 655 656 /* <--- time-critical path ended with 'ntp_set_tod()' <--- */ 657 658 sys_residual = 0; 659 lamport_violated = (tvbase.tv_sec < tvlast.tv_sec); 660 if (step_callback) 661 (*step_callback)(); 662 663 # ifdef NEED_HPUX_ADJTIME 664 /* 665 * CHECKME: is this correct when called by ntpdate????? 666 */ 667 _clear_adjtime(); 668 # endif 669 670 update_uwtmp(tvbase, tvlast); 671 msyslog(LOG_WARNING, 672 "clamp-systime: clock stepped from %s to %s!", 673 tv_fmt_libbuf(&tvlast), tv_fmt_libbuf(&tvbase)); 674 return TRUE; 675 676 #else 677 678 return FALSE; 679 680 #endif 681 } 682 683 #endif /* !SIM */ 684