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