systime.c revision 56746
1/*
2 * systime -- routines to fiddle a UNIX clock.
3 */
4
5#ifdef HAVE_CONFIG_H
6# include <config.h>
7#endif
8
9#include <stdio.h>
10#include <sys/types.h>
11#include <sys/time.h>
12#ifdef HAVE_SYS_PARAM_H
13# include <sys/param.h>
14#endif
15#ifdef HAVE_UTMP_H
16# include <utmp.h>
17#endif /* HAVE_UTMP_H */
18#ifdef HAVE_UTMPX_H
19# include <utmpx.h>
20#endif /* HAVE_UTMPX_H */
21
22#include "ntp_machine.h"
23#include "ntp_fp.h"
24#include "ntp_syslog.h"
25#include "ntp_unixtime.h"
26#include "ntp_stdlib.h"
27
28int	systime_10ms_ticks = 0;	/* adj sysclock in 10ms increments */
29
30#define MAXFREQ 500e-6
31
32/*
33 * These routines (init_systime, get_systime, step_systime, adj_systime)
34 * implement an interface between the (more or less) system independent
35 * bits of NTP and the peculiarities of dealing with the Unix system
36 * clock.
37 */
38double sys_residual = 0;	/* residual from previous adjustment */
39double sys_maxfreq = MAXFREQ;	/* max frequency correction */
40
41
42/*
43 * get_systime - return the system time in timestamp format biased by
44 * the current time offset.
45 */
46void
47get_systime(
48	l_fp *now
49	)
50{
51#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
52	struct timespec ts;
53#else
54	struct timeval tv;
55#endif
56	double dtemp;
57
58	/*
59	 * We use nanosecond time if we can get it. Watch out for
60	 * rounding wiggles, which may overflow the fraction.
61	 */
62#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
63# ifdef HAVE_CLOCK_GETTIME
64	(void) clock_gettime(CLOCK_REALTIME, &ts);
65# else
66	(void) getclock(TIMEOFDAY, &ts);
67# endif
68	now->l_i = ts.tv_sec + JAN_1970;
69	dtemp = ts.tv_nsec * FRAC / 1e9;
70	if (dtemp >= FRAC)
71		now->l_i++;
72	now->l_uf = (u_int32)dtemp;
73#else /* HAVE_CLOCK_GETTIME */
74	(void) GETTIMEOFDAY(&tv, (struct timezone *)0);
75	now->l_i = tv.tv_sec + JAN_1970;
76
77#if defined RELIANTUNIX_CLOCK || defined SCO5_CLOCK
78	if (systime_10ms_ticks) {
79		/* fake better than 10ms resolution by interpolating
80	   	accumulated residual (in adj_systime(), see below) */
81		dtemp = tv.tv_usec / 1e6;
82		if (sys_residual < 5000e-6 && sys_residual > -5000e-6) {
83			dtemp += sys_residual;
84			if (dtemp < 0) {
85				now->l_i--;
86				dtemp++;
87			}
88		}
89		dtemp *= FRAC;
90	} else
91#endif
92
93	dtemp = tv.tv_usec * FRAC / 1e6;
94
95	if (dtemp >= FRAC)
96		now->l_i++;
97	now->l_uf = (u_int32)dtemp;
98#endif /* HAVE_CLOCK_GETTIME */
99
100}
101
102
103/*
104 * adj_systime - called once every second to make system time adjustments.
105 * Returns 1 if okay, 0 if trouble.
106 */
107#if !defined SYS_WINNT
108int
109adj_systime(
110	double now
111	)
112{
113	double dtemp;
114	struct timeval adjtv;
115	u_char isneg = 0;
116	struct timeval oadjtv;
117
118	/*
119	 * Add the residual from the previous adjustment to the new
120	 * adjustment, bound and round.
121	 */
122	dtemp = sys_residual + now;
123	sys_residual = 0;
124	if (dtemp < 0) {
125		isneg = 1;
126		dtemp = -dtemp;
127	}
128
129#if defined RELIANTUNIX_CLOCK || defined SCO5_CLOCK
130	if (systime_10ms_ticks) {
131		/* accumulate changes until we have enough to adjust a tick */
132		if (dtemp < 5000e-6) {
133			if (isneg) sys_residual = -dtemp;
134			else sys_residual = dtemp;
135			dtemp = 0;
136		} else {
137			if (isneg) sys_residual = 10000e-6 - dtemp;
138			else sys_residual = dtemp - 10000e-6;
139			dtemp = 10000e-6;
140		}
141	} else
142#endif
143		if (dtemp > sys_maxfreq)
144			dtemp = sys_maxfreq;
145
146	dtemp = dtemp * 1e6 + .5;
147
148	if (isneg)
149		dtemp = -dtemp;
150	adjtv.tv_sec = 0;
151	adjtv.tv_usec = (int32)dtemp;
152
153	/*
154	 * Here we do the actual adjustment. If for some reason the adjtime()
155	 * call fails, like it is not implemented or something like that,
156	 * we honk to the log. If the previous adjustment did not complete,
157	 * we correct the residual offset.
158	 */
159	/* casey - we need a posix type thang here */
160	if (adjtime(&adjtv, &oadjtv) < 0)
161	{
162		msyslog(LOG_ERR, "Can't adjust time: %m");
163		return 0;
164	}
165	else {
166	sys_residual += oadjtv.tv_usec / 1e6;
167	}
168#ifdef DEBUG
169	if (debug > 6)
170		printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual);
171#endif
172	return 1;
173}
174#endif
175
176
177/*
178 * step_systime - step the system clock.
179 */
180int
181step_systime(
182	double now
183	)
184{
185	struct timeval timetv, adjtv, oldtimetv;
186	int isneg = 0;
187	double dtemp;
188#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
189	struct timespec ts;
190#endif
191
192	dtemp = sys_residual + now;
193	if (dtemp < 0) {
194		isneg = 1;
195		dtemp = - dtemp;
196		adjtv.tv_sec = (int32)dtemp;
197		adjtv.tv_usec = (u_int32)((dtemp - (double)adjtv.tv_sec) *
198					  1e6 + .5);
199	} else {
200		adjtv.tv_sec = (int32)dtemp;
201		adjtv.tv_usec = (u_int32)((dtemp - (double)adjtv.tv_sec) *
202					  1e6 + .5);
203	}
204#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
205#ifdef HAVE_CLOCK_GETTIME
206	(void) clock_gettime(CLOCK_REALTIME, &ts);
207#else
208	(void) getclock(TIMEOFDAY, &ts);
209#endif
210	timetv.tv_sec = ts.tv_sec;
211	timetv.tv_usec = ts.tv_nsec / 1000;
212#else /*  not HAVE_GETCLOCK */
213	(void) GETTIMEOFDAY(&timetv, (struct timezone *)0);
214#endif /* not HAVE_GETCLOCK */
215
216	oldtimetv = timetv;
217
218#ifdef DEBUG
219	if (debug)
220		printf("step_systime: step %.6f residual %.6f\n", now, sys_residual);
221#endif
222	if (isneg) {
223		timetv.tv_sec -= adjtv.tv_sec;
224		timetv.tv_usec -= adjtv.tv_usec;
225		if (timetv.tv_usec < 0) {
226			timetv.tv_sec--;
227			timetv.tv_usec += 1000000;
228		}
229	} else {
230		timetv.tv_sec += adjtv.tv_sec;
231		timetv.tv_usec += adjtv.tv_usec;
232		if (timetv.tv_usec >= 1000000) {
233			timetv.tv_sec++;
234			timetv.tv_usec -= 1000000;
235		}
236	}
237	if (ntp_set_tod(&timetv, (struct timezone *)0) != 0) {
238		msyslog(LOG_ERR, "Can't set time of day: %m");
239		return (0);
240	}
241	sys_residual = 0;
242
243#ifdef NEED_HPUX_ADJTIME
244	/*
245	 * CHECKME: is this correct when called by ntpdate?????
246	 */
247	_clear_adjtime();
248#endif
249
250	/*
251	 * FreeBSD, for example, has:
252	 * struct utmp {
253	 *	   char    ut_line[UT_LINESIZE];
254	 *	   char    ut_name[UT_NAMESIZE];
255	 *	   char    ut_host[UT_HOSTSIZE];
256	 *	   long    ut_time;
257	 * };
258	 * and appends line="|", name="date", host="", time for the OLD
259	 * and appends line="{", name="date", host="", time for the NEW
260	 * to _PATH_WTMP .
261	 *
262	 * Some OSes have utmp, some have utmpx.
263	 */
264
265	/*
266	 * Write old and new time entries in utmp and wtmp if step adjustment
267	 * is greater than one second.
268	 *
269	 * This might become even Uglier...
270	 */
271	if (oldtimetv.tv_sec != timetv.tv_sec)
272	{
273#ifdef HAVE_UTMP_H
274		struct utmp ut;
275#endif
276#ifdef HAVE_UTMPX_H
277		struct utmpx utx;
278#endif
279
280#ifdef HAVE_UTMP_H
281		memset((char *)&ut, 0, sizeof(ut));
282#endif
283#ifdef HAVE_UTMPX_H
284		memset((char *)&utx, 0, sizeof(utx));
285#endif
286
287		/* UTMP */
288
289#ifdef UPDATE_UTMP
290# ifdef HAVE_PUTUTLINE
291		ut.ut_type = OLD_TIME;
292		(void)strcpy(ut.ut_line, OTIME_MSG);
293		ut.ut_time = oldtimetv.tv_sec;
294		pututline(&ut);
295		setutent();
296		ut.ut_type = NEW_TIME;
297		(void)strcpy(ut.ut_line, NTIME_MSG);
298		ut.ut_time = timetv.tv_sec;
299		pututline(&ut);
300		endutent();
301# else /* not HAVE_PUTUTLINE */
302# endif /* not HAVE_PUTUTLINE */
303#endif /* UPDATE_UTMP */
304
305		/* UTMPX */
306
307#ifdef UPDATE_UTMPX
308# ifdef HAVE_PUTUTXLINE
309		utx.ut_type = OLD_TIME;
310		(void)strcpy(utx.ut_line, OTIME_MSG);
311		utx.ut_tv = oldtimetv;
312		pututxline(&utx);
313		setutxent();
314		utx.ut_type = NEW_TIME;
315		(void)strcpy(utx.ut_line, NTIME_MSG);
316		utx.ut_tv = timetv;
317		pututxline(&utx);
318		endutxent();
319# else /* not HAVE_PUTUTXLINE */
320# endif /* not HAVE_PUTUTXLINE */
321#endif /* UPDATE_UTMPX */
322
323		/* WTMP */
324
325#ifdef UPDATE_WTMP
326# ifdef HAVE_PUTUTLINE
327		utmpname(WTMP_FILE);
328		ut.ut_type = OLD_TIME;
329		(void)strcpy(ut.ut_line, OTIME_MSG);
330		ut.ut_time = oldtimetv.tv_sec;
331		pututline(&ut);
332		ut.ut_type = NEW_TIME;
333		(void)strcpy(ut.ut_line, NTIME_MSG);
334		ut.ut_time = timetv.tv_sec;
335		pututline(&ut);
336		endutent();
337# else /* not HAVE_PUTUTLINE */
338# endif /* not HAVE_PUTUTLINE */
339#endif /* UPDATE_WTMP */
340
341		/* WTMPX */
342
343#ifdef UPDATE_WTMPX
344# ifdef HAVE_PUTUTXLINE
345		utx.ut_type = OLD_TIME;
346		utx.ut_tv = oldtimetv;
347		(void)strcpy(utx.ut_line, OTIME_MSG);
348#  ifdef HAVE_UPDWTMPX
349		updwtmpx(WTMPX_FILE, &utx);
350#  else /* not HAVE_UPDWTMPX */
351#  endif /* not HAVE_UPDWTMPX */
352# else /* not HAVE_PUTUTXLINE */
353# endif /* not HAVE_PUTUTXLINE */
354# ifdef HAVE_PUTUTXLINE
355		utx.ut_type = NEW_TIME;
356		utx.ut_tv = timetv;
357		(void)strcpy(utx.ut_line, NTIME_MSG);
358#  ifdef HAVE_UPDWTMPX
359		updwtmpx(WTMPX_FILE, &utx);
360#  else /* not HAVE_UPDWTMPX */
361#  endif /* not HAVE_UPDWTMPX */
362# else /* not HAVE_PUTUTXLINE */
363# endif /* not HAVE_PUTUTXLINE */
364#endif /* UPDATE_WTMPX */
365
366	}
367	return (1);
368}
369