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