Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / libntp / systime.c
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 }