xref: /freebsd/contrib/ntp/libntp/systime.c (revision b52b9d56d4e96089873a75f9e29062eec19fabba)
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 
22 int	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  */
30 double 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  */
37 void
38 get_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
99 int
100 adj_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  */
172 int
173 step_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