xref: /freebsd/contrib/ntp/libntp/systime.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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 int	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  */
38 double sys_residual = 0;	/* residual from previous adjustment */
39 double 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  */
46 void
47 get_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
108 int
109 adj_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  */
180 int
181 step_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