/src/ntp-dev/ntpd/ntp_util.c
Line | Count | Source |
1 | | /* |
2 | | * ntp_util.c - stuff I didn't have any other place for |
3 | | */ |
4 | | #ifdef HAVE_CONFIG_H |
5 | | # include <config.h> |
6 | | #endif |
7 | | |
8 | | #include "ntpd.h" |
9 | | #include "ntp_unixtime.h" |
10 | | #include "ntp_filegen.h" |
11 | | #include "ntp_if.h" |
12 | | #include "ntp_stdlib.h" |
13 | | #include "ntp_assert.h" |
14 | | #include "ntp_calendar.h" |
15 | | #include "ntp_leapsec.h" |
16 | | |
17 | | #include <stdio.h> |
18 | | #include <ctype.h> |
19 | | #include <sys/types.h> |
20 | | #ifdef HAVE_SYS_IOCTL_H |
21 | | # include <sys/ioctl.h> |
22 | | #endif |
23 | | #ifdef HAVE_UNISTD_H |
24 | | # include <unistd.h> |
25 | | #endif |
26 | | #include <sys/stat.h> |
27 | | |
28 | | #ifdef HAVE_IEEEFP_H |
29 | | # include <ieeefp.h> |
30 | | #endif |
31 | | #ifdef HAVE_MATH_H |
32 | | # include <math.h> |
33 | | #endif |
34 | | |
35 | | #if defined(VMS) |
36 | | # include <descrip.h> |
37 | | #endif /* VMS */ |
38 | | |
39 | | /* |
40 | | * Defines used by the leapseconds stuff |
41 | | */ |
42 | | #define MAX_TAI 100 /* max TAI offset (s) */ |
43 | | #define L_DAY 86400UL /* seconds per day */ |
44 | | #define L_YEAR (L_DAY * 365) /* days per year */ |
45 | | #define L_LYEAR (L_YEAR + L_DAY) /* days per leap year */ |
46 | | #define L_4YEAR (L_LYEAR + 3 * L_YEAR) /* days per leap cycle */ |
47 | | #define L_CENT (L_4YEAR * 25) /* days per century */ |
48 | | |
49 | | /* |
50 | | * This contains odds and ends, including the hourly stats, various |
51 | | * configuration items, leapseconds stuff, etc. |
52 | | */ |
53 | | /* |
54 | | * File names |
55 | | */ |
56 | | static char *key_file_name; /* keys file name */ |
57 | | static char *leapfile_name; /* leapseconds file name */ |
58 | | static struct stat leapfile_stat; /* leapseconds file stat() buffer */ |
59 | | static int /*BOOL*/have_leapfile = FALSE; |
60 | | static int /*BOOL*/chck_leaphash = TRUE; |
61 | | char *stats_drift_file; /* frequency file name */ |
62 | | static char *stats_temp_file; /* temp frequency file name */ |
63 | | static double wander_resid; /* last frequency update */ |
64 | | double wander_threshold = 1e-7; /* initial frequency threshold */ |
65 | | |
66 | | /* |
67 | | * Statistics file stuff |
68 | | */ |
69 | | #ifndef NTP_VAR |
70 | | # ifndef SYS_WINNT |
71 | | # define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ |
72 | | # else |
73 | | # define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */ |
74 | | # endif /* SYS_WINNT */ |
75 | | #endif |
76 | | |
77 | | |
78 | | char statsdir[MAXFILENAME] = NTP_VAR; |
79 | | static FILEGEN peerstats; |
80 | | static FILEGEN loopstats; |
81 | | static FILEGEN clockstats; |
82 | | static FILEGEN rawstats; |
83 | | static FILEGEN sysstats; |
84 | | static FILEGEN protostats; |
85 | | static FILEGEN cryptostats; |
86 | | static FILEGEN timingstats; |
87 | | |
88 | | /* |
89 | | * This controls whether stats are written to the fileset. Provided |
90 | | * so that ntpdc can turn off stats when the file system fills up. |
91 | | */ |
92 | | int stats_control; |
93 | | |
94 | | /* |
95 | | * Last frequency written to file. |
96 | | */ |
97 | | static double prev_drift_comp; /* last frequency update */ |
98 | | |
99 | | /* |
100 | | * Function prototypes |
101 | | */ |
102 | | static void record_sys_stats(void); |
103 | | void ntpd_time_stepped(void); |
104 | | static void check_leap_expiration(int, uint32_t, const time_t*); |
105 | | |
106 | | /* |
107 | | * Prototypes |
108 | | */ |
109 | | #ifdef DEBUG |
110 | | void uninit_util(void); |
111 | | #endif |
112 | | |
113 | | /* |
114 | | * uninit_util - free memory allocated by init_util |
115 | | */ |
116 | | #ifdef DEBUG |
117 | | void |
118 | | uninit_util(void) |
119 | 1 | { |
120 | | #if defined(_MSC_VER) && defined (_DEBUG) |
121 | | _CrtCheckMemory(); |
122 | | #endif |
123 | 1 | if (stats_drift_file) { |
124 | 0 | free(stats_drift_file); |
125 | 0 | free(stats_temp_file); |
126 | 0 | stats_drift_file = NULL; |
127 | 0 | stats_temp_file = NULL; |
128 | 0 | } |
129 | 1 | if (key_file_name) { |
130 | 0 | free(key_file_name); |
131 | 0 | key_file_name = NULL; |
132 | 0 | } |
133 | 1 | filegen_unregister("peerstats"); |
134 | 1 | filegen_unregister("loopstats"); |
135 | 1 | filegen_unregister("clockstats"); |
136 | 1 | filegen_unregister("rawstats"); |
137 | 1 | filegen_unregister("sysstats"); |
138 | 1 | filegen_unregister("protostats"); |
139 | 1 | #ifdef AUTOKEY |
140 | 1 | filegen_unregister("cryptostats"); |
141 | 1 | #endif /* AUTOKEY */ |
142 | | #ifdef DEBUG_TIMING |
143 | | filegen_unregister("timingstats"); |
144 | | #endif /* DEBUG_TIMING */ |
145 | | |
146 | | #if defined(_MSC_VER) && defined (_DEBUG) |
147 | | _CrtCheckMemory(); |
148 | | #endif |
149 | 1 | } |
150 | | #endif /* DEBUG */ |
151 | | |
152 | | |
153 | | /* |
154 | | * init_util - initialize the util module of ntpd |
155 | | */ |
156 | | void |
157 | | init_util(void) |
158 | 1 | { |
159 | 1 | filegen_register(statsdir, "peerstats", &peerstats); |
160 | 1 | filegen_register(statsdir, "loopstats", &loopstats); |
161 | 1 | filegen_register(statsdir, "clockstats", &clockstats); |
162 | 1 | filegen_register(statsdir, "rawstats", &rawstats); |
163 | 1 | filegen_register(statsdir, "sysstats", &sysstats); |
164 | 1 | filegen_register(statsdir, "protostats", &protostats); |
165 | 1 | filegen_register(statsdir, "cryptostats", &cryptostats); |
166 | 1 | filegen_register(statsdir, "timingstats", &timingstats); |
167 | | /* |
168 | | * register with libntp ntp_set_tod() to call us back |
169 | | * when time is stepped. |
170 | | */ |
171 | 1 | step_callback = &ntpd_time_stepped; |
172 | 1 | #ifdef DEBUG |
173 | 1 | atexit(&uninit_util); |
174 | 1 | #endif /* DEBUG */ |
175 | 1 | } |
176 | | |
177 | | |
178 | | /* |
179 | | * hourly_stats - print some interesting stats |
180 | | */ |
181 | | void |
182 | | write_stats(void) |
183 | 0 | { |
184 | 0 | FILE *fp = NULL; |
185 | | #ifdef DOSYNCTODR |
186 | | struct timeval tv; |
187 | | #if !defined(VMS) |
188 | | int prio_set; |
189 | | #endif |
190 | | #ifdef HAVE_GETCLOCK |
191 | | struct timespec ts; |
192 | | #endif |
193 | | int o_prio; |
194 | | |
195 | | /* |
196 | | * Sometimes having a Sun can be a drag. |
197 | | * |
198 | | * The kernel variable dosynctodr controls whether the system's |
199 | | * soft clock is kept in sync with the battery clock. If it |
200 | | * is zero, then the soft clock is not synced, and the battery |
201 | | * clock is simply left to rot. That means that when the system |
202 | | * reboots, the battery clock (which has probably gone wacky) |
203 | | * sets the soft clock. That means ntpd starts off with a very |
204 | | * confused idea of what time it is. It then takes a large |
205 | | * amount of time to figure out just how wacky the battery clock |
206 | | * has made things drift, etc, etc. The solution is to make the |
207 | | * battery clock sync up to system time. The way to do THAT is |
208 | | * to simply set the time of day to the current time of day, but |
209 | | * as quickly as possible. This may, or may not be a sensible |
210 | | * thing to do. |
211 | | * |
212 | | * CAVEAT: settimeofday() steps the sun clock by about 800 us, |
213 | | * so setting DOSYNCTODR seems a bad idea in the |
214 | | * case of us resolution |
215 | | */ |
216 | | |
217 | | #if !defined(VMS) |
218 | | /* |
219 | | * (prr) getpriority returns -1 on error, but -1 is also a valid |
220 | | * return value (!), so instead we have to zero errno before the |
221 | | * call and check it for non-zero afterwards. |
222 | | */ |
223 | | errno = 0; |
224 | | prio_set = 0; |
225 | | o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ |
226 | | |
227 | | /* |
228 | | * (prr) if getpriority succeeded, call setpriority to raise |
229 | | * scheduling priority as high as possible. If that succeeds |
230 | | * as well, set the prio_set flag so we remember to reset |
231 | | * priority to its previous value below. Note that on Solaris |
232 | | * 2.6 (and beyond?), both getpriority and setpriority will fail |
233 | | * with ESRCH, because sched_setscheduler (called from main) put |
234 | | * us in the real-time scheduling class which setpriority |
235 | | * doesn't know about. Being in the real-time class is better |
236 | | * than anything setpriority can do, anyhow, so this error is |
237 | | * silently ignored. |
238 | | */ |
239 | | if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) |
240 | | prio_set = 1; /* overdrive */ |
241 | | #endif /* VMS */ |
242 | | #ifdef HAVE_GETCLOCK |
243 | | (void) getclock(TIMEOFDAY, &ts); |
244 | | tv.tv_sec = ts.tv_sec; |
245 | | tv.tv_usec = ts.tv_nsec / 1000; |
246 | | #else /* not HAVE_GETCLOCK */ |
247 | | GETTIMEOFDAY(&tv,(struct timezone *)NULL); |
248 | | #endif /* not HAVE_GETCLOCK */ |
249 | | if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) |
250 | | msyslog(LOG_ERR, "can't sync battery time: %m"); |
251 | | #if !defined(VMS) |
252 | | if (prio_set) |
253 | | setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ |
254 | | #endif /* VMS */ |
255 | | #endif /* DOSYNCTODR */ |
256 | 0 | record_sys_stats(); |
257 | 0 | if (stats_drift_file != 0) { |
258 | | |
259 | | /* |
260 | | * When the frequency file is written, initialize the |
261 | | * prev_drift_comp and wander_resid. Thereafter, |
262 | | * reduce the wander_resid by half each hour. When |
263 | | * the difference between the prev_drift_comp and |
264 | | * drift_comp is less than the wander_resid, update |
265 | | * the frequncy file. This minimizes the file writes to |
266 | | * nonvolaile storage. |
267 | | */ |
268 | 0 | DPRINTF(1, ("write_stats: frequency %.6f thresh %.6f, freq %.6f\n", |
269 | 0 | (prev_drift_comp - drift_comp) * 1e6, wander_resid * |
270 | 0 | 1e6, drift_comp * 1e6)); |
271 | |
|
272 | 0 | if (fabs(prev_drift_comp - drift_comp) < wander_resid) { |
273 | 0 | wander_resid *= 0.5; |
274 | 0 | return; |
275 | 0 | } |
276 | 0 | prev_drift_comp = drift_comp; |
277 | 0 | wander_resid = wander_threshold; |
278 | 0 | if ((fp = fopen(stats_temp_file, "w")) == NULL) { |
279 | 0 | msyslog(LOG_ERR, "frequency file %s: %m", |
280 | 0 | stats_temp_file); |
281 | 0 | return; |
282 | 0 | } |
283 | 0 | fprintf(fp, "%.6f\n", drift_comp * 1e6); |
284 | 0 | (void)fclose(fp); |
285 | | /* atomic */ |
286 | | #ifdef SYS_WINNT |
287 | | if (_unlink(stats_drift_file)) /* rename semantics differ under NT */ |
288 | | msyslog(LOG_WARNING, |
289 | | "Unable to remove prior drift file %s, %m", |
290 | | stats_drift_file); |
291 | | #endif /* SYS_WINNT */ |
292 | |
|
293 | 0 | #ifndef NO_RENAME |
294 | 0 | if (rename(stats_temp_file, stats_drift_file)) |
295 | 0 | msyslog(LOG_WARNING, |
296 | 0 | "Unable to rename temp drift file %s to %s, %m", |
297 | 0 | stats_temp_file, stats_drift_file); |
298 | | #else |
299 | | /* we have no rename NFS of ftp in use */ |
300 | | if ((fp = fopen(stats_drift_file, "w")) == |
301 | | NULL) { |
302 | | msyslog(LOG_ERR, |
303 | | "frequency file %s: %m", |
304 | | stats_drift_file); |
305 | | return; |
306 | | } |
307 | | #endif |
308 | |
|
309 | | #if defined(VMS) |
310 | | /* PURGE */ |
311 | | { |
312 | | $DESCRIPTOR(oldvers,";-1"); |
313 | | struct dsc$descriptor driftdsc = { |
314 | | strlen(stats_drift_file), 0, 0, |
315 | | stats_drift_file }; |
316 | | while(lib$delete_file(&oldvers, |
317 | | &driftdsc) & 1); |
318 | | } |
319 | | #endif |
320 | 0 | } |
321 | 0 | } |
322 | | |
323 | | |
324 | | /* |
325 | | * If an option was given on the command line make sure it takes |
326 | | * precedence over the configuration file, as command-line options |
327 | | * are processed first. Similarly, if an option is given in the |
328 | | * configuration file, do not allow it to be overridden with runtime |
329 | | * configuration. Done by simply remembering an option was already |
330 | | * seen. |
331 | | */ |
332 | | static int |
333 | | allow_config( |
334 | | u_int option, |
335 | | int/*BOOL*/ cmdopt |
336 | | ) |
337 | 0 | { |
338 | 0 | static u_int seen = 0; /* stat options previously set */ |
339 | 0 | u_int mask; |
340 | 0 | int retv; |
341 | |
|
342 | 0 | if (cmdopt) { |
343 | 0 | DEBUG_REQUIRE(option < sizeof(mask) * 8); |
344 | 0 | mask = 1u << option; |
345 | 0 | retv = !(seen & mask); |
346 | 0 | seen |= mask; |
347 | 0 | } else { |
348 | 0 | retv = FALSE; |
349 | 0 | } |
350 | 0 | return retv; |
351 | 0 | } |
352 | | |
353 | | |
354 | | /* |
355 | | * stats_config - configure the stats operation |
356 | | */ |
357 | | void |
358 | | stats_config( |
359 | | int item, |
360 | | const char *invalue, /* only one type so far */ |
361 | | int optflag |
362 | | ) |
363 | 0 | { |
364 | 0 | FILE *fp = NULL; |
365 | 0 | const char *value; |
366 | 0 | size_t len; |
367 | 0 | double old_drift; |
368 | 0 | l_fp now; |
369 | 0 | time_t ttnow; |
370 | 0 | char dirsep_or_nul; |
371 | 0 | #ifndef VMS |
372 | 0 | static const char temp_ext[] = ".TEMP"; |
373 | | #else |
374 | | static const char temp_ext[] = "-TEMP"; |
375 | | #endif |
376 | | |
377 | | /* |
378 | | * Expand environment strings under Windows NT, since the |
379 | | * command interpreter doesn't do this, the program must. |
380 | | */ |
381 | | #ifdef SYS_WINNT |
382 | | char newvalue[MAX_PATH], parameter[MAX_PATH]; |
383 | | |
384 | | if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) { |
385 | | switch (item) { |
386 | | case STATS_FREQ_FILE: |
387 | | strlcpy(parameter, "STATS_FREQ_FILE", |
388 | | sizeof(parameter)); |
389 | | break; |
390 | | |
391 | | case STATS_LEAP_FILE: |
392 | | strlcpy(parameter, "STATS_LEAP_FILE", |
393 | | sizeof(parameter)); |
394 | | break; |
395 | | |
396 | | case STATS_STATSDIR: |
397 | | strlcpy(parameter, "STATS_STATSDIR", |
398 | | sizeof(parameter)); |
399 | | break; |
400 | | |
401 | | case STATS_PID_FILE: |
402 | | strlcpy(parameter, "STATS_PID_FILE", |
403 | | sizeof(parameter)); |
404 | | break; |
405 | | |
406 | | default: |
407 | | strlcpy(parameter, "UNKNOWN", |
408 | | sizeof(parameter)); |
409 | | break; |
410 | | } |
411 | | value = invalue; |
412 | | msyslog(LOG_ERR, |
413 | | "ExpandEnvironmentStrings(%s) failed: %m\n", |
414 | | parameter); |
415 | | } else { |
416 | | value = newvalue; |
417 | | } |
418 | | #else |
419 | 0 | value = invalue; |
420 | 0 | #endif /* SYS_WINNT */ |
421 | |
|
422 | 0 | switch (item) { |
423 | | |
424 | | /* |
425 | | * Open and read frequency file. |
426 | | */ |
427 | 0 | case STATS_FREQ_FILE: |
428 | 0 | if (!allow_config(STATS_FREQ_FILE, optflag)) { |
429 | 0 | break; |
430 | 0 | } |
431 | 0 | if (!value || 0 == (len = strlen(value))) { |
432 | 0 | free(stats_drift_file); |
433 | 0 | free(stats_temp_file); |
434 | 0 | stats_drift_file = stats_temp_file = NULL; |
435 | 0 | } else { |
436 | 0 | stats_drift_file = erealloc(stats_drift_file, |
437 | 0 | 1 + len); |
438 | 0 | stats_temp_file = erealloc(stats_temp_file, |
439 | 0 | len + sizeof(temp_ext)); |
440 | 0 | memcpy(stats_drift_file, value, 1 + len); |
441 | 0 | memcpy(stats_temp_file, value, len); |
442 | 0 | memcpy(stats_temp_file + len, temp_ext, |
443 | 0 | sizeof(temp_ext)); |
444 | 0 | } |
445 | | |
446 | | /* |
447 | | * Open drift file and read frequency. If the file is |
448 | | * missing or contains errors, tell the loop to reset. |
449 | | */ |
450 | 0 | if (NULL == stats_drift_file) { |
451 | 0 | goto nofreq; |
452 | 0 | } else if ((fp = fopen(stats_drift_file, "r")) == NULL) { |
453 | 0 | if (errno != ENOENT) { |
454 | 0 | msyslog(LOG_WARNING, |
455 | 0 | "cannot read frequency file %s: %m", |
456 | 0 | stats_drift_file); |
457 | 0 | } |
458 | 0 | goto nofreq; |
459 | 0 | } else if (fscanf(fp, "%lf", &old_drift) != 1) { |
460 | 0 | msyslog(LOG_ERR, |
461 | 0 | "format error frequency file %s", |
462 | 0 | stats_drift_file); |
463 | 0 | nofreq: |
464 | 0 | prev_drift_comp = 0.0; |
465 | 0 | loop_config(LOOP_NOFREQ, prev_drift_comp); |
466 | 0 | } else { |
467 | 0 | loop_config(LOOP_FREQ, old_drift); |
468 | 0 | prev_drift_comp = drift_comp; |
469 | 0 | msyslog(LOG_INFO, |
470 | 0 | "initial drift restored to %.6f", |
471 | 0 | old_drift); |
472 | 0 | } |
473 | 0 | if (NULL != fp) { |
474 | 0 | fclose(fp); |
475 | 0 | } |
476 | 0 | break; |
477 | | |
478 | | /* |
479 | | * Specify statistics directory. |
480 | | */ |
481 | 0 | case STATS_STATSDIR: |
482 | 0 | if (!allow_config(STATS_STATSDIR, optflag)) { |
483 | 0 | break; |
484 | 0 | } |
485 | | /* - 2 since value may be missing the DIR_SEP. */ |
486 | 0 | len = strlen(value); |
487 | 0 | if (len > sizeof(statsdir) - 2) { |
488 | 0 | msyslog(LOG_ERR, |
489 | 0 | "statsdir %s too long (>%u)", value, |
490 | 0 | (u_int)sizeof(statsdir) - 2); |
491 | 0 | break; |
492 | 0 | } |
493 | | /* Add a DIR_SEP unless we already have one. */ |
494 | 0 | if (0 == len || DIR_SEP == value[len - 1]) { |
495 | 0 | dirsep_or_nul = '\0'; |
496 | 0 | } else { |
497 | 0 | dirsep_or_nul = DIR_SEP; |
498 | 0 | } |
499 | 0 | snprintf(statsdir, sizeof(statsdir), "%s%c", |
500 | 0 | value, dirsep_or_nul); |
501 | 0 | filegen_statsdir(); |
502 | 0 | break; |
503 | | |
504 | | /* |
505 | | * Write pid file. |
506 | | */ |
507 | 0 | case STATS_PID_FILE: |
508 | 0 | if (!allow_config(STATS_PID_FILE, optflag)) { |
509 | 0 | break; |
510 | 0 | } |
511 | 0 | if ((fp = fopen(value, "w")) == NULL) { |
512 | 0 | msyslog(LOG_ERR, "pid file %s: %m", value); |
513 | 0 | break; |
514 | 0 | } |
515 | 0 | fprintf(fp, "%ld", (long)getpid()); |
516 | 0 | fclose(fp); |
517 | 0 | break; |
518 | | |
519 | | /* |
520 | | * Read leapseconds file. |
521 | | * |
522 | | * By default a leap file without SHA1 signature is accepted, |
523 | | * but if there is a signature line, the signature must be |
524 | | * valid or the leapfile line in ntp.conf must have ignorehash. |
525 | | */ |
526 | 0 | case STATS_LEAP_FILE: |
527 | 0 | if (NULL == value || 0 == (len = strlen(value))) { |
528 | 0 | break; |
529 | 0 | } |
530 | 0 | leapfile_name = erealloc(leapfile_name, len + 1); |
531 | 0 | memcpy(leapfile_name, value, len + 1); |
532 | 0 | chck_leaphash = optflag; |
533 | |
|
534 | 0 | if (leapsec_load_file( |
535 | 0 | leapfile_name, &leapfile_stat, |
536 | 0 | TRUE, TRUE, chck_leaphash)) { |
537 | 0 | leap_signature_t lsig; |
538 | |
|
539 | 0 | get_systime(&now); |
540 | 0 | time(&ttnow); |
541 | 0 | leapsec_getsig(&lsig); |
542 | 0 | mprintf_event(EVNT_TAI, NULL, |
543 | 0 | "%d leap %s expire%s %s", |
544 | 0 | lsig.taiof, |
545 | 0 | fstostr(lsig.ttime), |
546 | 0 | leapsec_expired(now.l_ui, NULL) |
547 | 0 | ? "d" |
548 | 0 | : "s", |
549 | 0 | fstostr(lsig.etime)); |
550 | |
|
551 | 0 | have_leapfile = TRUE; |
552 | | |
553 | | /* force an immediate daily expiration check of |
554 | | * the leap seconds table |
555 | | */ |
556 | 0 | check_leap_expiration(TRUE, now.l_ui, &ttnow); |
557 | 0 | } |
558 | 0 | break; |
559 | | |
560 | 0 | default: |
561 | | /* oh well */ |
562 | 0 | break; |
563 | 0 | } |
564 | 0 | } |
565 | | |
566 | | |
567 | | /* |
568 | | * record_peer_stats - write peer statistics to file |
569 | | * |
570 | | * file format: |
571 | | * day (MJD) |
572 | | * time (s past UTC midnight) |
573 | | * IP address |
574 | | * status word (hex) |
575 | | * offset |
576 | | * delay |
577 | | * dispersion |
578 | | * jitter |
579 | | */ |
580 | | void |
581 | | record_peer_stats( |
582 | | sockaddr_u * addr, |
583 | | int status, |
584 | | double offset, |
585 | | double delay, |
586 | | double dispersion, |
587 | | double jitter |
588 | | ) |
589 | 0 | { |
590 | 0 | l_fp now; |
591 | 0 | u_long day; |
592 | |
|
593 | 0 | if (!stats_control) { |
594 | 0 | return; |
595 | 0 | } |
596 | 0 | get_systime(&now); |
597 | 0 | filegen_setup(&peerstats, now.l_ui); |
598 | 0 | if (NULL == peerstats.fp) { |
599 | 0 | return; |
600 | 0 | } |
601 | 0 | day = now.l_ui / 86400 + MJD_1900; |
602 | 0 | now.l_ui %= 86400; |
603 | 0 | fprintf(peerstats.fp, |
604 | 0 | "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day, |
605 | 0 | ulfptoa(&now, 3), stoa(addr), status, offset, |
606 | 0 | delay, dispersion, jitter); |
607 | 0 | fflush(peerstats.fp); |
608 | 0 | } |
609 | | |
610 | | |
611 | | /* |
612 | | * record_loop_stats - write loop filter statistics to file |
613 | | * |
614 | | * file format: |
615 | | * day (MJD) |
616 | | * time (s past midnight) |
617 | | * offset |
618 | | * frequency (PPM) |
619 | | * jitter |
620 | | * wnder (PPM) |
621 | | * time constant (log2) |
622 | | */ |
623 | | void |
624 | | record_loop_stats( |
625 | | double offset, /* offset */ |
626 | | double freq, /* frequency (PPM) */ |
627 | | double jitter, /* jitter */ |
628 | | double wander, /* wander (PPM) */ |
629 | | int spoll |
630 | | ) |
631 | 0 | { |
632 | 0 | l_fp now; |
633 | 0 | u_long day; |
634 | |
|
635 | 0 | if (!stats_control) |
636 | 0 | return; |
637 | | |
638 | 0 | get_systime(&now); |
639 | 0 | filegen_setup(&loopstats, now.l_ui); |
640 | 0 | day = now.l_ui / 86400 + MJD_1900; |
641 | 0 | now.l_ui %= 86400; |
642 | 0 | if (loopstats.fp != NULL) { |
643 | 0 | fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n", |
644 | 0 | day, ulfptoa(&now, 3), offset, freq * 1e6, jitter, |
645 | 0 | wander * 1e6, spoll); |
646 | 0 | fflush(loopstats.fp); |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | |
651 | | /* |
652 | | * record_clock_stats - write clock statistics to file |
653 | | * |
654 | | * file format: |
655 | | * day (MJD) |
656 | | * time (s past midnight) |
657 | | * IP address |
658 | | * text message |
659 | | */ |
660 | | void |
661 | | record_clock_stats( |
662 | | sockaddr_u *addr, |
663 | | const char *text /* timecode string */ |
664 | | ) |
665 | 0 | { |
666 | 0 | l_fp now; |
667 | 0 | u_long day; |
668 | |
|
669 | 0 | if (!stats_control) |
670 | 0 | return; |
671 | | |
672 | 0 | get_systime(&now); |
673 | 0 | filegen_setup(&clockstats, now.l_ui); |
674 | 0 | day = now.l_ui / 86400 + MJD_1900; |
675 | 0 | now.l_ui %= 86400; |
676 | 0 | if (clockstats.fp != NULL) { |
677 | 0 | fprintf(clockstats.fp, "%lu %s %s %s\n", day, |
678 | 0 | ulfptoa(&now, 3), stoa(addr), text); |
679 | 0 | fflush(clockstats.fp); |
680 | 0 | } |
681 | 0 | } |
682 | | |
683 | | |
684 | | /* |
685 | | * mprintf_clock_stats - write clock statistics to file with |
686 | | * msnprintf-style formatting. |
687 | | */ |
688 | | int |
689 | | mprintf_clock_stats( |
690 | | sockaddr_u *addr, |
691 | | const char *fmt, |
692 | | ... |
693 | | ) |
694 | 0 | { |
695 | 0 | va_list ap; |
696 | 0 | int rc; |
697 | 0 | char msg[512]; |
698 | |
|
699 | 0 | va_start(ap, fmt); |
700 | 0 | rc = mvsnprintf(msg, sizeof(msg), fmt, ap); |
701 | 0 | va_end(ap); |
702 | 0 | if (stats_control) |
703 | 0 | record_clock_stats(addr, msg); |
704 | |
|
705 | 0 | return rc; |
706 | 0 | } |
707 | | |
708 | | /* |
709 | | * record_raw_stats - write raw timestamps to file |
710 | | * |
711 | | * file format |
712 | | * day (MJD) |
713 | | * time (s past midnight) |
714 | | * peer ip address |
715 | | * IP address |
716 | | * t1 t2 t3 t4 timestamps |
717 | | * leap, version, mode, stratum, ppoll, precision, root delay, root dispersion, REFID |
718 | | * length and hex dump of any EFs and any legacy MAC. |
719 | | */ |
720 | | void |
721 | | record_raw_stats( |
722 | | sockaddr_u *srcadr, |
723 | | sockaddr_u *dstadr, |
724 | | l_fp *t1, /* originate timestamp */ |
725 | | l_fp *t2, /* receive timestamp */ |
726 | | l_fp *t3, /* transmit timestamp */ |
727 | | l_fp *t4, /* destination timestamp */ |
728 | | int leap, |
729 | | int version, |
730 | | int mode, |
731 | | int stratum, |
732 | | int ppoll, |
733 | | int precision, |
734 | | double root_delay, /* seconds */ |
735 | | double root_dispersion,/* seconds */ |
736 | | u_int32 refid, |
737 | | int len, |
738 | | u_char *extra |
739 | | ) |
740 | 0 | { |
741 | 0 | l_fp now; |
742 | 0 | u_long day; |
743 | |
|
744 | 0 | if (!stats_control) |
745 | 0 | return; |
746 | | |
747 | | /* |
748 | | * Mode 6 and mode 7 packets do not have the format of normal |
749 | | * NTP packets and will log garbage. So don't. [Bug 3774] |
750 | | */ |
751 | 0 | if (MODE_CONTROL == mode || MODE_PRIVATE == mode) |
752 | 0 | return; |
753 | | |
754 | 0 | get_systime(&now); |
755 | 0 | filegen_setup(&rawstats, now.l_ui); |
756 | 0 | day = now.l_ui / 86400 + MJD_1900; |
757 | 0 | now.l_ui %= 86400; |
758 | 0 | if (rawstats.fp != NULL) { |
759 | 0 | fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s", |
760 | 0 | day, ulfptoa(&now, 3), |
761 | 0 | srcadr ? stoa(srcadr) : "-", |
762 | 0 | dstadr ? stoa(dstadr) : "-", |
763 | 0 | ulfptoa(t1, 9), ulfptoa(t2, 9), |
764 | 0 | ulfptoa(t3, 9), ulfptoa(t4, 9), |
765 | 0 | leap, version, mode, stratum, ppoll, precision, |
766 | 0 | root_delay, root_dispersion, refid_str(refid, stratum)); |
767 | 0 | if (len > 0) { |
768 | 0 | int i; |
769 | |
|
770 | 0 | fprintf(rawstats.fp, " %d: ", len); |
771 | 0 | for (i = 0; i < len; ++i) { |
772 | 0 | fprintf(rawstats.fp, "%02x", extra[i]); |
773 | 0 | } |
774 | 0 | } |
775 | 0 | fprintf(rawstats.fp, "\n"); |
776 | 0 | fflush(rawstats.fp); |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | | |
781 | | /* |
782 | | * record_sys_stats - write system statistics to file |
783 | | * |
784 | | * file format |
785 | | * day (MJD) |
786 | | * time (s past midnight) |
787 | | * time since reset |
788 | | * packets recieved |
789 | | * packets for this host |
790 | | * current version |
791 | | * old version |
792 | | * access denied |
793 | | * bad length or format |
794 | | * bad authentication |
795 | | * declined |
796 | | * rate exceeded |
797 | | * KoD sent |
798 | | */ |
799 | | void |
800 | | record_sys_stats(void) |
801 | 0 | { |
802 | 0 | l_fp now; |
803 | 0 | u_long day; |
804 | |
|
805 | 0 | if (!stats_control) |
806 | 0 | return; |
807 | | |
808 | 0 | get_systime(&now); |
809 | 0 | filegen_setup(&sysstats, now.l_ui); |
810 | 0 | day = now.l_ui / 86400 + MJD_1900; |
811 | 0 | now.l_ui %= 86400; |
812 | 0 | if (sysstats.fp != NULL) { |
813 | 0 | fprintf(sysstats.fp, |
814 | 0 | "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", |
815 | 0 | day, ulfptoa(&now, 3), current_time - sys_stattime, |
816 | 0 | sys_received, sys_processed, sys_newversion, |
817 | 0 | sys_oldversion, sys_restricted, sys_badlength, |
818 | 0 | sys_badauth, sys_declined, sys_limitrejected, |
819 | 0 | sys_kodsent); |
820 | 0 | fflush(sysstats.fp); |
821 | 0 | proto_clr_stats(); |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | |
826 | | /* |
827 | | * record_proto_stats - write system statistics to file |
828 | | * |
829 | | * file format |
830 | | * day (MJD) |
831 | | * time (s past midnight) |
832 | | * text message |
833 | | */ |
834 | | void |
835 | | record_proto_stats( |
836 | | char *str /* text string */ |
837 | | ) |
838 | 0 | { |
839 | 0 | l_fp now; |
840 | 0 | u_long day; |
841 | |
|
842 | 0 | if (!stats_control) |
843 | 0 | return; |
844 | | |
845 | 0 | get_systime(&now); |
846 | 0 | filegen_setup(&protostats, now.l_ui); |
847 | 0 | day = now.l_ui / 86400 + MJD_1900; |
848 | 0 | now.l_ui %= 86400; |
849 | 0 | if (protostats.fp != NULL) { |
850 | 0 | fprintf(protostats.fp, "%lu %s %s\n", day, |
851 | 0 | ulfptoa(&now, 3), str); |
852 | 0 | fflush(protostats.fp); |
853 | 0 | } |
854 | 0 | } |
855 | | |
856 | | |
857 | | #ifdef AUTOKEY |
858 | | /* |
859 | | * record_crypto_stats - write crypto statistics to file |
860 | | * |
861 | | * file format: |
862 | | * day (mjd) |
863 | | * time (s past midnight) |
864 | | * peer ip address |
865 | | * text message |
866 | | */ |
867 | | void |
868 | | record_crypto_stats( |
869 | | sockaddr_u *addr, |
870 | | const char *text /* text message */ |
871 | | ) |
872 | 0 | { |
873 | 0 | l_fp now; |
874 | 0 | u_long day; |
875 | |
|
876 | 0 | if (!stats_control) |
877 | 0 | return; |
878 | | |
879 | 0 | get_systime(&now); |
880 | 0 | filegen_setup(&cryptostats, now.l_ui); |
881 | 0 | day = now.l_ui / 86400 + MJD_1900; |
882 | 0 | now.l_ui %= 86400; |
883 | 0 | if (cryptostats.fp != NULL) { |
884 | 0 | if (addr == NULL) |
885 | 0 | fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n", |
886 | 0 | day, ulfptoa(&now, 3), text); |
887 | 0 | else |
888 | 0 | fprintf(cryptostats.fp, "%lu %s %s %s\n", |
889 | 0 | day, ulfptoa(&now, 3), stoa(addr), text); |
890 | 0 | fflush(cryptostats.fp); |
891 | 0 | } |
892 | 0 | } |
893 | | #endif /* AUTOKEY */ |
894 | | |
895 | | |
896 | | #ifdef DEBUG_TIMING |
897 | | /* |
898 | | * record_timing_stats - write timing statistics to file |
899 | | * |
900 | | * file format: |
901 | | * day (mjd) |
902 | | * time (s past midnight) |
903 | | * text message |
904 | | */ |
905 | | void |
906 | | record_timing_stats( |
907 | | const char *text /* text message */ |
908 | | ) |
909 | | { |
910 | | static unsigned int flshcnt; |
911 | | l_fp now; |
912 | | u_long day; |
913 | | |
914 | | if (!stats_control) |
915 | | return; |
916 | | |
917 | | get_systime(&now); |
918 | | filegen_setup(&timingstats, now.l_ui); |
919 | | day = now.l_ui / 86400 + MJD_1900; |
920 | | now.l_ui %= 86400; |
921 | | if (timingstats.fp != NULL) { |
922 | | fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now, |
923 | | 3), text); |
924 | | if (++flshcnt % 100 == 0) |
925 | | fflush(timingstats.fp); |
926 | | } |
927 | | } |
928 | | #endif |
929 | | |
930 | | |
931 | | /* |
932 | | * check_leap_file - See if the leapseconds file has been updated. |
933 | | * |
934 | | * Returns: n/a |
935 | | * |
936 | | * Note: This loads a new leapfile on the fly. Currently a leap file |
937 | | * without SHA1 signature is accepted, but if there is a signature line, |
938 | | * the signature must be valid unless the ntp.conf leapfile line specified |
939 | | * ignorehash. |
940 | | */ |
941 | | void |
942 | | check_leap_file( |
943 | | int is_daily_check, |
944 | | uint32_t ntptime, |
945 | | const time_t * systime |
946 | | ) |
947 | 0 | { |
948 | | /* just do nothing if there is no leap file */ |
949 | 0 | if ( ! (leapfile_name && *leapfile_name)) |
950 | 0 | return; |
951 | | |
952 | | /* try to load leapfile, force it if no leapfile loaded yet */ |
953 | 0 | if (leapsec_load_file( |
954 | 0 | leapfile_name, &leapfile_stat, |
955 | 0 | !have_leapfile, is_daily_check, chck_leaphash)) |
956 | 0 | have_leapfile = TRUE; |
957 | 0 | else if (!have_leapfile) |
958 | 0 | return; |
959 | | |
960 | 0 | check_leap_expiration(is_daily_check, ntptime, systime); |
961 | 0 | } |
962 | | |
963 | | /* |
964 | | * check expiration of a loaded leap table |
965 | | */ |
966 | | static void |
967 | | check_leap_expiration( |
968 | | int is_daily_check, |
969 | | uint32_t ntptime , |
970 | | const time_t *systime |
971 | | ) |
972 | 0 | { |
973 | 0 | static const char * const logPrefix = "leapsecond file"; |
974 | 0 | int rc; |
975 | | |
976 | | /* test the expiration of the leap data and log with proper |
977 | | * level and frequency (once/hour or once/day, depending on the |
978 | | * state. |
979 | | */ |
980 | 0 | rc = leapsec_daystolive(ntptime, systime); |
981 | 0 | if (rc == 0) { |
982 | 0 | msyslog(LOG_WARNING, |
983 | 0 | "%s ('%s'): will expire in less than one day", |
984 | 0 | logPrefix, leapfile_name); |
985 | 0 | } else if (is_daily_check && rc < 28) { |
986 | 0 | if (rc < 0) |
987 | 0 | msyslog(LOG_ERR, |
988 | 0 | "%s ('%s'): expired %d day%s ago", |
989 | 0 | logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s")); |
990 | 0 | else |
991 | 0 | msyslog(LOG_WARNING, |
992 | 0 | "%s ('%s'): will expire in less than %d days", |
993 | 0 | logPrefix, leapfile_name, 1+rc); |
994 | 0 | } |
995 | 0 | } |
996 | | |
997 | | |
998 | | /* |
999 | | * getauthkeys - read the authentication keys from the specified file |
1000 | | */ |
1001 | | void |
1002 | | getauthkeys( |
1003 | | const char *keyfile |
1004 | | ) |
1005 | 0 | { |
1006 | 0 | size_t len; |
1007 | |
|
1008 | 0 | len = strlen(keyfile); |
1009 | 0 | if (!len) |
1010 | 0 | return; |
1011 | | |
1012 | 0 | #ifndef SYS_WINNT |
1013 | 0 | key_file_name = erealloc(key_file_name, len + 1); |
1014 | 0 | memcpy(key_file_name, keyfile, len + 1); |
1015 | | #else |
1016 | | key_file_name = erealloc(key_file_name, _MAX_PATH); |
1017 | | if (len + 1 > _MAX_PATH) |
1018 | | return; |
1019 | | if (!ExpandEnvironmentStrings(keyfile, key_file_name, |
1020 | | _MAX_PATH)) { |
1021 | | msyslog(LOG_ERR, |
1022 | | "ExpandEnvironmentStrings(KEY_FILE) failed: %m"); |
1023 | | strlcpy(key_file_name, keyfile, _MAX_PATH); |
1024 | | } |
1025 | | key_file_name = erealloc(key_file_name, |
1026 | | 1 + strlen(key_file_name)); |
1027 | | #endif /* SYS_WINNT */ |
1028 | |
|
1029 | 0 | authreadkeys(key_file_name); |
1030 | 0 | } |
1031 | | |
1032 | | |
1033 | | /* |
1034 | | * rereadkeys - read the authentication key file over again. |
1035 | | */ |
1036 | | void |
1037 | | rereadkeys(void) |
1038 | 0 | { |
1039 | 0 | if (NULL != key_file_name) |
1040 | 0 | authreadkeys(key_file_name); |
1041 | 0 | } |
1042 | | |
1043 | | |
1044 | | #if notyet |
1045 | | /* |
1046 | | * ntp_exit - document explicitly that ntpd has exited |
1047 | | */ |
1048 | | void |
1049 | | ntp_exit(int retval) |
1050 | | { |
1051 | | msyslog(LOG_ERR, "EXITING with return code %d", retval); |
1052 | | exit(retval); |
1053 | | } |
1054 | | #endif |
1055 | | |
1056 | | /* |
1057 | | * fstostr - prettyprint NTP seconds |
1058 | | */ |
1059 | | char * fstostr( |
1060 | | time_t ntp_stamp |
1061 | | ) |
1062 | 0 | { |
1063 | 0 | char * buf; |
1064 | 0 | struct calendar tm; |
1065 | |
|
1066 | 0 | LIB_GETBUF(buf); |
1067 | 0 | if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0) |
1068 | 0 | snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error", |
1069 | 0 | (long)ntp_stamp); |
1070 | 0 | else |
1071 | 0 | snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d", |
1072 | 0 | tm.year, tm.month, tm.monthday, |
1073 | 0 | tm.hour, tm.minute); |
1074 | 0 | return buf; |
1075 | 0 | } |
1076 | | |
1077 | | |
1078 | | /* |
1079 | | * ntpd_time_stepped is called back by step_systime(), allowing ntpd |
1080 | | * to do any one-time processing necessitated by the step. |
1081 | | */ |
1082 | | void |
1083 | | ntpd_time_stepped(void) |
1084 | 0 | { |
1085 | 0 | u_int saved_mon_enabled; |
1086 | | |
1087 | | /* |
1088 | | * flush the monitor MRU list which contains l_fp timestamps |
1089 | | * which should not be compared across the step. |
1090 | | */ |
1091 | 0 | if (MON_OFF != mon_enabled) { |
1092 | 0 | saved_mon_enabled = mon_enabled; |
1093 | 0 | mon_stop(MON_OFF); |
1094 | 0 | mon_start(saved_mon_enabled); |
1095 | 0 | } |
1096 | | |
1097 | | /* inform interpolating Windows code to allow time to go back */ |
1098 | | #ifdef SYS_WINNT |
1099 | | win_time_stepped(); |
1100 | | #endif |
1101 | 0 | } |
1102 | | |
1103 | | |
1104 | | #ifdef DEBUG |
1105 | | void |
1106 | | append_flagstr( |
1107 | | char * flagstr, |
1108 | | size_t sz, |
1109 | | const char * text |
1110 | | ) |
1111 | 0 | { |
1112 | 0 | if ('\0' != flagstr[0]) { |
1113 | 0 | strlcat(flagstr, ",", sz); |
1114 | 0 | } |
1115 | | /* bail if we ran out of room */ |
1116 | 0 | INSIST(strlcat(flagstr, text, sz) < sz); |
1117 | 0 | } |
1118 | | #endif /* DEBUG */ |