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