xref: /freebsd/contrib/ntp/libntp/systime.c (revision 9c2daa00c2315f101948c7144d62af5d5fb515cf)
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"
12c0b746e5SOllivier Robert 
139c2daa00SOllivier Robert #ifdef SIM
149c2daa00SOllivier Robert #include "ntpsim.h"
159c2daa00SOllivier Robert #endif /*SIM */
169c2daa00SOllivier Robert 
17c0b746e5SOllivier Robert #ifdef HAVE_SYS_PARAM_H
18c0b746e5SOllivier Robert # include <sys/param.h>
19c0b746e5SOllivier Robert #endif
20c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H
21c0b746e5SOllivier Robert # include <utmp.h>
22c0b746e5SOllivier Robert #endif /* HAVE_UTMP_H */
23c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H
24c0b746e5SOllivier Robert # include <utmpx.h>
25c0b746e5SOllivier Robert #endif /* HAVE_UTMPX_H */
26c0b746e5SOllivier Robert 
27c0b746e5SOllivier Robert /*
289c2daa00SOllivier Robert  * These routines (get_systime, step_systime, adj_systime) implement an
299c2daa00SOllivier Robert  * interface between the system independent NTP clock and the Unix
309c2daa00SOllivier Robert  * system clock in various architectures and operating systems.
319c2daa00SOllivier Robert  *
329c2daa00SOllivier Robert  * Time is a precious quantity in these routines and every effort is
339c2daa00SOllivier Robert  * made to minimize errors by always rounding toward zero and amortizing
349c2daa00SOllivier Robert  * adjustment residues. By default the adjustment quantum is 1 us for
359c2daa00SOllivier Robert  * the usual Unix tickadj() system call, but this can be increased if
369c2daa00SOllivier Robert  * necessary by a configuration command. For instance, when the
379c2daa00SOllivier Robert  * adjtime() quantum is a clock tick for a 100-Hz clock, the quantum
389c2daa00SOllivier Robert  * should be 10 ms.
39c0b746e5SOllivier Robert  */
409c2daa00SOllivier Robert double	sys_tick = 1e-6;	/* tickadj() quantum (s) */
419c2daa00SOllivier Robert double	sys_residual = 0;	/* adjustment residue (s) */
42c0b746e5SOllivier Robert 
439c2daa00SOllivier Robert #ifndef SIM
44c0b746e5SOllivier Robert 
45c0b746e5SOllivier Robert /*
469c2daa00SOllivier Robert  * get_systime - return system time in NTP timestamp format.
47c0b746e5SOllivier Robert  */
48c0b746e5SOllivier Robert void
49c0b746e5SOllivier Robert get_systime(
509c2daa00SOllivier Robert 	l_fp *now		/* system time */
51c0b746e5SOllivier Robert 	)
52c0b746e5SOllivier Robert {
53c0b746e5SOllivier Robert 	double dtemp;
54c0b746e5SOllivier Robert 
55c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
569c2daa00SOllivier Robert 	struct timespec ts;	/* seconds and nanoseconds */
579c2daa00SOllivier Robert 
589c2daa00SOllivier Robert 	/*
599c2daa00SOllivier Robert 	 * Convert Unix clock from seconds and nanoseconds to seconds.
609c2daa00SOllivier Robert 	 */
61c0b746e5SOllivier Robert # ifdef HAVE_CLOCK_GETTIME
629c2daa00SOllivier Robert 	clock_gettime(CLOCK_REALTIME, &ts);
63c0b746e5SOllivier Robert # else
649c2daa00SOllivier Robert 	getclock(TIMEOFDAY, &ts);
65c0b746e5SOllivier Robert # endif
66c0b746e5SOllivier Robert 	now->l_i = ts.tv_sec + JAN_1970;
679c2daa00SOllivier Robert 	dtemp = ts.tv_nsec / 1e9;
68c0b746e5SOllivier Robert 
699c2daa00SOllivier Robert #else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
709c2daa00SOllivier Robert 	struct timeval tv;	/* seconds and microseconds */
719c2daa00SOllivier Robert 
729c2daa00SOllivier Robert 	/*
739c2daa00SOllivier Robert 	 * Convert Unix clock from seconds and microseconds to seconds.
749c2daa00SOllivier Robert 	 */
759c2daa00SOllivier Robert 	GETTIMEOFDAY(&tv, NULL);
769c2daa00SOllivier Robert 	now->l_i = tv.tv_sec + JAN_1970;
77c0b746e5SOllivier Robert 	dtemp = tv.tv_usec / 1e6;
789c2daa00SOllivier Robert 
799c2daa00SOllivier Robert #endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
809c2daa00SOllivier Robert 
819c2daa00SOllivier Robert 	/*
829c2daa00SOllivier Robert 	 * Renormalize to seconds past 1900 and fraction.
839c2daa00SOllivier Robert 	 */
84c0b746e5SOllivier Robert 	dtemp += sys_residual;
859c2daa00SOllivier Robert 	if (dtemp >= 1) {
869c2daa00SOllivier Robert 		dtemp -= 1;
879c2daa00SOllivier Robert 		now->l_i++;
889c2daa00SOllivier Robert 	} else if (dtemp < -1) {
899c2daa00SOllivier Robert 		dtemp += 1;
90c0b746e5SOllivier Robert 		now->l_i--;
91c0b746e5SOllivier Robert 	}
92c0b746e5SOllivier Robert 	dtemp *= FRAC;
93c0b746e5SOllivier Robert 	now->l_uf = (u_int32)dtemp;
94c0b746e5SOllivier Robert }
95c0b746e5SOllivier Robert 
96c0b746e5SOllivier Robert 
97c0b746e5SOllivier Robert /*
989c2daa00SOllivier Robert  * adj_systime - adjust system time by the argument.
99c0b746e5SOllivier Robert  */
100a151a66cSOllivier Robert #if !defined SYS_WINNT
1019c2daa00SOllivier Robert int				/* 0 okay, 1 error */
102c0b746e5SOllivier Robert adj_systime(
1039c2daa00SOllivier Robert 	double now		/* adjustment (s) */
104c0b746e5SOllivier Robert 	)
105c0b746e5SOllivier Robert {
1069c2daa00SOllivier Robert 	struct timeval adjtv;	/* new adjustment */
1079c2daa00SOllivier Robert 	struct timeval oadjtv;	/* residual adjustment */
108c0b746e5SOllivier Robert 	double	dtemp;
1099c2daa00SOllivier Robert 	long	ticks;
1109c2daa00SOllivier Robert 	int	isneg = 0;
111c0b746e5SOllivier Robert 
112c0b746e5SOllivier Robert 	/*
1139c2daa00SOllivier Robert 	 * Most Unix adjtime() implementations adjust the system clock
1149c2daa00SOllivier Robert 	 * in microsecond quanta, but some adjust in 10-ms quanta. We
1159c2daa00SOllivier Robert 	 * carefully round the adjustment to the nearest quantum, then
1169c2daa00SOllivier Robert 	 * adjust in quanta and keep the residue for later.
117c0b746e5SOllivier Robert 	 */
1189c2daa00SOllivier Robert 	dtemp = now + sys_residual;
119c0b746e5SOllivier Robert 	if (dtemp < 0) {
120c0b746e5SOllivier Robert 		isneg = 1;
121c0b746e5SOllivier Robert 		dtemp = -dtemp;
122c0b746e5SOllivier Robert 	}
1239c2daa00SOllivier Robert 	adjtv.tv_sec = (long)dtemp;
1249c2daa00SOllivier Robert 	dtemp -= adjtv.tv_sec;
1259c2daa00SOllivier Robert 	ticks = (long)(dtemp / sys_tick + .5);
1269c2daa00SOllivier Robert 	adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
1279c2daa00SOllivier Robert 	dtemp -= adjtv.tv_usec / 1e6;
1289c2daa00SOllivier Robert 	sys_residual = dtemp;
129c0b746e5SOllivier Robert 
130c0b746e5SOllivier Robert 	/*
1319c2daa00SOllivier Robert 	 * Convert to signed seconds and microseconds for the Unix
1329c2daa00SOllivier Robert 	 * adjtime() system call. Note we purposely lose the adjtime()
1339c2daa00SOllivier Robert 	 * leftover.
134c0b746e5SOllivier Robert 	 */
1359c2daa00SOllivier Robert 	if (isneg) {
1369c2daa00SOllivier Robert 		adjtv.tv_sec = -adjtv.tv_sec;
1379c2daa00SOllivier Robert 		adjtv.tv_usec = -adjtv.tv_usec;
138c0b746e5SOllivier Robert 	}
1399c2daa00SOllivier Robert 	if (adjtime(&adjtv, &oadjtv) < 0) {
1409c2daa00SOllivier Robert 		msyslog(LOG_ERR, "adj_systime: %m");
1419c2daa00SOllivier Robert 		return (0);
142c0b746e5SOllivier Robert 	}
1439c2daa00SOllivier Robert 	return (1);
144c0b746e5SOllivier Robert }
145a151a66cSOllivier Robert #endif
146c0b746e5SOllivier Robert 
147c0b746e5SOllivier Robert 
148c0b746e5SOllivier Robert /*
149c0b746e5SOllivier Robert  * step_systime - step the system clock.
150c0b746e5SOllivier Robert  */
151c0b746e5SOllivier Robert int
152c0b746e5SOllivier Robert step_systime(
153c0b746e5SOllivier Robert 	double now
154c0b746e5SOllivier Robert 	)
155c0b746e5SOllivier Robert {
156c0b746e5SOllivier Robert 	struct timeval timetv, adjtv, oldtimetv;
157c0b746e5SOllivier Robert 	int isneg = 0;
158c0b746e5SOllivier Robert 	double dtemp;
159c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
160c0b746e5SOllivier Robert 	struct timespec ts;
161c0b746e5SOllivier Robert #endif
162c0b746e5SOllivier Robert 
163c0b746e5SOllivier Robert 	dtemp = sys_residual + now;
164c0b746e5SOllivier Robert 	if (dtemp < 0) {
165c0b746e5SOllivier Robert 		isneg = 1;
166c0b746e5SOllivier Robert 		dtemp = - dtemp;
167c0b746e5SOllivier Robert 		adjtv.tv_sec = (int32)dtemp;
1689c2daa00SOllivier Robert 		adjtv.tv_usec = (u_int32)((dtemp -
1699c2daa00SOllivier Robert 		    (double)adjtv.tv_sec) * 1e6 + .5);
170c0b746e5SOllivier Robert 	} else {
171c0b746e5SOllivier Robert 		adjtv.tv_sec = (int32)dtemp;
1729c2daa00SOllivier Robert 		adjtv.tv_usec = (u_int32)((dtemp -
1739c2daa00SOllivier Robert 		    (double)adjtv.tv_sec) * 1e6 + .5);
174c0b746e5SOllivier Robert 	}
175c0b746e5SOllivier Robert #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
176c0b746e5SOllivier Robert #ifdef HAVE_CLOCK_GETTIME
177c0b746e5SOllivier Robert 	(void) clock_gettime(CLOCK_REALTIME, &ts);
178c0b746e5SOllivier Robert #else
179c0b746e5SOllivier Robert 	(void) getclock(TIMEOFDAY, &ts);
180c0b746e5SOllivier Robert #endif
181c0b746e5SOllivier Robert 	timetv.tv_sec = ts.tv_sec;
182c0b746e5SOllivier Robert 	timetv.tv_usec = ts.tv_nsec / 1000;
183c0b746e5SOllivier Robert #else /*  not HAVE_GETCLOCK */
184c0b746e5SOllivier Robert 	(void) GETTIMEOFDAY(&timetv, (struct timezone *)0);
185c0b746e5SOllivier Robert #endif /* not HAVE_GETCLOCK */
186c0b746e5SOllivier Robert 
187c0b746e5SOllivier Robert 	oldtimetv = timetv;
188c0b746e5SOllivier Robert 
189c0b746e5SOllivier Robert #ifdef DEBUG
190c0b746e5SOllivier Robert 	if (debug)
191c0b746e5SOllivier Robert 		printf("step_systime: step %.6f residual %.6f\n", now, sys_residual);
192c0b746e5SOllivier Robert #endif
193c0b746e5SOllivier Robert 	if (isneg) {
194c0b746e5SOllivier Robert 		timetv.tv_sec -= adjtv.tv_sec;
195c0b746e5SOllivier Robert 		timetv.tv_usec -= adjtv.tv_usec;
196c0b746e5SOllivier Robert 		if (timetv.tv_usec < 0) {
197c0b746e5SOllivier Robert 			timetv.tv_sec--;
198c0b746e5SOllivier Robert 			timetv.tv_usec += 1000000;
199c0b746e5SOllivier Robert 		}
200c0b746e5SOllivier Robert 	} else {
201c0b746e5SOllivier Robert 		timetv.tv_sec += adjtv.tv_sec;
202c0b746e5SOllivier Robert 		timetv.tv_usec += adjtv.tv_usec;
203c0b746e5SOllivier Robert 		if (timetv.tv_usec >= 1000000) {
204c0b746e5SOllivier Robert 			timetv.tv_sec++;
205c0b746e5SOllivier Robert 			timetv.tv_usec -= 1000000;
206c0b746e5SOllivier Robert 		}
207c0b746e5SOllivier Robert 	}
2089c2daa00SOllivier Robert 	if (ntp_set_tod(&timetv, NULL) != 0) {
2099c2daa00SOllivier Robert 		msyslog(LOG_ERR, "step-systime: %m");
210c0b746e5SOllivier Robert 		return (0);
211c0b746e5SOllivier Robert 	}
212c0b746e5SOllivier Robert 	sys_residual = 0;
213c0b746e5SOllivier Robert 
214c0b746e5SOllivier Robert #ifdef NEED_HPUX_ADJTIME
215c0b746e5SOllivier Robert 	/*
216c0b746e5SOllivier Robert 	 * CHECKME: is this correct when called by ntpdate?????
217c0b746e5SOllivier Robert 	 */
218c0b746e5SOllivier Robert 	_clear_adjtime();
219c0b746e5SOllivier Robert #endif
220c0b746e5SOllivier Robert 
221c0b746e5SOllivier Robert 	/*
222c0b746e5SOllivier Robert 	 * FreeBSD, for example, has:
223c0b746e5SOllivier Robert 	 * struct utmp {
224c0b746e5SOllivier Robert 	 *	   char    ut_line[UT_LINESIZE];
225c0b746e5SOllivier Robert 	 *	   char    ut_name[UT_NAMESIZE];
226c0b746e5SOllivier Robert 	 *	   char    ut_host[UT_HOSTSIZE];
227c0b746e5SOllivier Robert 	 *	   long    ut_time;
228c0b746e5SOllivier Robert 	 * };
229c0b746e5SOllivier Robert 	 * and appends line="|", name="date", host="", time for the OLD
230c0b746e5SOllivier Robert 	 * and appends line="{", name="date", host="", time for the NEW
231c0b746e5SOllivier Robert 	 * to _PATH_WTMP .
232c0b746e5SOllivier Robert 	 *
233c0b746e5SOllivier Robert 	 * Some OSes have utmp, some have utmpx.
234c0b746e5SOllivier Robert 	 */
235c0b746e5SOllivier Robert 
236c0b746e5SOllivier Robert 	/*
2379c2daa00SOllivier Robert 	 * Write old and new time entries in utmp and wtmp if step
2389c2daa00SOllivier Robert 	 * adjustment is greater than one second.
239c0b746e5SOllivier Robert 	 *
240c0b746e5SOllivier Robert 	 * This might become even Uglier...
241c0b746e5SOllivier Robert 	 */
242c0b746e5SOllivier Robert 	if (oldtimetv.tv_sec != timetv.tv_sec)
243c0b746e5SOllivier Robert 	{
244c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H
245c0b746e5SOllivier Robert 		struct utmp ut;
246c0b746e5SOllivier Robert #endif
247c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H
248c0b746e5SOllivier Robert 		struct utmpx utx;
249c0b746e5SOllivier Robert #endif
250c0b746e5SOllivier Robert 
251c0b746e5SOllivier Robert #ifdef HAVE_UTMP_H
252c0b746e5SOllivier Robert 		memset((char *)&ut, 0, sizeof(ut));
253c0b746e5SOllivier Robert #endif
254c0b746e5SOllivier Robert #ifdef HAVE_UTMPX_H
255c0b746e5SOllivier Robert 		memset((char *)&utx, 0, sizeof(utx));
256c0b746e5SOllivier Robert #endif
257c0b746e5SOllivier Robert 
258c0b746e5SOllivier Robert 		/* UTMP */
259c0b746e5SOllivier Robert 
260c0b746e5SOllivier Robert #ifdef UPDATE_UTMP
261c0b746e5SOllivier Robert # ifdef HAVE_PUTUTLINE
262c0b746e5SOllivier Robert 		ut.ut_type = OLD_TIME;
263c0b746e5SOllivier Robert 		(void)strcpy(ut.ut_line, OTIME_MSG);
264c0b746e5SOllivier Robert 		ut.ut_time = oldtimetv.tv_sec;
265c0b746e5SOllivier Robert 		pututline(&ut);
266c0b746e5SOllivier Robert 		setutent();
267c0b746e5SOllivier Robert 		ut.ut_type = NEW_TIME;
268c0b746e5SOllivier Robert 		(void)strcpy(ut.ut_line, NTIME_MSG);
269c0b746e5SOllivier Robert 		ut.ut_time = timetv.tv_sec;
270c0b746e5SOllivier Robert 		pututline(&ut);
271c0b746e5SOllivier Robert 		endutent();
272c0b746e5SOllivier Robert # else /* not HAVE_PUTUTLINE */
273c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTLINE */
274c0b746e5SOllivier Robert #endif /* UPDATE_UTMP */
275c0b746e5SOllivier Robert 
276c0b746e5SOllivier Robert 		/* UTMPX */
277c0b746e5SOllivier Robert 
278c0b746e5SOllivier Robert #ifdef UPDATE_UTMPX
279c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE
280c0b746e5SOllivier Robert 		utx.ut_type = OLD_TIME;
281c0b746e5SOllivier Robert 		(void)strcpy(utx.ut_line, OTIME_MSG);
282c0b746e5SOllivier Robert 		utx.ut_tv = oldtimetv;
283c0b746e5SOllivier Robert 		pututxline(&utx);
284c0b746e5SOllivier Robert 		setutxent();
285c0b746e5SOllivier Robert 		utx.ut_type = NEW_TIME;
286c0b746e5SOllivier Robert 		(void)strcpy(utx.ut_line, NTIME_MSG);
287c0b746e5SOllivier Robert 		utx.ut_tv = timetv;
288c0b746e5SOllivier Robert 		pututxline(&utx);
289c0b746e5SOllivier Robert 		endutxent();
290c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */
291c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */
292c0b746e5SOllivier Robert #endif /* UPDATE_UTMPX */
293c0b746e5SOllivier Robert 
294c0b746e5SOllivier Robert 		/* WTMP */
295c0b746e5SOllivier Robert 
296c0b746e5SOllivier Robert #ifdef UPDATE_WTMP
297c0b746e5SOllivier Robert # ifdef HAVE_PUTUTLINE
298c0b746e5SOllivier Robert 		utmpname(WTMP_FILE);
299c0b746e5SOllivier Robert 		ut.ut_type = OLD_TIME;
300c0b746e5SOllivier Robert 		(void)strcpy(ut.ut_line, OTIME_MSG);
301c0b746e5SOllivier Robert 		ut.ut_time = oldtimetv.tv_sec;
302c0b746e5SOllivier Robert 		pututline(&ut);
303c0b746e5SOllivier Robert 		ut.ut_type = NEW_TIME;
304c0b746e5SOllivier Robert 		(void)strcpy(ut.ut_line, NTIME_MSG);
305c0b746e5SOllivier Robert 		ut.ut_time = timetv.tv_sec;
306c0b746e5SOllivier Robert 		pututline(&ut);
307c0b746e5SOllivier Robert 		endutent();
308c0b746e5SOllivier Robert # else /* not HAVE_PUTUTLINE */
309c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTLINE */
310c0b746e5SOllivier Robert #endif /* UPDATE_WTMP */
311c0b746e5SOllivier Robert 
312c0b746e5SOllivier Robert 		/* WTMPX */
313c0b746e5SOllivier Robert 
314c0b746e5SOllivier Robert #ifdef UPDATE_WTMPX
315c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE
316c0b746e5SOllivier Robert 		utx.ut_type = OLD_TIME;
317c0b746e5SOllivier Robert 		utx.ut_tv = oldtimetv;
318c0b746e5SOllivier Robert 		(void)strcpy(utx.ut_line, OTIME_MSG);
319c0b746e5SOllivier Robert #  ifdef HAVE_UPDWTMPX
320c0b746e5SOllivier Robert 		updwtmpx(WTMPX_FILE, &utx);
321c0b746e5SOllivier Robert #  else /* not HAVE_UPDWTMPX */
322c0b746e5SOllivier Robert #  endif /* not HAVE_UPDWTMPX */
323c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */
324c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */
325c0b746e5SOllivier Robert # ifdef HAVE_PUTUTXLINE
326c0b746e5SOllivier Robert 		utx.ut_type = NEW_TIME;
327c0b746e5SOllivier Robert 		utx.ut_tv = timetv;
328c0b746e5SOllivier Robert 		(void)strcpy(utx.ut_line, NTIME_MSG);
329c0b746e5SOllivier Robert #  ifdef HAVE_UPDWTMPX
330c0b746e5SOllivier Robert 		updwtmpx(WTMPX_FILE, &utx);
331c0b746e5SOllivier Robert #  else /* not HAVE_UPDWTMPX */
332c0b746e5SOllivier Robert #  endif /* not HAVE_UPDWTMPX */
333c0b746e5SOllivier Robert # else /* not HAVE_PUTUTXLINE */
334c0b746e5SOllivier Robert # endif /* not HAVE_PUTUTXLINE */
335c0b746e5SOllivier Robert #endif /* UPDATE_WTMPX */
336c0b746e5SOllivier Robert 
337c0b746e5SOllivier Robert 	}
338c0b746e5SOllivier Robert 	return (1);
339c0b746e5SOllivier Robert }
3409c2daa00SOllivier Robert 
3419c2daa00SOllivier Robert #else /* SIM */
3429c2daa00SOllivier Robert /*
3439c2daa00SOllivier Robert  * Clock routines for the simulator - Harish Nair, with help
3449c2daa00SOllivier Robert  */
3459c2daa00SOllivier Robert /*
3469c2daa00SOllivier Robert  * get_systime - return the system time in NTP timestamp format
3479c2daa00SOllivier Robert  */
3489c2daa00SOllivier Robert void
3499c2daa00SOllivier Robert get_systime(
3509c2daa00SOllivier Robert         l_fp *now		/* current system time in l_fp */        )
3519c2daa00SOllivier Robert {
3529c2daa00SOllivier Robert 	/*
3539c2daa00SOllivier Robert 	 * To fool the code that determines the local clock precision,
3549c2daa00SOllivier Robert 	 * we advance the clock a minimum of 200 nanoseconds on every
3559c2daa00SOllivier Robert 	 * clock read. This is appropriate for a typical modern machine
3569c2daa00SOllivier Robert 	 * with nanosecond clocks. Note we make no attempt here to
3579c2daa00SOllivier Robert 	 * simulate reading error, since the error is so small. This may
3589c2daa00SOllivier Robert 	 * change when the need comes to implement picosecond clocks.
3599c2daa00SOllivier Robert 	 */
3609c2daa00SOllivier Robert 	if (ntp_node.ntp_time == ntp_node.last_time)
3619c2daa00SOllivier Robert 		ntp_node.ntp_time += 200e-9;
3629c2daa00SOllivier Robert 	ntp_node.last_time = ntp_node.ntp_time;
3639c2daa00SOllivier Robert 	DTOLFP(ntp_node.ntp_time, now);
3649c2daa00SOllivier Robert }
3659c2daa00SOllivier Robert 
3669c2daa00SOllivier Robert 
3679c2daa00SOllivier Robert /*
3689c2daa00SOllivier Robert  * adj_systime - advance or retard the system clock exactly like the
3699c2daa00SOllivier Robert  * real thng.
3709c2daa00SOllivier Robert  */
3719c2daa00SOllivier Robert int				/* always succeeds */
3729c2daa00SOllivier Robert adj_systime(
3739c2daa00SOllivier Robert         double now		/* time adjustment (s) */
3749c2daa00SOllivier Robert         )
3759c2daa00SOllivier Robert {
3769c2daa00SOllivier Robert 	struct timeval adjtv;	/* new adjustment */
3779c2daa00SOllivier Robert 	double	dtemp;
3789c2daa00SOllivier Robert 	long	ticks;
3799c2daa00SOllivier Robert 	int	isneg = 0;
3809c2daa00SOllivier Robert 
3819c2daa00SOllivier Robert 	/*
3829c2daa00SOllivier Robert 	 * Most Unix adjtime() implementations adjust the system clock
3839c2daa00SOllivier Robert 	 * in microsecond quanta, but some adjust in 10-ms quanta. We
3849c2daa00SOllivier Robert 	 * carefully round the adjustment to the nearest quantum, then
3859c2daa00SOllivier Robert 	 * adjust in quanta and keep the residue for later.
3869c2daa00SOllivier Robert 	 */
3879c2daa00SOllivier Robert 	dtemp = now + sys_residual;
3889c2daa00SOllivier Robert 	if (dtemp < 0) {
3899c2daa00SOllivier Robert 		isneg = 1;
3909c2daa00SOllivier Robert 		dtemp = -dtemp;
3919c2daa00SOllivier Robert 	}
3929c2daa00SOllivier Robert 	adjtv.tv_sec = (long)dtemp;
3939c2daa00SOllivier Robert 	dtemp -= adjtv.tv_sec;
3949c2daa00SOllivier Robert 	ticks = (long)(dtemp / sys_tick + .5);
3959c2daa00SOllivier Robert 	adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
3969c2daa00SOllivier Robert 	dtemp -= adjtv.tv_usec / 1e6;
3979c2daa00SOllivier Robert 	sys_residual = dtemp;
3989c2daa00SOllivier Robert 
3999c2daa00SOllivier Robert 	/*
4009c2daa00SOllivier Robert 	 * Convert to signed seconds and microseconds for the Unix
4019c2daa00SOllivier Robert 	 * adjtime() system call. Note we purposely lose the adjtime()
4029c2daa00SOllivier Robert 	 * leftover.
4039c2daa00SOllivier Robert 	 */
4049c2daa00SOllivier Robert 	if (isneg) {
4059c2daa00SOllivier Robert 		adjtv.tv_sec = -adjtv.tv_sec;
4069c2daa00SOllivier Robert 		adjtv.tv_usec = -adjtv.tv_usec;
4079c2daa00SOllivier Robert 		sys_residual = -sys_residual;
4089c2daa00SOllivier Robert 	}
4099c2daa00SOllivier Robert 
4109c2daa00SOllivier Robert 	/*
4119c2daa00SOllivier Robert 	 * We went to all the trouble just to be sure the emulation is
4129c2daa00SOllivier Robert 	 * precise. We now return to our regularly scheduled concert.
4139c2daa00SOllivier Robert 	 */
4149c2daa00SOllivier Robert 	ntp_node.clk_time -= adjtv.tv_sec + adjtv.tv_usec / 1e6;
4159c2daa00SOllivier Robert         return (1);
4169c2daa00SOllivier Robert }
4179c2daa00SOllivier Robert 
4189c2daa00SOllivier Robert 
4199c2daa00SOllivier Robert /*
4209c2daa00SOllivier Robert  * step_systime - step the system clock. We are religious here.
4219c2daa00SOllivier Robert  */
4229c2daa00SOllivier Robert int				/* always succeeds */
4239c2daa00SOllivier Robert step_systime(
4249c2daa00SOllivier Robert         double now		/* step adjustment (s) */
4259c2daa00SOllivier Robert         )
4269c2daa00SOllivier Robert {
4279c2daa00SOllivier Robert 	ntp_node.adj = now;
4289c2daa00SOllivier Robert 	return (1);
4299c2daa00SOllivier Robert }
4309c2daa00SOllivier Robert 
4319c2daa00SOllivier Robert /*
4329c2daa00SOllivier Robert  * node_clock - update the clocks
4339c2daa00SOllivier Robert  */
4349c2daa00SOllivier Robert int				/* always succeeds */
4359c2daa00SOllivier Robert node_clock(
4369c2daa00SOllivier Robert 	Node *n,		/* global node pointer */
4379c2daa00SOllivier Robert 	double t		/* node time */
4389c2daa00SOllivier Robert 	)
4399c2daa00SOllivier Robert {
4409c2daa00SOllivier Robert 	double	dtemp;
4419c2daa00SOllivier Robert 
4429c2daa00SOllivier Robert 	/*
4439c2daa00SOllivier Robert 	 * Advance client clock (ntp_time). Advance server clock
4449c2daa00SOllivier Robert 	 * (clk_time) adjusted for systematic and random frequency
4459c2daa00SOllivier Robert 	 * errors. The random error is a random walk computed as the
4469c2daa00SOllivier Robert 	 * integral of samples from a Gaussian distribution.
4479c2daa00SOllivier Robert 	 */
4489c2daa00SOllivier Robert 	dtemp = t - n->ntp_time;
4499c2daa00SOllivier Robert 	n->time = t;
4509c2daa00SOllivier Robert 	n->ntp_time += dtemp;
4519c2daa00SOllivier Robert 	n->ferr += gauss(0, dtemp * n->fnse);
4529c2daa00SOllivier Robert 	n->clk_time += dtemp * (1 + n->ferr);
4539c2daa00SOllivier Robert 
4549c2daa00SOllivier Robert 	/*
4559c2daa00SOllivier Robert 	 * Perform the adjtime() function. If the adjustment completed
4569c2daa00SOllivier Robert 	 * in the previous interval, amortize the entire amount; if not,
4579c2daa00SOllivier Robert 	 * carry the leftover to the next interval.
4589c2daa00SOllivier Robert 	 */
4599c2daa00SOllivier Robert 	dtemp *= n->slew;
4609c2daa00SOllivier Robert 	if (dtemp < fabs(n->adj)) {
4619c2daa00SOllivier Robert 		if (n->adj < 0) {
4629c2daa00SOllivier Robert 			n->adj += dtemp;
4639c2daa00SOllivier Robert 			n->ntp_time -= dtemp;
4649c2daa00SOllivier Robert 		} else {
4659c2daa00SOllivier Robert 			n->adj -= dtemp;
4669c2daa00SOllivier Robert 			n->ntp_time += dtemp;
4679c2daa00SOllivier Robert 		}
4689c2daa00SOllivier Robert 	} else {
4699c2daa00SOllivier Robert 		n->ntp_time += n->adj;
4709c2daa00SOllivier Robert 		n->adj = 0;
4719c2daa00SOllivier Robert 	}
4729c2daa00SOllivier Robert         return (0);
4739c2daa00SOllivier Robert }
4749c2daa00SOllivier Robert 
4759c2daa00SOllivier Robert 
4769c2daa00SOllivier Robert /*
4779c2daa00SOllivier Robert  * gauss() - returns samples from a gaussion distribution
4789c2daa00SOllivier Robert  */
4799c2daa00SOllivier Robert double				/* Gaussian sample */
4809c2daa00SOllivier Robert gauss(
4819c2daa00SOllivier Robert 	double m,		/* sample mean */
4829c2daa00SOllivier Robert 	double s		/* sample standard deviation (sigma) */
4839c2daa00SOllivier Robert 	)
4849c2daa00SOllivier Robert {
4859c2daa00SOllivier Robert         double q1, q2;
4869c2daa00SOllivier Robert 
4879c2daa00SOllivier Robert 	/*
4889c2daa00SOllivier Robert 	 * Roll a sample from a Gaussian distribution with mean m and
4899c2daa00SOllivier Robert 	 * standard deviation s. For m = 0, s = 1, mean(y) = 0,
4909c2daa00SOllivier Robert 	 * std(y) = 1.
4919c2daa00SOllivier Robert 	 */
4929c2daa00SOllivier Robert 	if (s == 0)
4939c2daa00SOllivier Robert 		return (m);
4949c2daa00SOllivier Robert         while ((q1 = drand48()) == 0);
4959c2daa00SOllivier Robert         q2 = drand48();
4969c2daa00SOllivier Robert         return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
4979c2daa00SOllivier Robert }
4989c2daa00SOllivier Robert 
4999c2daa00SOllivier Robert 
5009c2daa00SOllivier Robert /*
5019c2daa00SOllivier Robert  * poisson() - returns samples from a network delay distribution
5029c2daa00SOllivier Robert  */
5039c2daa00SOllivier Robert double				/* delay sample (s) */
5049c2daa00SOllivier Robert poisson(
5059c2daa00SOllivier Robert 	double m,		/* fixed propagation delay (s) */
5069c2daa00SOllivier Robert 	double s		/* exponential parameter (mu) */
5079c2daa00SOllivier Robert 	)
5089c2daa00SOllivier Robert {
5099c2daa00SOllivier Robert         double q1;
5109c2daa00SOllivier Robert 
5119c2daa00SOllivier Robert 	/*
5129c2daa00SOllivier Robert 	 * Roll a sample from a composite distribution with propagation
5139c2daa00SOllivier Robert 	 * delay m and exponential distribution time with parameter s.
5149c2daa00SOllivier Robert 	 * For m = 0, s = 1, mean(y) = std(y) = 1.
5159c2daa00SOllivier Robert 	 */
5169c2daa00SOllivier Robert 	if (s == 0)
5179c2daa00SOllivier Robert 		return (m);
5189c2daa00SOllivier Robert         while ((q1 = drand48()) == 0);
5199c2daa00SOllivier Robert         return (m - s * log(q1 * s));
5209c2daa00SOllivier Robert }
5219c2daa00SOllivier Robert #endif /* SIM */
522