]> git.ipfire.org Git - thirdparty/chrony.git/blame - local.c
Add fallback drifts
[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;
8f9c2370 56static lcl_SetLeapDriver drv_set_leap;
88840341
RC
57
58/* ================================================== */
59
60/* Types and variables associated with handling the parameter change
61 list */
62
63typedef struct _ChangeListEntry {
64 struct _ChangeListEntry *next;
65 struct _ChangeListEntry *prev;
66 LCL_ParameterChangeHandler handler;
67 void *anything;
68} ChangeListEntry;
69
70static ChangeListEntry change_list;
71
72/* ================================================== */
73
74/* Types and variables associated with handling the parameter change
75 list */
76
77typedef struct _DispersionNotifyListEntry {
78 struct _DispersionNotifyListEntry *next;
79 struct _DispersionNotifyListEntry *prev;
80 LCL_DispersionNotifyHandler handler;
81 void *anything;
82} DispersionNotifyListEntry;
83
84static DispersionNotifyListEntry dispersion_notify_list;
85
86/* ================================================== */
87
88static int precision_log;
89static double precision_quantum;
90
91/* ================================================== */
92
93/* Define the number of increments of the system clock that we want
94 to see to be fairly sure that we've got something approaching
95 the minimum increment. Even on a crummy implementation that can't
96 interpolate between 10ms ticks, we should get this done in
97 under 1s of busy waiting. */
98#define NITERS 100
99
100static void
101calculate_sys_precision(void)
102{
103 struct timeval tv, old_tv, first_tv;
104 struct timezone tz;
105 int dusec, best_dusec;
106 int iters;
107
108 gettimeofday(&old_tv, &tz);
109 first_tv = old_tv;
110 best_dusec = 1000000; /* Assume we must be better than a second */
111 iters = 0;
112 do {
113 gettimeofday(&tv, &tz);
114 dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
115 old_tv = tv;
116 if (dusec > 0) {
117 if (dusec < best_dusec) {
118 best_dusec = dusec;
119 }
120 iters++;
121 }
122 } while (iters < NITERS);
123 if (!(best_dusec > 0)) {
124 CROAK("best_dusec should be positive");
125 }
10c9a7d4 126 precision_quantum = best_dusec * 1.0e-6;
88840341
RC
127 precision_log = 0;
128 while (best_dusec < 500000) {
129 precision_log--;
130 best_dusec *= 2;
131 }
132
88840341
RC
133 return;
134}
135
136/* ================================================== */
137
138void
139LCL_Initialise(void)
140{
141 change_list.next = change_list.prev = &change_list;
142
143 dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
144
145 /* Null out the system drivers, so that we die
146 if they never get defined before use */
147
148 drv_read_freq = NULL;
149 drv_set_freq = NULL;
150 drv_accrue_offset = NULL;
151 drv_offset_convert = NULL;
152
153 /* This ought to be set from the system driver layer */
154 current_freq_ppm = 0.0;
155
156 calculate_sys_precision();
157}
158
159/* ================================================== */
160
161void
162LCL_Finalise(void)
163{
164 return;
165}
166
167/* ================================================== */
168
169/* Routine to read the system precision as a log to base 2 value. */
170int
171LCL_GetSysPrecisionAsLog(void)
172{
173 return precision_log;
174}
175
176/* ================================================== */
177/* Routine to read the system precision in terms of the actual time step */
178
179double
180LCL_GetSysPrecisionAsQuantum(void)
181{
182 return precision_quantum;
183}
184
185/* ================================================== */
186
187void
188LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
189{
190 ChangeListEntry *ptr, *new_entry;
191
192 /* Check that the handler is not already registered */
193 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
194 if (!(ptr->handler != handler || ptr->anything != anything)) {
195 CROAK("a handler is already registered");
196 }
197 }
198
199 new_entry = MallocNew(ChangeListEntry);
200
201 new_entry->handler = handler;
202 new_entry->anything = anything;
203
204 /* Chain it into the list */
205 new_entry->next = &change_list;
206 new_entry->prev = change_list.prev;
207 change_list.prev->next = new_entry;
208 change_list.prev = new_entry;
209
210 return;
211}
212
213/* ================================================== */
214
215/* Remove a handler */
216extern
217void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
218{
219
220 ChangeListEntry *ptr;
221 int ok;
222
223 ptr = NULL;
224 ok = 0;
225
226 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
227 if (ptr->handler == handler && ptr->anything == anything) {
228 ok = 1;
229 break;
230 }
231 }
232
233 if (!ok) {
234 CROAK("did not find a matching handler");
235 }
236
237 /* Unlink entry from the list */
238 ptr->next->prev = ptr->prev;
239 ptr->prev->next = ptr->next;
240
241 free(ptr);
242
243 return;
244}
245
246/* ================================================== */
247
248void
249LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
250{
251 DispersionNotifyListEntry *ptr, *new_entry;
252
253 /* Check that the handler is not already registered */
254 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
255 if (!(ptr->handler != handler || ptr->anything != anything)) {
256 CROAK("a handler is already registered");
257 }
258 }
259
260 new_entry = MallocNew(DispersionNotifyListEntry);
261
262 new_entry->handler = handler;
263 new_entry->anything = anything;
264
265 /* Chain it into the list */
266 new_entry->next = &dispersion_notify_list;
267 new_entry->prev = dispersion_notify_list.prev;
268 dispersion_notify_list.prev->next = new_entry;
269 dispersion_notify_list.prev = new_entry;
270
271 return;
272}
273
274/* ================================================== */
275
276/* Remove a handler */
277extern
278void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
279{
280
281 DispersionNotifyListEntry *ptr;
282 int ok;
283
284 ptr = NULL;
285 ok = 0;
286
287 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
288 if (ptr->handler == handler && ptr->anything == anything) {
289 ok = 1;
290 break;
291 }
292 }
293
294 if (!ok) {
295 CROAK("no matching handler found");
296 }
297
298 /* Unlink entry from the list */
299 ptr->next->prev = ptr->prev;
300 ptr->prev->next = ptr->next;
301
302 free(ptr);
303
304 return;
305}
306
307/* ================================================== */
308/* At the moment, this is just gettimeofday(), because
309 I can't think of a Unix system where it would not be */
310
311void
312LCL_ReadRawTime(struct timeval *result)
313{
314 struct timezone tz;
315
316 if (!(gettimeofday(result, &tz) >= 0)) {
317 CROAK("Could not get time of day");
318 }
88840341
RC
319}
320
321/* ================================================== */
322
323void
324LCL_ReadCookedTime(struct timeval *result, double *err)
325{
326 struct timeval raw;
88840341
RC
327
328 LCL_ReadRawTime(&raw);
20d898d1
ML
329 LCL_CookTime(&raw, result, err);
330}
88840341 331
20d898d1 332/* ================================================== */
88840341 333
20d898d1
ML
334void
335LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err)
336{
337 double correction;
88840341 338
20d898d1
ML
339 LCL_GetOffsetCorrection(raw, &correction, err);
340 UTI_AddDoubleToTimeval(raw, correction, cooked);
88840341
RC
341}
342
343/* ================================================== */
344
20d898d1
ML
345void
346LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
88840341 347{
20d898d1
ML
348 double e;
349
350 /* Call system specific driver to get correction */
351 (*drv_offset_convert)(raw, correction, &e);
352
353 if (err)
354 *err = e;
88840341
RC
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;
88840341
RC
375 double dfreq;
376
377 /* Call the system-specific driver for setting the frequency */
378
379 (*drv_set_freq)(afreq_ppm);
380
99d18abf 381 dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 + current_freq_ppm);
88840341
RC
382
383 LCL_ReadRawTime(&raw);
20d898d1 384 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
385
386 /* Dispatch to all handlers */
387 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
388 (ptr->handler)(&raw, &cooked, dfreq, afreq_ppm, 0.0, 0, ptr->anything);
389 }
390
391 current_freq_ppm = afreq_ppm;
392
393}
394
395/* ================================================== */
396
397void
398LCL_AccumulateDeltaFrequency(double dfreq)
399{
400 ChangeListEntry *ptr;
401 struct timeval raw, cooked;
88840341
RC
402
403 /* Work out new absolute frequency. Note that absolute frequencies
404 are handled in units of ppm, whereas the 'dfreq' argument is in
405 terms of the gradient of the (offset) v (local time) function. */
406
99d18abf 407 current_freq_ppm = (1.0 + dfreq) * current_freq_ppm + 1.0e6 * dfreq;
88840341
RC
408
409 /* Call the system-specific driver for setting the frequency */
410 (*drv_set_freq)(current_freq_ppm);
411
412 LCL_ReadRawTime(&raw);
20d898d1 413 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
414
415 /* Dispatch to all handlers */
416 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
417 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, 0.0, 0, ptr->anything);
418 }
419
420}
421
422/* ================================================== */
423
424void
425LCL_AccumulateOffset(double offset)
426{
427 ChangeListEntry *ptr;
428 struct timeval raw, cooked;
88840341
RC
429
430 /* In this case, the cooked time to be passed to the notify clients
431 has to be the cooked time BEFORE the change was made */
432
433 LCL_ReadRawTime(&raw);
20d898d1 434 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
435
436 (*drv_accrue_offset)(offset);
437
438 /* Dispatch to all handlers */
439 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
440 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 0, ptr->anything);
441 }
442
443}
444
445/* ================================================== */
446
447void
448LCL_ApplyStepOffset(double offset)
449{
450 ChangeListEntry *ptr;
451 struct timeval raw, cooked;
88840341
RC
452
453 /* In this case, the cooked time to be passed to the notify clients
454 has to be the cooked time BEFORE the change was made */
455
456 LCL_ReadRawTime(&raw);
20d898d1 457 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
458
459 (*drv_apply_step_offset)(offset);
460
461 /* Dispatch to all handlers */
462 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
463 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 1, ptr->anything);
464 }
465
466}
467
468/* ================================================== */
469
470void
471LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
472{
473 ChangeListEntry *ptr;
474 struct timeval raw, cooked;
88840341
RC
475 double old_freq_ppm;
476
477 LCL_ReadRawTime(&raw);
88840341
RC
478 /* Due to modifying the offset, this has to be the cooked time prior
479 to the change we are about to make */
20d898d1 480 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
481
482 old_freq_ppm = current_freq_ppm;
483
484 /* Work out new absolute frequency. Note that absolute frequencies
485 are handled in units of ppm, whereas the 'dfreq' argument is in
486 terms of the gradient of the (offset) v (local time) function. */
99d18abf 487 current_freq_ppm = (1.0 + dfreq) * old_freq_ppm + 1.0e6 * dfreq;
88840341
RC
488
489#ifdef TRACEON
490 LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
491 old_freq_ppm, current_freq_ppm, doffset);
492#endif
493
494 /* Call the system-specific driver for setting the frequency */
495 (*drv_set_freq)(current_freq_ppm);
496 (*drv_accrue_offset)(doffset);
497
498 /* Dispatch to all handlers */
499 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
500 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, doffset, 0, ptr->anything);
501 }
502
503
504}
505
506/* ================================================== */
507
508void
509lcl_InvokeDispersionNotifyHandlers(double dispersion)
510{
511 DispersionNotifyListEntry *ptr;
512
513 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
514 (ptr->handler)(dispersion, ptr->anything);
515 }
516
517}
518
519/* ================================================== */
520
521void
522lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
523 lcl_SetFrequencyDriver set_freq,
524 lcl_AccrueOffsetDriver accrue_offset,
525 lcl_ApplyStepOffsetDriver apply_step_offset,
526 lcl_OffsetCorrectionDriver offset_convert,
8f9c2370 527 lcl_SetLeapDriver set_leap)
88840341
RC
528{
529 drv_read_freq = read_freq;
530 drv_set_freq = set_freq;
531 drv_accrue_offset = accrue_offset;
532 drv_apply_step_offset = apply_step_offset;
533 drv_offset_convert = offset_convert;
8f9c2370 534 drv_set_leap = set_leap;
88840341
RC
535
536 current_freq_ppm = (*drv_read_freq)();
537
538#ifdef TRACEON
539 LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
540#endif
541
542 return;
543}
544
545/* ================================================== */
546/* Look at the current difference between the system time and the NTP
8a00758c 547 time, and make a step to cancel it if it's larger than the threshold. */
88840341
RC
548
549int
8a00758c 550LCL_MakeStep(double threshold)
88840341 551{
15e154c0
ML
552 struct timeval raw;
553 double correction;
554
555 LCL_ReadRawTime(&raw);
20d898d1 556 LCL_GetOffsetCorrection(&raw, &correction, NULL);
15e154c0 557
8a00758c
ML
558 if (fabs(correction) <= threshold)
559 return 0;
560
15e154c0
ML
561 /* Cancel remaining slew and make the step */
562 LCL_AccumulateOffset(correction);
563 LCL_ApplyStepOffset(-correction);
564
565 LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.3f seconds", correction);
88840341 566
15e154c0 567 return 1;
88840341
RC
568}
569
570/* ================================================== */
8f9c2370
ML
571
572void
573LCL_SetLeap(int leap)
574{
575 if (drv_set_leap) {
576 (drv_set_leap)(leap);
577 }
578
579 return;
580}
581
582/* ================================================== */