1 /* 2 * ntp_util.c - stuff I didn't have any other place for 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #include "ntpd.h" 10 #include "ntp_io.h" 11 #include "ntp_unixtime.h" 12 #include "ntp_filegen.h" 13 #include "ntp_if.h" 14 #include "ntp_stdlib.h" 15 16 #include <stdio.h> 17 #include <ctype.h> 18 #include <sys/types.h> 19 #ifdef HAVE_SYS_IOCTL_H 20 # include <sys/ioctl.h> 21 #endif 22 23 #ifdef HAVE_IEEEFP_H 24 # include <ieeefp.h> 25 #endif 26 #ifdef HAVE_MATH_H 27 # include <math.h> 28 #endif 29 30 #ifdef DOSYNCTODR 31 #if !defined(VMS) 32 #include <sys/resource.h> 33 #endif /* VMS */ 34 #endif 35 36 #if defined(VMS) 37 #include <descrip.h> 38 #endif /* VMS */ 39 40 /* 41 * This contains odds and ends. Right now the only thing you'll find 42 * in here is the hourly stats printer and some code to support rereading 43 * the keys file, but I may eventually put other things in here such as 44 * code to do something with the leap bits. 45 */ 46 47 /* 48 * Name of the keys file 49 */ 50 static char *key_file_name; 51 52 /* 53 * The name of the drift_comp file and the temporary. 54 */ 55 static char *stats_drift_file; 56 static char *stats_temp_file; 57 58 /* 59 * Statistics file stuff 60 */ 61 #ifndef NTP_VAR 62 #ifndef SYS_WINNT 63 #define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ 64 #else 65 #define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */ 66 #endif /* SYS_WINNT */ 67 #endif 68 69 #ifndef MAXPATHLEN 70 #define MAXPATHLEN 256 71 #endif 72 73 static char statsdir[MAXPATHLEN] = NTP_VAR; 74 75 static FILEGEN peerstats; 76 static FILEGEN loopstats; 77 static FILEGEN clockstats; 78 static FILEGEN rawstats; 79 80 /* 81 * This controls whether stats are written to the fileset. Provided 82 * so that ntpdc can turn off stats when the file system fills up. 83 */ 84 int stats_control; 85 86 /* 87 * init_util - initialize the utilities 88 */ 89 void 90 init_util(void) 91 { 92 stats_drift_file = 0; 93 stats_temp_file = 0; 94 key_file_name = 0; 95 96 #define PEERNAME "peerstats" 97 #define LOOPNAME "loopstats" 98 #define CLOCKNAME "clockstats" 99 #define RAWNAME "rawstats" 100 peerstats.fp = NULL; 101 peerstats.prefix = &statsdir[0]; 102 peerstats.basename = (char*)emalloc(strlen(PEERNAME)+1); 103 strcpy(peerstats.basename, PEERNAME); 104 peerstats.id = 0; 105 peerstats.type = FILEGEN_DAY; 106 peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 107 filegen_register("peerstats", &peerstats); 108 109 loopstats.fp = NULL; 110 loopstats.prefix = &statsdir[0]; 111 loopstats.basename = (char*)emalloc(strlen(LOOPNAME)+1); 112 strcpy(loopstats.basename, LOOPNAME); 113 loopstats.id = 0; 114 loopstats.type = FILEGEN_DAY; 115 loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 116 filegen_register("loopstats", &loopstats); 117 118 clockstats.fp = NULL; 119 clockstats.prefix = &statsdir[0]; 120 clockstats.basename = (char*)emalloc(strlen(CLOCKNAME)+1); 121 strcpy(clockstats.basename, CLOCKNAME); 122 clockstats.id = 0; 123 clockstats.type = FILEGEN_DAY; 124 clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 125 filegen_register("clockstats", &clockstats); 126 127 rawstats.fp = NULL; 128 rawstats.prefix = &statsdir[0]; 129 rawstats.basename = (char*)emalloc(strlen(RAWNAME)+1); 130 strcpy(rawstats.basename, RAWNAME); 131 rawstats.id = 0; 132 rawstats.type = FILEGEN_DAY; 133 rawstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ 134 filegen_register("rawstats", &rawstats); 135 136 #undef PEERNAME 137 #undef LOOPNAME 138 #undef CLOCKNAME 139 #undef RAWNAME 140 141 } 142 143 144 /* 145 * hourly_stats - print some interesting stats 146 */ 147 void 148 hourly_stats(void) 149 { 150 FILE *fp; 151 152 #ifdef DOSYNCTODR 153 struct timeval tv; 154 #if !defined(VMS) 155 int prio_set; 156 #endif 157 #ifdef HAVE_GETCLOCK 158 struct timespec ts; 159 #endif 160 int o_prio; 161 162 /* 163 * Sometimes having a Sun can be a drag. 164 * 165 * The kernel variable dosynctodr controls whether the system's 166 * soft clock is kept in sync with the battery clock. If it 167 * is zero, then the soft clock is not synced, and the battery 168 * clock is simply left to rot. That means that when the system 169 * reboots, the battery clock (which has probably gone wacky) 170 * sets the soft clock. That means ntpd starts off with a very 171 * confused idea of what time it is. It then takes a large 172 * amount of time to figure out just how wacky the battery clock 173 * has made things drift, etc, etc. The solution is to make the 174 * battery clock sync up to system time. The way to do THAT is 175 * to simply set the time of day to the current time of day, but 176 * as quickly as possible. This may, or may not be a sensible 177 * thing to do. 178 * 179 * CAVEAT: settimeofday() steps the sun clock by about 800 us, 180 * so setting DOSYNCTODR seems a bad idea in the 181 * case of us resolution 182 */ 183 184 #if !defined(VMS) 185 /* (prr) getpriority returns -1 on error, but -1 is also a valid 186 * return value (!), so instead we have to zero errno before the call 187 * and check it for non-zero afterwards. 188 */ 189 190 errno = 0; 191 prio_set = 0; 192 o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ 193 194 /* (prr) if getpriority succeeded, call setpriority to raise 195 * scheduling priority as high as possible. If that succeeds 196 * as well, set the prio_set flag so we remember to reset 197 * priority to its previous value below. Note that on Solaris 2.6 198 * (and beyond?), both getpriority and setpriority will fail with 199 * ESRCH, because sched_setscheduler (called from main) put us in 200 * the real-time scheduling class which setpriority doesn't know about. 201 * Being in the real-time class is better than anything setpriority 202 * can do, anyhow, so this error is silently ignored. 203 */ 204 205 if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) 206 prio_set = 1; /* overdrive */ 207 #endif /* VMS */ 208 #ifdef HAVE_GETCLOCK 209 (void) getclock(TIMEOFDAY, &ts); 210 tv.tv_sec = ts.tv_sec; 211 tv.tv_usec = ts.tv_nsec / 1000; 212 #else /* not HAVE_GETCLOCK */ 213 GETTIMEOFDAY(&tv,(struct timezone *)NULL); 214 #endif /* not HAVE_GETCLOCK */ 215 if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) 216 { 217 msyslog(LOG_ERR, "can't sync battery time: %m"); 218 } 219 #if !defined(VMS) 220 if (prio_set) 221 setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ 222 #endif /* VMS */ 223 #endif /* DOSYNCTODR */ 224 225 NLOG(NLOG_SYSSTATIST) 226 msyslog(LOG_INFO, 227 "offset %.6f sec freq %.3f ppm error %.6f poll %d", 228 last_offset, drift_comp * 1e6, sys_jitter, sys_poll); 229 230 231 if (stats_drift_file != 0) { 232 if ((fp = fopen(stats_temp_file, "w")) == NULL) { 233 msyslog(LOG_ERR, "can't open %s: %m", 234 stats_temp_file); 235 return; 236 } 237 fprintf(fp, "%.3f\n", drift_comp * 1e6); 238 (void)fclose(fp); 239 /* atomic */ 240 #ifdef SYS_WINNT 241 (void) unlink(stats_drift_file); /* rename semantics differ under NT */ 242 #endif /* SYS_WINNT */ 243 244 #ifndef NO_RENAME 245 (void) rename(stats_temp_file, stats_drift_file); 246 #else 247 /* we have no rename NFS of ftp in use*/ 248 if ((fp = fopen(stats_drift_file, "w")) == NULL) { 249 msyslog(LOG_ERR, "can't open %s: %m", 250 stats_drift_file); 251 return; 252 } 253 254 #endif 255 256 #if defined(VMS) 257 /* PURGE */ 258 { 259 $DESCRIPTOR(oldvers,";-1"); 260 struct dsc$descriptor driftdsc = { 261 strlen(stats_drift_file),0,0,stats_drift_file }; 262 263 while(lib$delete_file(&oldvers,&driftdsc) & 1) ; 264 } 265 #endif 266 } 267 } 268 269 270 /* 271 * stats_config - configure the stats operation 272 */ 273 void 274 stats_config( 275 int item, 276 char *invalue /* only one type so far */ 277 ) 278 { 279 FILE *fp; 280 char *value; 281 double old_drift; 282 int len; 283 284 /* Expand environment strings under Windows NT, since the command 285 * interpreter doesn't do this, the program must. 286 */ 287 #ifdef SYS_WINNT 288 char newvalue[MAX_PATH], parameter[MAX_PATH]; 289 290 if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) 291 { 292 switch(item) { 293 case STATS_FREQ_FILE: 294 strcpy(parameter,"STATS_FREQ_FILE"); 295 break; 296 case STATS_STATSDIR: 297 strcpy(parameter,"STATS_STATSDIR"); 298 break; 299 case STATS_PID_FILE: 300 strcpy(parameter,"STATS_PID_FILE"); 301 break; 302 default: 303 strcpy(parameter,"UNKNOWN"); 304 break; 305 } 306 value = invalue; 307 308 msyslog(LOG_ERR, 309 "ExpandEnvironmentStrings(%s) failed: %m\n", parameter); 310 } 311 else 312 value = newvalue; 313 #else 314 value = invalue; 315 #endif /* SYS_WINNT */ 316 317 318 319 switch(item) { 320 case STATS_FREQ_FILE: 321 if (stats_drift_file != 0) { 322 (void) free(stats_drift_file); 323 (void) free(stats_temp_file); 324 stats_drift_file = 0; 325 stats_temp_file = 0; 326 } 327 328 if (value == 0 || (len = strlen(value)) == 0) 329 break; 330 331 stats_drift_file = (char*)emalloc((u_int)(len + 1)); 332 #if !defined(VMS) 333 stats_temp_file = (char*)emalloc((u_int)(len + sizeof(".TEMP"))); 334 #else 335 stats_temp_file = (char*)emalloc((u_int)(len + sizeof("-TEMP"))); 336 #endif /* VMS */ 337 memmove(stats_drift_file, value, (unsigned)(len+1)); 338 memmove(stats_temp_file, value, (unsigned)len); 339 #if !defined(VMS) 340 memmove(stats_temp_file + len, ".TEMP", sizeof(".TEMP")); 341 #else 342 memmove(stats_temp_file + len, "-TEMP", sizeof("-TEMP")); 343 #endif /* VMS */ 344 345 /* 346 * Open drift file and read frequency 347 */ 348 if ((fp = fopen(stats_drift_file, "r")) == NULL) { 349 break; 350 } 351 if (fscanf(fp, "%lf", &old_drift) != 1) { 352 msyslog(LOG_ERR, "Un-parsable frequency in %s", 353 stats_drift_file); 354 (void) fclose(fp); 355 break; 356 } 357 (void) fclose(fp); 358 if ( 359 #ifdef HAVE_FINITE 360 !finite(old_drift) 361 #else /* not HAVE_FINITE */ 362 # ifdef HAVE_ISFINITE 363 !isfinite(old_drift) 364 # else /* not HAVE_ISFINITE */ 365 0 366 # endif /* not HAVE_ISFINITE */ 367 #endif /* not HAVE_FINITE */ 368 || (fabs(old_drift) > (NTP_MAXFREQ * 1e6))) { 369 msyslog(LOG_ERR, "invalid frequency (%f) in %s", 370 old_drift, stats_drift_file); 371 old_drift = 0.0; 372 } 373 msyslog(LOG_INFO, "frequency initialized %.3f from %s", 374 old_drift, stats_drift_file); 375 loop_config(LOOP_DRIFTCOMP, old_drift / 1e6); 376 break; 377 378 case STATS_STATSDIR: 379 if (strlen(value) >= sizeof(statsdir)) { 380 msyslog(LOG_ERR, 381 "value for statsdir too long (>%d, sigh)", 382 (int)sizeof(statsdir)-1); 383 } else { 384 l_fp now; 385 386 get_systime(&now); 387 strcpy(statsdir,value); 388 if(peerstats.prefix == &statsdir[0] && 389 peerstats.fp != NULL) { 390 fclose(peerstats.fp); 391 peerstats.fp = NULL; 392 filegen_setup(&peerstats, now.l_ui); 393 } 394 if(loopstats.prefix == &statsdir[0] && 395 loopstats.fp != NULL) { 396 fclose(loopstats.fp); 397 loopstats.fp = NULL; 398 filegen_setup(&loopstats, now.l_ui); 399 } 400 if(clockstats.prefix == &statsdir[0] && 401 clockstats.fp != NULL) { 402 fclose(clockstats.fp); 403 clockstats.fp = NULL; 404 filegen_setup(&clockstats, now.l_ui); 405 } 406 if(rawstats.prefix == &statsdir[0] && 407 rawstats.fp != NULL) { 408 fclose(rawstats.fp); 409 rawstats.fp = NULL; 410 filegen_setup(&rawstats, now.l_ui); 411 } 412 } 413 break; 414 415 case STATS_PID_FILE: 416 if ((fp = fopen(value, "w")) == NULL) { 417 msyslog(LOG_ERR, "Can't open %s: %m", value); 418 break; 419 } 420 fprintf(fp, "%d", (int) getpid()); 421 fclose(fp);; 422 break; 423 424 default: 425 /* oh well */ 426 break; 427 } 428 } 429 430 /* 431 * record_peer_stats - write peer statistics to file 432 * 433 * file format: 434 * day (mjd) 435 * time (s past UTC midnight) 436 * peer (ip address) 437 * peer status word (hex) 438 * peer offset (s) 439 * peer delay (s) 440 * peer error bound (s) 441 * peer error (s) 442 */ 443 void 444 record_peer_stats( 445 struct sockaddr_in *addr, 446 int status, 447 double offset, 448 double delay, 449 double dispersion, 450 double skew 451 ) 452 { 453 struct timeval tv; 454 #ifdef HAVE_GETCLOCK 455 struct timespec ts; 456 #endif 457 u_long day, sec, msec; 458 459 if (!stats_control) 460 return; 461 #ifdef HAVE_GETCLOCK 462 (void) getclock(TIMEOFDAY, &ts); 463 tv.tv_sec = ts.tv_sec; 464 tv.tv_usec = ts.tv_nsec / 1000; 465 #else /* not HAVE_GETCLOCK */ 466 GETTIMEOFDAY(&tv, (struct timezone *)NULL); 467 #endif /* not HAVE_GETCLOCK */ 468 day = tv.tv_sec / 86400 + MJD_1970; 469 sec = tv.tv_sec % 86400; 470 msec = tv.tv_usec / 1000; 471 472 filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970)); 473 if (peerstats.fp != NULL) { 474 fprintf(peerstats.fp, 475 "%lu %lu.%03lu %s %x %.9f %.9f %.9f %.9f\n", 476 day, sec, msec, ntoa(addr), status, offset, 477 delay, dispersion, skew); 478 fflush(peerstats.fp); 479 } 480 } 481 /* 482 * record_loop_stats - write loop filter statistics to file 483 * 484 * file format: 485 * day (mjd) 486 * time (s past midnight) 487 * offset (s) 488 * frequency (approx ppm) 489 * time constant (log base 2) 490 */ 491 void 492 record_loop_stats( 493 double offset, 494 double freq, 495 double jitter, 496 double stability, 497 int poll 498 ) 499 { 500 struct timeval tv; 501 #ifdef HAVE_GETCLOCK 502 struct timespec ts; 503 #endif 504 u_long day, sec, msec; 505 506 if (!stats_control) 507 return; 508 #ifdef HAVE_GETCLOCK 509 (void) getclock(TIMEOFDAY, &ts); 510 tv.tv_sec = ts.tv_sec; 511 tv.tv_usec = ts.tv_nsec / 1000; 512 #else /* not HAVE_GETCLOCK */ 513 GETTIMEOFDAY(&tv, (struct timezone *)NULL); 514 #endif /* not HAVE_GETCLOCK */ 515 day = tv.tv_sec / 86400 + MJD_1970; 516 sec = tv.tv_sec % 86400; 517 msec = tv.tv_usec / 1000; 518 519 filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970)); 520 if (loopstats.fp != NULL) { 521 fprintf(loopstats.fp, "%lu %lu.%03lu %.9f %.6f %.9f %.6f %d\n", 522 day, sec, msec, offset, freq * 1e6, jitter, 523 stability * 1e6, poll); 524 fflush(loopstats.fp); 525 } 526 } 527 528 /* 529 * record_clock_stats - write clock statistics to file 530 * 531 * file format: 532 * day (mjd) 533 * time (s past midnight) 534 * peer (ip address) 535 * text message 536 */ 537 void 538 record_clock_stats( 539 struct sockaddr_in *addr, 540 const char *text 541 ) 542 { 543 struct timeval tv; 544 #ifdef HAVE_GETCLOCK 545 struct timespec ts; 546 #endif 547 u_long day, sec, msec; 548 549 if (!stats_control) 550 return; 551 #ifdef HAVE_GETCLOCK 552 (void) getclock(TIMEOFDAY, &ts); 553 tv.tv_sec = ts.tv_sec; 554 tv.tv_usec = ts.tv_nsec / 1000; 555 #else /* not HAVE_GETCLOCK */ 556 GETTIMEOFDAY(&tv, (struct timezone *)NULL); 557 #endif /* not HAVE_GETCLOCK */ 558 day = tv.tv_sec / 86400 + MJD_1970; 559 sec = tv.tv_sec % 86400; 560 msec = tv.tv_usec / 1000; 561 562 filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970)); 563 if (clockstats.fp != NULL) { 564 fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n", 565 day, sec, msec, ntoa(addr), text); 566 fflush(clockstats.fp); 567 } 568 } 569 570 /* 571 * record_raw_stats - write raw timestamps to file 572 * 573 * 574 * file format 575 * time (s past midnight) 576 * peer ip address 577 * local ip address 578 * t1 t2 t3 t4 timestamps 579 */ 580 void 581 record_raw_stats( 582 struct sockaddr_in *srcadr, 583 struct sockaddr_in *dstadr, 584 l_fp *t1, 585 l_fp *t2, 586 l_fp *t3, 587 l_fp *t4 588 ) 589 { 590 struct timeval tv; 591 #ifdef HAVE_GETCLOCK 592 struct timespec ts; 593 #endif 594 u_long day, sec, msec; 595 596 if (!stats_control) 597 return; 598 #ifdef HAVE_GETCLOCK 599 (void) getclock(TIMEOFDAY, &ts); 600 tv.tv_sec = ts.tv_sec; 601 tv.tv_usec = ts.tv_nsec / 1000; 602 #else /* not HAVE_GETCLOCK */ 603 GETTIMEOFDAY(&tv, (struct timezone *)NULL); 604 #endif /* not HAVE_GETCLOCK */ 605 day = tv.tv_sec / 86400 + MJD_1970; 606 sec = tv.tv_sec % 86400; 607 msec = tv.tv_usec / 1000; 608 609 filegen_setup(&rawstats, (u_long)(tv.tv_sec + JAN_1970)); 610 if (rawstats.fp != NULL) { 611 fprintf(rawstats.fp, "%lu %lu.%03lu %s %s %s %s %s %s\n", 612 day, sec, msec, ntoa(srcadr), ntoa(dstadr), 613 ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9), 614 ulfptoa(t4, 9)); 615 fflush(rawstats.fp); 616 } 617 } 618 619 /* 620 * getauthkeys - read the authentication keys from the specified file 621 */ 622 void 623 getauthkeys( 624 char *keyfile 625 ) 626 { 627 int len; 628 629 len = strlen(keyfile); 630 if (len == 0) 631 return; 632 633 if (key_file_name != 0) { 634 if (len > (int)strlen(key_file_name)) { 635 (void) free(key_file_name); 636 key_file_name = 0; 637 } 638 } 639 640 if (key_file_name == 0) { 641 #ifndef SYS_WINNT 642 key_file_name = (char*)emalloc((u_int) (len + 1)); 643 #else 644 key_file_name = (char*)emalloc((u_int) (MAXPATHLEN)); 645 #endif 646 } 647 #ifndef SYS_WINNT 648 memmove(key_file_name, keyfile, (unsigned)(len+1)); 649 #else 650 if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN)) 651 { 652 msyslog(LOG_ERR, 653 "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n"); 654 } 655 #endif /* SYS_WINNT */ 656 657 authreadkeys(key_file_name); 658 } 659 660 661 /* 662 * rereadkeys - read the authentication key file over again. 663 */ 664 void 665 rereadkeys(void) 666 { 667 if (key_file_name != 0) 668 authreadkeys(key_file_name); 669 } 670