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