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