]> git.ipfire.org Git - thirdparty/chrony.git/blame - local.c
sys_linux: allow lstat and readlink in seccomp filter
[thirdparty/chrony.git] / local.c
CommitLineData
88840341 1/*
88840341
RC
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
6672f045 5 * Copyright (C) Richard P. Curnow 1997-2003
61272e7c 6 * Copyright (C) Miroslav Lichvar 2011, 2014-2015
88840341
RC
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 The routines in this file present a common local (system) clock
26 interface to the rest of the software.
27
28 They interface with the system specific driver files in sys_*.c
29 */
30
da2c8d90
ML
31#include "config.h"
32
6ca73bf6 33#include "sysincl.h"
88840341 34
cc3a8918 35#include "conf.h"
88840341
RC
36#include "local.h"
37#include "localp.h"
38#include "memory.h"
7f45eb79 39#include "smooth.h"
88840341
RC
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
a12c7c42
ML
48/* Maximum allowed frequency, in ppm */
49static double max_freq_ppm;
50
c386d117
ML
51/* Temperature compensation, in ppm */
52static double temp_comp_ppm;
53
88840341
RC
54/* ================================================== */
55/* Store the system dependent drivers */
56
57static lcl_ReadFrequencyDriver drv_read_freq;
58static lcl_SetFrequencyDriver drv_set_freq;
59static lcl_AccrueOffsetDriver drv_accrue_offset;
60static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
61static lcl_OffsetCorrectionDriver drv_offset_convert;
8f9c2370 62static lcl_SetLeapDriver drv_set_leap;
e14a03a1 63static lcl_SetSyncStatusDriver drv_set_sync_status;
88840341
RC
64
65/* ================================================== */
66
67/* Types and variables associated with handling the parameter change
68 list */
69
70typedef struct _ChangeListEntry {
71 struct _ChangeListEntry *next;
72 struct _ChangeListEntry *prev;
73 LCL_ParameterChangeHandler handler;
74 void *anything;
75} ChangeListEntry;
76
77static ChangeListEntry change_list;
78
79/* ================================================== */
80
81/* Types and variables associated with handling the parameter change
82 list */
83
84typedef struct _DispersionNotifyListEntry {
85 struct _DispersionNotifyListEntry *next;
86 struct _DispersionNotifyListEntry *prev;
87 LCL_DispersionNotifyHandler handler;
88 void *anything;
89} DispersionNotifyListEntry;
90
91static DispersionNotifyListEntry dispersion_notify_list;
92
93/* ================================================== */
94
95static int precision_log;
96static double precision_quantum;
97
cc3a8918
ML
98static double max_clock_error;
99
88840341
RC
100/* ================================================== */
101
102/* Define the number of increments of the system clock that we want
103 to see to be fairly sure that we've got something approaching
104 the minimum increment. Even on a crummy implementation that can't
105 interpolate between 10ms ticks, we should get this done in
106 under 1s of busy waiting. */
107#define NITERS 100
108
d0dfa1de
ML
109#define NSEC_PER_SEC 1000000000
110
88840341
RC
111static void
112calculate_sys_precision(void)
113{
d0dfa1de
ML
114 struct timespec ts, old_ts;
115 int iters, diff, best;
116
117 LCL_ReadRawTime(&old_ts);
88840341 118
d0dfa1de
ML
119 /* Assume we must be better than a second */
120 best = NSEC_PER_SEC;
88840341 121 iters = 0;
d0dfa1de 122
88840341 123 do {
d0dfa1de
ML
124 LCL_ReadRawTime(&ts);
125
126 diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
127
128 old_ts = ts;
129 if (diff > 0) {
130 if (diff < best)
131 best = diff;
88840341
RC
132 iters++;
133 }
134 } while (iters < NITERS);
6b0198c2 135
d0dfa1de 136 assert(best > 0);
6b0198c2 137
d0dfa1de 138 precision_quantum = 1.0e-9 * best;
97ba9e4d
ML
139
140 /* Get rounded log2 value of the measured precision */
88840341 141 precision_log = 0;
d0dfa1de 142 while (best < 707106781) {
88840341 143 precision_log--;
d0dfa1de 144 best *= 2;
88840341 145 }
97ba9e4d 146
48b16ae6
ML
147 assert(precision_log >= -30);
148
f282856c 149 DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
88840341
RC
150}
151
152/* ================================================== */
153
154void
155LCL_Initialise(void)
156{
157 change_list.next = change_list.prev = &change_list;
158
159 dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
160
161 /* Null out the system drivers, so that we die
162 if they never get defined before use */
163
164 drv_read_freq = NULL;
165 drv_set_freq = NULL;
166 drv_accrue_offset = NULL;
167 drv_offset_convert = NULL;
168
169 /* This ought to be set from the system driver layer */
170 current_freq_ppm = 0.0;
c386d117 171 temp_comp_ppm = 0.0;
88840341
RC
172
173 calculate_sys_precision();
cc3a8918 174
a12c7c42
ML
175 /* This is the maximum allowed frequency offset in ppm, the time must
176 never stop or run backwards */
177 max_freq_ppm = CNF_GetMaxDrift();
178 max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
179
cc3a8918 180 max_clock_error = CNF_GetMaxClockError() * 1e-6;
88840341
RC
181}
182
183/* ================================================== */
184
185void
186LCL_Finalise(void)
187{
5f6f265f
ML
188 /* Make sure all handlers have been removed */
189 if (change_list.next != &change_list)
190 assert(0);
191 if (dispersion_notify_list.next != &dispersion_notify_list)
192 assert(0);
88840341
RC
193}
194
195/* ================================================== */
196
197/* Routine to read the system precision as a log to base 2 value. */
198int
199LCL_GetSysPrecisionAsLog(void)
200{
201 return precision_log;
202}
203
204/* ================================================== */
205/* Routine to read the system precision in terms of the actual time step */
206
207double
208LCL_GetSysPrecisionAsQuantum(void)
209{
210 return precision_quantum;
211}
212
213/* ================================================== */
214
cc3a8918
ML
215double
216LCL_GetMaxClockError(void)
217{
218 return max_clock_error;
219}
220
221/* ================================================== */
222
88840341
RC
223void
224LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
225{
226 ChangeListEntry *ptr, *new_entry;
227
228 /* Check that the handler is not already registered */
229 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
230 if (!(ptr->handler != handler || ptr->anything != anything)) {
6b0198c2 231 assert(0);
88840341
RC
232 }
233 }
234
235 new_entry = MallocNew(ChangeListEntry);
236
237 new_entry->handler = handler;
238 new_entry->anything = anything;
239
240 /* Chain it into the list */
241 new_entry->next = &change_list;
242 new_entry->prev = change_list.prev;
243 change_list.prev->next = new_entry;
244 change_list.prev = new_entry;
88840341
RC
245}
246
247/* ================================================== */
248
249/* Remove a handler */
88840341
RC
250void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
251{
252
253 ChangeListEntry *ptr;
254 int ok;
255
256 ptr = NULL;
257 ok = 0;
258
259 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
260 if (ptr->handler == handler && ptr->anything == anything) {
261 ok = 1;
262 break;
263 }
264 }
265
6b0198c2 266 assert(ok);
88840341
RC
267
268 /* Unlink entry from the list */
269 ptr->next->prev = ptr->prev;
270 ptr->prev->next = ptr->next;
271
33647339 272 Free(ptr);
88840341
RC
273}
274
275/* ================================================== */
276
0bdac2c7
ML
277int
278LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
279{
280 return change_list.next->handler == handler;
281}
282
283/* ================================================== */
284
b69b648d 285static void
d0dfa1de 286invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
b69b648d 287 double dfreq, double doffset,
44c9744d 288 LCL_ChangeType change_type)
b69b648d
ML
289{
290 ChangeListEntry *ptr;
291
292 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
44c9744d 293 (ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
b69b648d
ML
294 }
295}
296
297/* ================================================== */
298
88840341
RC
299void
300LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
301{
302 DispersionNotifyListEntry *ptr, *new_entry;
303
304 /* Check that the handler is not already registered */
305 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
306 if (!(ptr->handler != handler || ptr->anything != anything)) {
6b0198c2 307 assert(0);
88840341
RC
308 }
309 }
310
311 new_entry = MallocNew(DispersionNotifyListEntry);
312
313 new_entry->handler = handler;
314 new_entry->anything = anything;
315
316 /* Chain it into the list */
317 new_entry->next = &dispersion_notify_list;
318 new_entry->prev = dispersion_notify_list.prev;
319 dispersion_notify_list.prev->next = new_entry;
320 dispersion_notify_list.prev = new_entry;
88840341
RC
321}
322
323/* ================================================== */
324
325/* Remove a handler */
326extern
327void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
328{
329
330 DispersionNotifyListEntry *ptr;
331 int ok;
332
333 ptr = NULL;
334 ok = 0;
335
336 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
337 if (ptr->handler == handler && ptr->anything == anything) {
338 ok = 1;
339 break;
340 }
341 }
342
6b0198c2 343 assert(ok);
88840341
RC
344
345 /* Unlink entry from the list */
346 ptr->next->prev = ptr->prev;
347 ptr->prev->next = ptr->next;
348
33647339 349 Free(ptr);
88840341
RC
350}
351
352/* ================================================== */
88840341
RC
353
354void
d0dfa1de 355LCL_ReadRawTime(struct timespec *ts)
88840341 356{
8d89610f
ML
357#if HAVE_CLOCK_GETTIME
358 if (clock_gettime(CLOCK_REALTIME, ts) < 0)
f282856c 359 LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
8d89610f 360#else
d0dfa1de
ML
361 struct timeval tv;
362
8d89610f 363 if (gettimeofday(&tv, NULL) < 0)
f282856c 364 LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
d0dfa1de
ML
365
366 UTI_TimevalToTimespec(&tv, ts);
8d89610f 367#endif
88840341
RC
368}
369
370/* ================================================== */
371
372void
d0dfa1de 373LCL_ReadCookedTime(struct timespec *result, double *err)
88840341 374{
d0dfa1de 375 struct timespec raw;
88840341
RC
376
377 LCL_ReadRawTime(&raw);
20d898d1
ML
378 LCL_CookTime(&raw, result, err);
379}
88840341 380
20d898d1 381/* ================================================== */
88840341 382
20d898d1 383void
d0dfa1de 384LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
20d898d1
ML
385{
386 double correction;
88840341 387
20d898d1 388 LCL_GetOffsetCorrection(raw, &correction, err);
d0dfa1de 389 UTI_AddDoubleToTimespec(raw, correction, cooked);
88840341
RC
390}
391
392/* ================================================== */
393
20d898d1 394void
d0dfa1de 395LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
88840341 396{
20d898d1 397 /* Call system specific driver to get correction */
1faeb450 398 (*drv_offset_convert)(raw, correction, err);
88840341
RC
399}
400
401/* ================================================== */
22fda21e 402/* Return current frequency */
88840341
RC
403
404double
405LCL_ReadAbsoluteFrequency(void)
406{
c386d117
ML
407 double freq;
408
22fda21e 409 freq = current_freq_ppm;
c386d117
ML
410
411 /* Undo temperature compensation */
412 if (temp_comp_ppm != 0.0) {
413 freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm);
414 }
415
416 return freq;
88840341
RC
417}
418
419/* ================================================== */
183a648d
ML
420
421static double
422clamp_freq(double freq)
423{
a12c7c42 424 if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
183a648d
ML
425 return freq;
426
f282856c 427 LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
183a648d 428
a12c7c42 429 return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
183a648d
ML
430}
431
432/* ================================================== */
433
aec97397 434static int
d0dfa1de 435check_offset(struct timespec *now, double offset)
aec97397
ML
436{
437 /* Check if the time will be still sane with accumulated offset */
438 if (UTI_IsTimeOffsetSane(now, -offset))
439 return 1;
440
f282856c 441 LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
aec97397
ML
442 return 0;
443}
444
445/* ================================================== */
446
88840341
RC
447/* This involves both setting the absolute frequency with the
448 system-specific driver, as well as calling all notify handlers */
449
450void
451LCL_SetAbsoluteFrequency(double afreq_ppm)
452{
d0dfa1de 453 struct timespec raw, cooked;
88840341
RC
454 double dfreq;
455
183a648d
ML
456 afreq_ppm = clamp_freq(afreq_ppm);
457
c386d117
ML
458 /* Apply temperature compensation */
459 if (temp_comp_ppm != 0.0) {
460 afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
461 }
462
88840341
RC
463 /* Call the system-specific driver for setting the frequency */
464
1a7415a6 465 afreq_ppm = (*drv_set_freq)(afreq_ppm);
88840341 466
60d0fa29 467 dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm);
88840341
RC
468
469 LCL_ReadRawTime(&raw);
20d898d1 470 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
471
472 /* Dispatch to all handlers */
44c9744d 473 invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
88840341
RC
474
475 current_freq_ppm = afreq_ppm;
476
477}
478
479/* ================================================== */
480
481void
482LCL_AccumulateDeltaFrequency(double dfreq)
483{
d0dfa1de 484 struct timespec raw, cooked;
1a7415a6
ML
485 double old_freq_ppm;
486
487 old_freq_ppm = current_freq_ppm;
88840341
RC
488
489 /* Work out new absolute frequency. Note that absolute frequencies
490 are handled in units of ppm, whereas the 'dfreq' argument is in
491 terms of the gradient of the (offset) v (local time) function. */
492
d34ebdb4 493 current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
88840341 494
183a648d
ML
495 current_freq_ppm = clamp_freq(current_freq_ppm);
496
88840341 497 /* Call the system-specific driver for setting the frequency */
1a7415a6 498 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
60d0fa29 499 dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
88840341
RC
500
501 LCL_ReadRawTime(&raw);
20d898d1 502 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
503
504 /* Dispatch to all handlers */
44c9744d 505 invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
88840341
RC
506}
507
508/* ================================================== */
509
510void
c7d0232b 511LCL_AccumulateOffset(double offset, double corr_rate)
88840341 512{
d0dfa1de 513 struct timespec raw, cooked;
88840341
RC
514
515 /* In this case, the cooked time to be passed to the notify clients
516 has to be the cooked time BEFORE the change was made */
517
518 LCL_ReadRawTime(&raw);
20d898d1 519 LCL_CookTime(&raw, &cooked, NULL);
88840341 520
aec97397
ML
521 if (!check_offset(&cooked, offset))
522 return;
523
c7d0232b 524 (*drv_accrue_offset)(offset, corr_rate);
88840341
RC
525
526 /* Dispatch to all handlers */
44c9744d 527 invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
88840341
RC
528}
529
530/* ================================================== */
531
aec97397 532int
88840341
RC
533LCL_ApplyStepOffset(double offset)
534{
d0dfa1de 535 struct timespec raw, cooked;
88840341
RC
536
537 /* In this case, the cooked time to be passed to the notify clients
538 has to be the cooked time BEFORE the change was made */
539
540 LCL_ReadRawTime(&raw);
20d898d1 541 LCL_CookTime(&raw, &cooked, NULL);
88840341 542
aec97397
ML
543 if (!check_offset(&raw, offset))
544 return 0;
545
f6a9c5c1 546 if (!(*drv_apply_step_offset)(offset)) {
0ff449e6 547 LOG(LOGS_ERR, "Could not step system clock");
f6a9c5c1
ML
548 return 0;
549 }
88840341 550
7f45eb79
ML
551 /* Reset smoothing on all clock steps */
552 SMT_Reset(&cooked);
553
88840341 554 /* Dispatch to all handlers */
44c9744d 555 invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
aec97397
ML
556
557 return 1;
88840341
RC
558}
559
560/* ================================================== */
561
91749ebb 562void
d0dfa1de 563LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
91749ebb
ML
564 double offset, double dispersion)
565{
91749ebb 566 /* Dispatch to all handlers */
a33a9551 567 invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
91749ebb
ML
568
569 lcl_InvokeDispersionNotifyHandlers(dispersion);
570}
571
572/* ================================================== */
573
f8db8324
ML
574void
575LCL_NotifyLeap(int leap)
576{
d0dfa1de 577 struct timespec raw, cooked;
f8db8324
ML
578
579 LCL_ReadRawTime(&raw);
580 LCL_CookTime(&raw, &cooked, NULL);
581
0abdc2a3
ML
582 /* Smooth the leap second out */
583 SMT_Leap(&cooked, leap);
584
f8db8324
ML
585 /* Dispatch to all handlers as if the clock was stepped */
586 invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
587}
588
589/* ================================================== */
590
88840341 591void
c7d0232b 592LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
88840341 593{
d0dfa1de 594 struct timespec raw, cooked;
88840341
RC
595 double old_freq_ppm;
596
597 LCL_ReadRawTime(&raw);
88840341
RC
598 /* Due to modifying the offset, this has to be the cooked time prior
599 to the change we are about to make */
20d898d1 600 LCL_CookTime(&raw, &cooked, NULL);
88840341 601
aec97397
ML
602 if (!check_offset(&cooked, doffset))
603 return;
604
88840341
RC
605 old_freq_ppm = current_freq_ppm;
606
607 /* Work out new absolute frequency. Note that absolute frequencies
608 are handled in units of ppm, whereas the 'dfreq' argument is in
609 terms of the gradient of the (offset) v (local time) function. */
d34ebdb4 610 current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
88840341 611
183a648d
ML
612 current_freq_ppm = clamp_freq(current_freq_ppm);
613
f282856c 614 DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
88840341 615 old_freq_ppm, current_freq_ppm, doffset);
88840341
RC
616
617 /* Call the system-specific driver for setting the frequency */
1a7415a6 618 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
60d0fa29 619 dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
1a7415a6 620
c7d0232b 621 (*drv_accrue_offset)(doffset, corr_rate);
88840341
RC
622
623 /* Dispatch to all handlers */
58b211d7 624 invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
88840341
RC
625}
626
627/* ================================================== */
628
629void
630lcl_InvokeDispersionNotifyHandlers(double dispersion)
631{
632 DispersionNotifyListEntry *ptr;
633
634 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
635 (ptr->handler)(dispersion, ptr->anything);
636 }
637
638}
639
640/* ================================================== */
641
642void
643lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
644 lcl_SetFrequencyDriver set_freq,
645 lcl_AccrueOffsetDriver accrue_offset,
646 lcl_ApplyStepOffsetDriver apply_step_offset,
647 lcl_OffsetCorrectionDriver offset_convert,
e14a03a1
ML
648 lcl_SetLeapDriver set_leap,
649 lcl_SetSyncStatusDriver set_sync_status)
88840341
RC
650{
651 drv_read_freq = read_freq;
652 drv_set_freq = set_freq;
653 drv_accrue_offset = accrue_offset;
654 drv_apply_step_offset = apply_step_offset;
655 drv_offset_convert = offset_convert;
8f9c2370 656 drv_set_leap = set_leap;
e14a03a1 657 drv_set_sync_status = set_sync_status;
88840341
RC
658
659 current_freq_ppm = (*drv_read_freq)();
660
f282856c 661 DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
88840341
RC
662}
663
664/* ================================================== */
665/* Look at the current difference between the system time and the NTP
20d2363f 666 time, and make a step to cancel it. */
88840341
RC
667
668int
20d2363f 669LCL_MakeStep(void)
88840341 670{
d0dfa1de 671 struct timespec raw;
15e154c0
ML
672 double correction;
673
674 LCL_ReadRawTime(&raw);
20d898d1 675 LCL_GetOffsetCorrection(&raw, &correction, NULL);
15e154c0 676
aec97397
ML
677 if (!check_offset(&raw, -correction))
678 return 0;
679
15e154c0 680 /* Cancel remaining slew and make the step */
c7d0232b 681 LCL_AccumulateOffset(correction, 0.0);
aec97397
ML
682 if (!LCL_ApplyStepOffset(-correction))
683 return 0;
15e154c0 684
f282856c 685 LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
88840341 686
15e154c0 687 return 1;
88840341
RC
688}
689
690/* ================================================== */
8f9c2370 691
802a98e7
ML
692int
693LCL_CanSystemLeap(void)
694{
695 return drv_set_leap ? 1 : 0;
696}
697
698/* ================================================== */
699
8f9c2370 700void
a768578a 701LCL_SetSystemLeap(int leap, int tai_offset)
8f9c2370
ML
702{
703 if (drv_set_leap) {
a768578a 704 (drv_set_leap)(leap, tai_offset);
8f9c2370 705 }
8f9c2370
ML
706}
707
708/* ================================================== */
c386d117 709
1a7415a6 710double
c386d117
ML
711LCL_SetTempComp(double comp)
712{
1a7415a6
ML
713 double uncomp_freq_ppm;
714
c386d117 715 if (temp_comp_ppm == comp)
1a7415a6 716 return comp;
c386d117
ML
717
718 /* Undo previous compensation */
719 current_freq_ppm = (current_freq_ppm + temp_comp_ppm) /
720 (1.0 - 1.0e-6 * temp_comp_ppm);
721
1a7415a6
ML
722 uncomp_freq_ppm = current_freq_ppm;
723
c386d117
ML
724 /* Apply new compensation */
725 current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp;
726
c386d117 727 /* Call the system-specific driver for setting the frequency */
1a7415a6 728 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
c386d117 729
1a7415a6
ML
730 temp_comp_ppm = (uncomp_freq_ppm - current_freq_ppm) /
731 (1.0e-6 * uncomp_freq_ppm + 1.0);
732
733 return temp_comp_ppm;
c386d117
ML
734}
735
736/* ================================================== */
e14a03a1
ML
737
738void
739LCL_SetSyncStatus(int synchronised, double est_error, double max_error)
740{
741 if (drv_set_sync_status) {
742 (drv_set_sync_status)(synchronised, est_error, max_error);
743 }
744}
745
746/* ================================================== */