]> git.ipfire.org Git - thirdparty/chrony.git/blame - local.c
Retry name resolving after temporary failure few times before giving up
[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.,
22 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
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;
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 }
126 precision_log = 0;
127 while (best_dusec < 500000) {
128 precision_log--;
129 best_dusec *= 2;
130 }
131
132 precision_quantum = 1.0 / (double)(1<<(-precision_log));
133
134 return;
135}
136
137/* ================================================== */
138
139void
140LCL_Initialise(void)
141{
142 change_list.next = change_list.prev = &change_list;
143
144 dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
145
146 /* Null out the system drivers, so that we die
147 if they never get defined before use */
148
149 drv_read_freq = NULL;
150 drv_set_freq = NULL;
151 drv_accrue_offset = NULL;
152 drv_offset_convert = NULL;
153
154 /* This ought to be set from the system driver layer */
155 current_freq_ppm = 0.0;
156
157 calculate_sys_precision();
158}
159
160/* ================================================== */
161
162void
163LCL_Finalise(void)
164{
165 return;
166}
167
168/* ================================================== */
169
170/* Routine to read the system precision as a log to base 2 value. */
171int
172LCL_GetSysPrecisionAsLog(void)
173{
174 return precision_log;
175}
176
177/* ================================================== */
178/* Routine to read the system precision in terms of the actual time step */
179
180double
181LCL_GetSysPrecisionAsQuantum(void)
182{
183 return precision_quantum;
184}
185
186/* ================================================== */
187
188void
189LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
190{
191 ChangeListEntry *ptr, *new_entry;
192
193 /* Check that the handler is not already registered */
194 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
195 if (!(ptr->handler != handler || ptr->anything != anything)) {
196 CROAK("a handler is already registered");
197 }
198 }
199
200 new_entry = MallocNew(ChangeListEntry);
201
202 new_entry->handler = handler;
203 new_entry->anything = anything;
204
205 /* Chain it into the list */
206 new_entry->next = &change_list;
207 new_entry->prev = change_list.prev;
208 change_list.prev->next = new_entry;
209 change_list.prev = new_entry;
210
211 return;
212}
213
214/* ================================================== */
215
216/* Remove a handler */
217extern
218void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
219{
220
221 ChangeListEntry *ptr;
222 int ok;
223
224 ptr = NULL;
225 ok = 0;
226
227 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
228 if (ptr->handler == handler && ptr->anything == anything) {
229 ok = 1;
230 break;
231 }
232 }
233
234 if (!ok) {
235 CROAK("did not find a matching handler");
236 }
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)) {
257 CROAK("a handler is already registered");
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
295 if (!ok) {
296 CROAK("no matching handler found");
297 }
298
299 /* Unlink entry from the list */
300 ptr->next->prev = ptr->prev;
301 ptr->prev->next = ptr->next;
302
303 free(ptr);
304
305 return;
306}
307
308/* ================================================== */
309/* At the moment, this is just gettimeofday(), because
310 I can't think of a Unix system where it would not be */
311
312void
313LCL_ReadRawTime(struct timeval *result)
314{
315 struct timezone tz;
316
317 if (!(gettimeofday(result, &tz) >= 0)) {
318 CROAK("Could not get time of day");
319 }
320 return;
321
322}
323
324/* ================================================== */
325
326void
327LCL_ReadCookedTime(struct timeval *result, double *err)
328{
329 struct timeval raw;
330 double correction;
331
332 LCL_ReadRawTime(&raw);
333
334 /* For now, cheat and set the error to zero in all cases.
335 */
336
337 *err = 0.0;
338
339 /* Call system specific driver to get correction */
340 (*drv_offset_convert)(&raw, &correction);
341 UTI_AddDoubleToTimeval(&raw, correction, result);
342
343 return;
344}
345
346/* ================================================== */
347
348double
349LCL_GetOffsetCorrection(struct timeval *raw)
350{
351 double correction;
352 (*drv_offset_convert)(raw, &correction);
353 return correction;
354}
355
356/* ================================================== */
357/* This is just a simple passthrough of the system specific routine */
358
359double
360LCL_ReadAbsoluteFrequency(void)
361{
362 return (*drv_read_freq)();
363}
364
365/* ================================================== */
366/* This involves both setting the absolute frequency with the
367 system-specific driver, as well as calling all notify handlers */
368
369void
370LCL_SetAbsoluteFrequency(double afreq_ppm)
371{
372 ChangeListEntry *ptr;
373 struct timeval raw, cooked;
374 double correction;
375 double dfreq;
376
377 /* Call the system-specific driver for setting the frequency */
378
379 (*drv_set_freq)(afreq_ppm);
380
381 dfreq = 1.0e-6 * (afreq_ppm - current_freq_ppm) / (1.0 - 1.0e-6 * current_freq_ppm);
382
383 LCL_ReadRawTime(&raw);
384 (drv_offset_convert)(&raw, &correction);
385 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
386
387 /* Dispatch to all handlers */
388 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
389 (ptr->handler)(&raw, &cooked, dfreq, afreq_ppm, 0.0, 0, ptr->anything);
390 }
391
392 current_freq_ppm = afreq_ppm;
393
394}
395
396/* ================================================== */
397
398void
399LCL_AccumulateDeltaFrequency(double dfreq)
400{
401 ChangeListEntry *ptr;
402 struct timeval raw, cooked;
403 double correction;
404
405 /* Work out new absolute frequency. Note that absolute frequencies
406 are handled in units of ppm, whereas the 'dfreq' argument is in
407 terms of the gradient of the (offset) v (local time) function. */
408
409 current_freq_ppm = (1.0 - dfreq) * current_freq_ppm +
410 (1.0e6 * dfreq);
411
412 /* Call the system-specific driver for setting the frequency */
413 (*drv_set_freq)(current_freq_ppm);
414
415 LCL_ReadRawTime(&raw);
416 (drv_offset_convert)(&raw, &correction);
417 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
418
419 /* Dispatch to all handlers */
420 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
421 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, 0.0, 0, ptr->anything);
422 }
423
424}
425
426/* ================================================== */
427
428void
429LCL_AccumulateOffset(double offset)
430{
431 ChangeListEntry *ptr;
432 struct timeval raw, cooked;
433 double correction;
434
435 /* In this case, the cooked time to be passed to the notify clients
436 has to be the cooked time BEFORE the change was made */
437
438 LCL_ReadRawTime(&raw);
439 (drv_offset_convert)(&raw, &correction);
440 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
441
442 (*drv_accrue_offset)(offset);
443
444 /* Dispatch to all handlers */
445 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
446 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 0, ptr->anything);
447 }
448
449}
450
451/* ================================================== */
452
453void
454LCL_ApplyStepOffset(double offset)
455{
456 ChangeListEntry *ptr;
457 struct timeval raw, cooked;
458 double correction;
459
460 /* In this case, the cooked time to be passed to the notify clients
461 has to be the cooked time BEFORE the change was made */
462
463 LCL_ReadRawTime(&raw);
464 (drv_offset_convert)(&raw, &correction);
465 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
466
467 (*drv_apply_step_offset)(offset);
468
469 /* Dispatch to all handlers */
470 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
471 (ptr->handler)(&raw, &cooked, 0.0, current_freq_ppm, offset, 1, ptr->anything);
472 }
473
474}
475
476/* ================================================== */
477
478void
479LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
480{
481 ChangeListEntry *ptr;
482 struct timeval raw, cooked;
483 double correction;
484 double old_freq_ppm;
485
486 LCL_ReadRawTime(&raw);
487 (drv_offset_convert)(&raw, &correction);
488 /* Due to modifying the offset, this has to be the cooked time prior
489 to the change we are about to make */
490 UTI_AddDoubleToTimeval(&raw, correction, &cooked);
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. */
497 current_freq_ppm = (1.0 - dfreq) * old_freq_ppm +
498 (1.0e6 * dfreq);
499
500#ifdef TRACEON
501 LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
502 old_freq_ppm, current_freq_ppm, doffset);
503#endif
504
505 /* Call the system-specific driver for setting the frequency */
506 (*drv_set_freq)(current_freq_ppm);
507 (*drv_accrue_offset)(doffset);
508
509 /* Dispatch to all handlers */
510 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
511 (ptr->handler)(&raw, &cooked, dfreq, current_freq_ppm, doffset, 0, ptr->anything);
512 }
513
514
515}
516
517/* ================================================== */
518
519void
520lcl_InvokeDispersionNotifyHandlers(double dispersion)
521{
522 DispersionNotifyListEntry *ptr;
523
524 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
525 (ptr->handler)(dispersion, ptr->anything);
526 }
527
528}
529
530/* ================================================== */
531
532void
533lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
534 lcl_SetFrequencyDriver set_freq,
535 lcl_AccrueOffsetDriver accrue_offset,
536 lcl_ApplyStepOffsetDriver apply_step_offset,
537 lcl_OffsetCorrectionDriver offset_convert,
538 lcl_ImmediateStepDriver immediate_step)
539{
540 drv_read_freq = read_freq;
541 drv_set_freq = set_freq;
542 drv_accrue_offset = accrue_offset;
543 drv_apply_step_offset = apply_step_offset;
544 drv_offset_convert = offset_convert;
545 drv_immediate_step = immediate_step;
546
547 current_freq_ppm = (*drv_read_freq)();
548
549#ifdef TRACEON
550 LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
551#endif
552
553 return;
554}
555
556/* ================================================== */
557/* Look at the current difference between the system time and the NTP
558 time, and make a step to cancel it. */
559
560int
561LCL_MakeStep(void)
562{
563 if (drv_immediate_step) {
564 (drv_immediate_step)();
565#ifdef TRACEON
566 LOG(LOGS_INFO, LOGF_Local, "Made step to system time to apply remaining slew");
567#endif
568 return 1;
569 }
570
571 return 0;
572}
573
574/* ================================================== */