Home | History | Annotate | Line # | Download | only in libntp
      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 (at) 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