]> git.ipfire.org Git - thirdparty/chrony.git/blame - local.c
Limit timeout for end of slew to avoid overflow
[thirdparty/chrony.git] / local.c
CommitLineData
88840341 1/*
6672f045 2 $Header: /cvs/src/chrony/local.c,v 1.21 2003/09/22 21:22:30 richard Exp $
88840341
RC
3
4 =======================================================================
5
6 chronyd/chronyc - Programs for keeping computer clocks accurate.
7
8 **********************************************************************
6672f045 9 * Copyright (C) Richard P. Curnow 1997-2003
88840341
RC
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
23 *
24 **********************************************************************
25
26 =======================================================================
27
28 The routines in this file present a common local (system) clock
29 interface to the rest of the software.
30
31 They interface with the system specific driver files in sys_*.c
32 */
33
34#include <assert.h>
35#include <stddef.h>
36
37#include "local.h"
38#include "localp.h"
39#include "memory.h"
40#include "util.h"
41#include "logging.h"
42
43/* ================================================== */
44
45/* Variable to store the current frequency, in ppm */
46static double current_freq_ppm;
47
48/* ================================================== */
49/* Store the system dependent drivers */
50
51static lcl_ReadFrequencyDriver drv_read_freq;
52static lcl_SetFrequencyDriver drv_set_freq;
53static lcl_AccrueOffsetDriver drv_accrue_offset;
54static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
55static lcl_OffsetCorrectionDriver drv_offset_convert;
56static lcl_ImmediateStepDriver drv_immediate_step;
8f9c2370 57static lcl_SetLeapDriver drv_set_leap;
88840341
RC
58
59/* ================================================== */
60
61/* Types and variables associated with handling the parameter change
62 list */
63
64typedef struct _ChangeListEntry {
65 struct _ChangeListEntry *next;
66 struct _ChangeListEntry *prev;
67 LCL_ParameterChangeHandler handler;
68 void *anything;
69} ChangeListEntry;
70
71static ChangeListEntry change_list;
72
73/* ================================================== */
74
75/* Types and variables associated with handling the parameter change
76 list */
77
78typedef struct _DispersionNotifyListEntry {
79 struct _DispersionNotifyListEntry *next;
80 struct _DispersionNotifyListEntry *prev;
81 LCL_DispersionNotifyHandler handler;
82 void *anything;
83} DispersionNotifyListEntry;
84
85static DispersionNotifyListEntry dispersion_notify_list;
86
87/* ================================================== */
88
89static int precision_log;
90static double precision_quantum;
91
92/* ================================================== */
93
94/* Define the number of increments of the system clock that we want
95 to see to be fairly sure that we've got something approaching
96 the minimum increment. Even on a crummy implementation that can't
97 interpolate between 10ms ticks, we should get this done in
98 under 1s of busy waiting. */
99#define NITERS 100
100
101static void
102calculate_sys_precision(void)
103{
104 struct timeval tv, old_tv, first_tv;
105 struct timezone tz;
106 int dusec, best_dusec;
107 int iters;
108
109 gettimeofday(&old_tv, &tz);
110 first_tv = old_tv;
111 best_dusec = 1000000; /* Assume we must be better than a second */
112 iters = 0;
113 do {
114 gettimeofday(&tv, &tz);
115 dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
116 old_tv = tv;
117 if (dusec > 0) {
118 if (dusec < best_dusec) {
119 best_dusec = dusec;
120 }
121 iters++;
122 }
123 } while (iters < NITERS);
124 if (!(best_dusec > 0)) {
125 CROAK("best_dusec should be positive");
126 }
127 precision_log = 0;
128 while (best_dusec < 500000) {
129 precision_log--;
130 best_dusec *= 2;
131 }
132
133 precision_quantum = 1.0 / (double)(1<<(-precision_log));
134
135 return;
136}
137
138/* ================================================== */
139
140void
141LCL_Initialise(void)
142{
143 change_list.next = change_list.prev = &change_list;
144
145 dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
146
147 /* Null out the system drivers, so that we die
148 if they never get defined before use */
149
150 drv_read_freq = NULL;
151 drv_set_freq = NULL;
152 drv_accrue_offset = NULL;
153 drv_offset_convert = NULL;
154
155 /* This ought to be set from the system driver layer */
156 current_freq_ppm = 0.0;
157
158 calculate_sys_precision();
159}
160
161/* ================================================== */
162
163void
164LCL_Finalise(void)
165{
166 return;
167}
168
169/* ================================================== */
170
171/* Routine to read the system precision as a log to base 2 value. */
172int
173LCL_GetSysPrecisionAsLog(void)
174{
175 return precision_log;
176}
177
178/* ================================================== */
179/* Routine to read the system precision in terms of the actual time step */
180
181double
182LCL_GetSysPrecisionAsQuantum(void)
183{
184 return precision_quantum;
185}
186
187/* ================================================== */
188
189void
190LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
191{
192 ChangeListEntry *ptr, *new_entry;
193
194 /* Check that the handler is not already registered */
195 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
196 if (!(ptr->handler != handler || ptr->anything != anything)) {
197 CROAK("a handler is already registered");
198 }
199 }
200
201 new_entry = MallocNew(ChangeListEntry);
202
203 new_entry->handler = handler;
204 new_entry->anything = anything;
205
206 /* Chain it into the list */
207 new_entry->next = &change_list;
208 new_entry->prev = change_list.prev;
209 change_list.prev->next = new_entry;
210 change_list.prev = new_entry;
211
212 return;
213}
214
215/* ================================================== */
216
217/* Remove a handler */
218extern
219void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
220{
221
222 ChangeListEntry *ptr;
223 int ok;
224
225 ptr = NULL;
226 ok = 0;
227
228 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
229 if (ptr->handler == handler && ptr->anything == anything) {
230 ok = 1;
231 break;
232 }
233 }
234
235 if (!ok) {
236 CROAK("did not find a matching handler");
237 }
238
239 /* Unlink entry from the list */
240 ptr->next->prev = ptr->prev;
241 ptr->prev->next = ptr->next;
242
243 free(ptr);
244
245 return;
246}
247
248/* ================================================== */
249
250void
251LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
252{
253 DispersionNotifyListEntry *ptr, *new_entry;
254
255 /* Check that the handler is not already registered */
256 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
257 if (!(ptr->handler != handler || ptr->anything != anything)) {
258 CROAK("a handler is already registered");
259 }
260 }
261
262 new_entry = MallocNew(DispersionNotifyListEntry);
263
264 new_entry->handler = handler;
265 new_entry->anything = anything;
266
267 /* Chain it into the list */
268 new_entry->next = &dispersion_notify_list;
269 new_entry->prev = dispersion_notify_list.prev;
270 dispersion_notify_list.prev->next = new_entry;
271 dispersion_notify_list.prev = new_entry;
272
273 return;
274}
275
276/* ================================================== */
277
278/* Remove a handler */
279extern
280void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
281{
282
283 DispersionNotifyListEntry *ptr;
284 int ok;
285
286 ptr = NULL;
287 ok = 0;
288
289 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
290 if (ptr->handler == handler && ptr->anything == anything) {
291 ok = 1;
292 break;
293 }
294 }
295
296 if (!ok) {
297 CROAK("no matching handler found");
298 }
299
300 /* Unlink entry from the list */
301 ptr->next->prev = ptr->prev;
302 ptr->prev->next = ptr->next;
303
304 free(ptr);
305
306 return;
307}
308
309/* ================================================== */
310/* At the moment, this is just gettimeofday(), because
311 I can't think of a Unix system where it would not be */
312
313void
314LCL_ReadRawTime(struct timeval *result)
315{
316 struct timezone tz;
317
318 if (!(gettimeofday(result, &tz) >= 0)) {
319 CROAK("Could not get time of day");
320 }
321 return;
322
323}
324
325/* ================================================== */
326
327void
328LCL_ReadCookedTime(struct timeval *result, double *err)
329{
330 struct timeval raw;
331 double correction;
332
333 LCL_ReadRawTime(&raw);
334
335 /* For now, cheat and set the error to zero in all cases.
336 */
337
338 *err = 0.0;
339
340 /* Call system specific driver to get correction */
341 (*drv_offset_convert)(&raw, &correction);
342 UTI_AddDoubleToTimeval(&raw, correction, result);
343
344 return;
345}
346
347/* ================================================== */
348
349double
350LCL_GetOffsetCorrection(struct timeval *raw)
351{
352 double correction;
353 (*drv_offset_convert)(raw, &correction);
354 return correction;
355}
356
357/* ================================================== */
358/* This is just a simple passthrough of the system specific routine */
359
360double
361LCL_ReadAbsoluteFrequency(void)
362{
363 return (*drv_read_freq)();
364}
365
366/* ================================================== */
367/* This involves both setting the absolute frequency with the
368 system-specific driver, as well as calling all notify handlers */
369
370void
371LCL_SetAbsoluteFrequency(double afreq_ppm)
372{
373 ChangeListEntry *ptr;
374 struct timeval raw, cooked;
375 double correction;
376 double dfreq;
377
378 /* Call the system-specific driver for setting the frequency */
379
380 (*drv_set_freq)(afreq_ppm);
381
382 dfreq = 1.0e-6 * (afreq_ppm - current_freq_ppm) / (1.0 - 1.0e-6 * current_freq_ppm);
383
384 LCL_ReadRawTime(&raw);
385 (drv_offset_convert)(&raw, &correction);
386 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
387
388 /* Dispatch to all handlers */
389 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
390 (ptr->handler)(&raw, &cooked, dfreq, afreq_ppm, 0.0, 0, ptr->anything);
391 }
392
393 current_freq_ppm = afreq_ppm;
394
395}
396
397/* ================================================== */
398
399void
400LCL_AccumulateDeltaFrequency(double dfreq)
401{
402 ChangeListEntry *ptr;
403 struct timeval raw, cooked;
404 double correction;
405
406 /* Work out new absolute frequency. Note that absolute frequencies
407 are handled in units of ppm, whereas the 'dfreq' argument is in
408 terms of the gradient of the (offset) v (local time) function. */
409
410 current_freq_ppm = (1.0 - dfreq) * current_freq_ppm +
411 (1.0e6 * dfreq);
412
413 /* Call the system-specific driver for setting the frequency */
414 (*drv_set_freq)(current_freq_ppm);
415
416 LCL_ReadRawTime(&raw);
417 (drv_offset_convert)(&raw, &correction);
418 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
419
420 /* Dispatch to all handlers */
421 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
422 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, 0.0, 0, ptr->anything);
423 }
424
425}
426
427/* ================================================== */
428
429void
430LCL_AccumulateOffset(double offset)
431{
432 ChangeListEntry *ptr;
433 struct timeval raw, cooked;
434 double correction;
435
436 /* In this case, the cooked time to be passed to the notify clients
437 has to be the cooked time BEFORE the change was made */
438
439 LCL_ReadRawTime(&raw);
440 (drv_offset_convert)(&raw, &correction);
441 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
442
443 (*drv_accrue_offset)(offset);
444
445 /* Dispatch to all handlers */
446 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
447 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 0, ptr->anything);
448 }
449
450}
451
452/* ================================================== */
453
454void
455LCL_ApplyStepOffset(double offset)
456{
457 ChangeListEntry *ptr;
458 struct timeval raw, cooked;
459 double correction;
460
461 /* In this case, the cooked time to be passed to the notify clients
462 has to be the cooked time BEFORE the change was made */
463
464 LCL_ReadRawTime(&raw);
465 (drv_offset_convert)(&raw, &correction);
466 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
467
468 (*drv_apply_step_offset)(offset);
469
470 /* Dispatch to all handlers */
471 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
472 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 1, ptr->anything);
473 }
474
475}
476
477/* ================================================== */
478
479void
480LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
481{
482 ChangeListEntry *ptr;
483 struct timeval raw, cooked;
484 double correction;
485 double old_freq_ppm;
486
487 LCL_ReadRawTime(&raw);
488 (drv_offset_convert)(&raw, &correction);
489 /* Due to modifying the offset, this has to be the cooked time prior
490 to the change we are about to make */
491 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
492
493 old_freq_ppm = current_freq_ppm;
494
495 /* Work out new absolute frequency. Note that absolute frequencies
496 are handled in units of ppm, whereas the 'dfreq' argument is in
497 terms of the gradient of the (offset) v (local time) function. */
498 current_freq_ppm = (1.0 - dfreq) * old_freq_ppm +
499 (1.0e6 * dfreq);
500
501#ifdef TRACEON
502 LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
503 old_freq_ppm, current_freq_ppm, doffset);
504#endif
505
506 /* Call the system-specific driver for setting the frequency */
507 (*drv_set_freq)(current_freq_ppm);
508 (*drv_accrue_offset)(doffset);
509
510 /* Dispatch to all handlers */
511 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
512 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, doffset, 0, ptr->anything);
513 }
514
515
516}
517
518/* ================================================== */
519
520void
521lcl_InvokeDispersionNotifyHandlers(double dispersion)
522{
523 DispersionNotifyListEntry *ptr;
524
525 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
526 (ptr->handler)(dispersion, ptr->anything);
527 }
528
529}
530
531/* ================================================== */
532
533void
534lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
535 lcl_SetFrequencyDriver set_freq,
536 lcl_AccrueOffsetDriver accrue_offset,
537 lcl_ApplyStepOffsetDriver apply_step_offset,
538 lcl_OffsetCorrectionDriver offset_convert,
8f9c2370
ML
539 lcl_ImmediateStepDriver immediate_step,
540 lcl_SetLeapDriver set_leap)
88840341
RC
541{
542 drv_read_freq = read_freq;
543 drv_set_freq = set_freq;
544 drv_accrue_offset = accrue_offset;
545 drv_apply_step_offset = apply_step_offset;
546 drv_offset_convert = offset_convert;
547 drv_immediate_step = immediate_step;
8f9c2370 548 drv_set_leap = set_leap;
88840341
RC
549
550 current_freq_ppm = (*drv_read_freq)();
551
552#ifdef TRACEON
553 LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
554#endif
555
556 return;
557}
558
559/* ================================================== */
560/* Look at the current difference between the system time and the NTP
561 time, and make a step to cancel it. */
562
563int
564LCL_MakeStep(void)
565{
566 if (drv_immediate_step) {
567 (drv_immediate_step)();
568#ifdef TRACEON
569 LOG(LOGS_INFO, LOGF_Local, "Made step to system time to apply remaining slew");
570#endif
571 return 1;
572 }
573
574 return 0;
575}
576
577/* ================================================== */
8f9c2370
ML
578
579void
580LCL_SetLeap(int leap)
581{
582 if (drv_set_leap) {
583 (drv_set_leap)(leap);
584 }
585
586 return;
587}
588
589/* ================================================== */