From: jhc@iscp.bellcore.com (Jonathan Clark)
Subject: Changes to allow xntp3 to gradually step the system clock
Organization: Bellcore
Date: Tue, 29 Sep 92 17:43:39 GMT

We found that xntp didn't quite do what we wanted wrt clock stepping.
If xntp finds that the system clock is more than CLOCK_MAX seconds
off the ``real'' time it will just step the clock to the new time.
We wanted to be able to change the time on a master machine and have
all the clients gradually move up (or down) to the new time. Setting
SLEW_ALWAYS didn't work, the daemon seemed to get confused and refuse 
to slew the clock at all (this may be a problem with AIX, rumour has
it that the time handling stuff inside their kernel is a bit confused).

The attached is a small change to xntpd/ntp_unixclock.c which will
gradually step the clock up to a new time. I've using it with CLOCK_MINSTEP
set to 60 and SMALLSTEP set to 2, which will allow the clock to jump by
a maximum of 2 seconds every 60. It seems to work, but I've only tried
it on a set of IBM RS6000 workstations running AIX 3.2.2. Also I was
concerned that the time never step back over a minute boundary, so I
put a test in for that. I'm not convinced that this test is correct, but
I do think that it's overly complex. I got confused when I tried to work
out whether the value returned by time(2) modulo 60 was always the same
as returned by 'date +%S', especially when leap seconds were taken into
account. I decided that it wasn't, but would welcome correction by an expert.

Jonathan Clark
jhc@iscp.bellcore.com, but not for long

The Englishman never enjoys himself except for some noble purpose.
--
*** xntpd/ntp_unixclock.c.orig	Fri Sep 25 15:25:32 1992
--- xntpd/ntp_unixclock.c	Tue Sep 29 13:19:38 1992
***************
*** 3,8 ****
--- 3,36 ----
   *		     system clock
   */
  
+ /*
+  *	Normally, if the clock is off by more than CLOCK_MAX, the
+  *	clock is stepped (jumped) to the correct time.
+  *
+  *	If SMALLSTEP is defined, it must be an integer which is
+  *	the maximum allowable clock step, in seconds.. If the clock
+  *	is off by more than this amount then multiple small steps will
+  *	be done until the necessary correction has been made. If you
+  *	use this it is highly recommended that you alter CLOCK_MINSTEP
+  *	(in ntp.h) to a lower value (eg 64), otherwise it will take
+  *	a long time to correct a clock which is off.
+  *
+  *	For fear of upsetting cron the clock is never stepped back
+  *	over a minute boundary. A 2-second correction which would
+  *	take place at :01 seconds will be skipped.
+  *
+  *	Caveats:
+  *
+  *	This scheme should really be extended to permit the correction
+  *	to be a fractional number of seconds. Also the scheme to work out
+  *	whether it is a minute boundary is hokey (and may be wrong), but
+  *	I worry about leap seconds.
+  *
+  *	Jonathan Clark jhc@ulysses.att.com, for want of anywhere better
+  */
+ 
+ /* #define SMALLSTEP	2	/* max allowable step */
+ 
  #include <stdio.h>
  #include <nlist.h>
  #include <sys/types.h>
***************
*** 29,34 ****
--- 57,67 ----
  #include "ntp_unixtime.h"
  #include "ntp.h"
  
+ #ifdef SMALLSTEP
+ /* Purely to get struct tm */
+ #include <time.h>
+ #endif /* SMALLSTEP */
+ 
  #ifdef RS6000
  #undef hz
  #endif /* RS6000 */
***************
*** 245,250 ****
--- 278,309 ----
  #if defined(hpux)
  	oldtime = timetv.tv_sec;
  #endif
+ 
+ #ifdef SMALLSTEP
+ 	if (adjtv.tv_sec > SMALLSTEP)
+ 	{
+ 		adjtv.tv_sec = SMALLSTEP;
+ 		/* let the microsecond adjustment go as normal */
+ 		if (isneg)
+ 		{
+ 			/* avoid dropping back over a minute boundary */
+ 			/* get current time in correct format for localtime() */
+ 			time_t current = time(NULL);
+ 			/* hopefully localtime understands leap seconds */
+ 			struct tm *now = localtime (&current);
+ 			/* punt if we would step back over a minute boundary */
+ 			if (now->tm_sec < SMALLSTEP)
+ 				return;
+ 
+ 			/*
+ 			 * this may get leap seconds wrong - wizards?
+ 			 * if ((timetv.tv_sec % 60) <= SMALLSTEP)
+ 			 * 	return;
+ 			 */
+ 		}
+ 	}
+ #endif /* SMALLSTEP */
+ 
  #ifdef DEBUG
  	if (debug > 3)
  		printf("step: %s, sys_offset = %s, adjtv = %s, timetv = %s\n",