154359Sroberto/* 254359Sroberto * ntp_util.c - stuff I didn't have any other place for 354359Sroberto */ 454359Sroberto#ifdef HAVE_CONFIG_H 582498Sroberto# include <config.h> 654359Sroberto#endif 754359Sroberto 854359Sroberto#include "ntpd.h" 954359Sroberto#include "ntp_unixtime.h" 1054359Sroberto#include "ntp_filegen.h" 1154359Sroberto#include "ntp_if.h" 1254359Sroberto#include "ntp_stdlib.h" 13290000Sglebius#include "ntp_assert.h" 14290000Sglebius#include "ntp_calendar.h" 15290000Sglebius#include "ntp_leapsec.h" 16290000Sglebius#include "lib_strbuf.h" 1754359Sroberto 1882498Sroberto#include <stdio.h> 1982498Sroberto#include <ctype.h> 2082498Sroberto#include <sys/types.h> 2182498Sroberto#ifdef HAVE_SYS_IOCTL_H 2282498Sroberto# include <sys/ioctl.h> 2382498Sroberto#endif 24290000Sglebius#ifdef HAVE_UNISTD_H 25290000Sglebius# include <unistd.h> 26290000Sglebius#endif 27290000Sglebius#include <sys/stat.h> 2882498Sroberto 2982498Sroberto#ifdef HAVE_IEEEFP_H 3082498Sroberto# include <ieeefp.h> 3182498Sroberto#endif 3282498Sroberto#ifdef HAVE_MATH_H 3382498Sroberto# include <math.h> 3482498Sroberto#endif 3582498Sroberto 3654359Sroberto#if defined(VMS) 37182007Sroberto# include <descrip.h> 3854359Sroberto#endif /* VMS */ 3954359Sroberto 4054359Sroberto/* 41290000Sglebius * Defines used by the leapseconds stuff 4254359Sroberto */ 43290000Sglebius#define MAX_TAI 100 /* max TAI offset (s) */ 44290000Sglebius#define L_DAY 86400UL /* seconds per day */ 45290000Sglebius#define L_YEAR (L_DAY * 365) /* days per year */ 46290000Sglebius#define L_LYEAR (L_YEAR + L_DAY) /* days per leap year */ 47290000Sglebius#define L_4YEAR (L_LYEAR + 3 * L_YEAR) /* days per leap cycle */ 48290000Sglebius#define L_CENT (L_4YEAR * 25) /* days per century */ 49290000Sglebius 5054359Sroberto/* 51290000Sglebius * This contains odds and ends, including the hourly stats, various 52290000Sglebius * configuration items, leapseconds stuff, etc. 5354359Sroberto */ 5454359Sroberto/* 55290000Sglebius * File names 5654359Sroberto */ 57290000Sglebiusstatic char *key_file_name; /* keys file name */ 58290000Sglebiusstatic char *leapfile_name; /* leapseconds file name */ 59290000Sglebiusstatic struct stat leapfile_stat; /* leapseconds file stat() buffer */ 60290000Sglebiusstatic int /*BOOL*/have_leapfile = FALSE; 61290000Sglebiuschar *stats_drift_file; /* frequency file name */ 62290000Sglebiusstatic char *stats_temp_file; /* temp frequency file name */ 63290000Sglebiusstatic double wander_resid; /* last frequency update */ 64290000Sglebiusdouble wander_threshold = 1e-7; /* initial frequency threshold */ 6554359Sroberto 6654359Sroberto/* 6754359Sroberto * Statistics file stuff 6854359Sroberto */ 6954359Sroberto#ifndef NTP_VAR 70182007Sroberto# ifndef SYS_WINNT 71182007Sroberto# define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ 72182007Sroberto# else 73290000Sglebius# define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */ 74182007Sroberto# endif /* SYS_WINNT */ 7554359Sroberto#endif 7654359Sroberto 7754359Sroberto 78290000Sglebiuschar statsdir[MAXFILENAME] = NTP_VAR; 7954359Srobertostatic FILEGEN peerstats; 8054359Srobertostatic FILEGEN loopstats; 8154359Srobertostatic FILEGEN clockstats; 8254359Srobertostatic FILEGEN rawstats; 83132451Srobertostatic FILEGEN sysstats; 84290000Sglebiusstatic FILEGEN protostats; 85290000Sglebiusstatic FILEGEN cryptostats; 86182007Srobertostatic FILEGEN timingstats; 8754359Sroberto 8854359Sroberto/* 8954359Sroberto * This controls whether stats are written to the fileset. Provided 9054359Sroberto * so that ntpdc can turn off stats when the file system fills up. 9154359Sroberto */ 9254359Srobertoint stats_control; 9354359Sroberto 9454359Sroberto/* 95290000Sglebius * Last frequency written to file. 96182007Sroberto */ 97290000Sglebiusstatic double prev_drift_comp; /* last frequency update */ 98182007Sroberto 99182007Sroberto/* 100290000Sglebius * Function prototypes 10154359Sroberto */ 102290000Sglebiusstatic void record_sys_stats(void); 103290000Sglebius void ntpd_time_stepped(void); 104290000Sglebiusstatic void check_leap_expiration(int, uint32_t, const time_t*); 105290000Sglebius 106290000Sglebius/* 107290000Sglebius * Prototypes 108290000Sglebius */ 109290000Sglebius#ifdef DEBUG 110290000Sglebiusvoid uninit_util(void); 111290000Sglebius#endif 112290000Sglebius 113290000Sglebius/* 114290000Sglebius * uninit_util - free memory allocated by init_util 115290000Sglebius */ 116290000Sglebius#ifdef DEBUG 11754359Srobertovoid 118290000Sglebiusuninit_util(void) 11954359Sroberto{ 120290000Sglebius#if defined(_MSC_VER) && defined (_DEBUG) 121290000Sglebius _CrtCheckMemory(); 122290000Sglebius#endif 123290000Sglebius if (stats_drift_file) { 124290000Sglebius free(stats_drift_file); 125290000Sglebius free(stats_temp_file); 126290000Sglebius stats_drift_file = NULL; 127290000Sglebius stats_temp_file = NULL; 128290000Sglebius } 129290000Sglebius if (key_file_name) { 130290000Sglebius free(key_file_name); 131290000Sglebius key_file_name = NULL; 132290000Sglebius } 133290000Sglebius filegen_unregister("peerstats"); 134290000Sglebius filegen_unregister("loopstats"); 135290000Sglebius filegen_unregister("clockstats"); 136290000Sglebius filegen_unregister("rawstats"); 137290000Sglebius filegen_unregister("sysstats"); 138290000Sglebius filegen_unregister("protostats"); 139290000Sglebius#ifdef AUTOKEY 140290000Sglebius filegen_unregister("cryptostats"); 141290000Sglebius#endif /* AUTOKEY */ 142290000Sglebius#ifdef DEBUG_TIMING 143290000Sglebius filegen_unregister("timingstats"); 144290000Sglebius#endif /* DEBUG_TIMING */ 14554359Sroberto 146290000Sglebius#if defined(_MSC_VER) && defined (_DEBUG) 147290000Sglebius _CrtCheckMemory(); 148290000Sglebius#endif 149290000Sglebius} 150290000Sglebius#endif /* DEBUG */ 151132451Sroberto 15254359Sroberto 153290000Sglebius/* 154290000Sglebius * init_util - initialize the util module of ntpd 155290000Sglebius */ 156290000Sglebiusvoid 157290000Sglebiusinit_util(void) 158290000Sglebius{ 159290000Sglebius filegen_register(statsdir, "peerstats", &peerstats); 160290000Sglebius filegen_register(statsdir, "loopstats", &loopstats); 161290000Sglebius filegen_register(statsdir, "clockstats", &clockstats); 162290000Sglebius filegen_register(statsdir, "rawstats", &rawstats); 163290000Sglebius filegen_register(statsdir, "sysstats", &sysstats); 164290000Sglebius filegen_register(statsdir, "protostats", &protostats); 165290000Sglebius filegen_register(statsdir, "cryptostats", &cryptostats); 166290000Sglebius filegen_register(statsdir, "timingstats", &timingstats); 167290000Sglebius /* 168290000Sglebius * register with libntp ntp_set_tod() to call us back 169290000Sglebius * when time is stepped. 170290000Sglebius */ 171290000Sglebius step_callback = &ntpd_time_stepped; 172290000Sglebius#ifdef DEBUG 173290000Sglebius atexit(&uninit_util); 174290000Sglebius#endif /* DEBUG */ 17554359Sroberto} 17654359Sroberto 17754359Sroberto 17854359Sroberto/* 17954359Sroberto * hourly_stats - print some interesting stats 18054359Sroberto */ 18154359Srobertovoid 182182007Srobertowrite_stats(void) 18354359Sroberto{ 184290000Sglebius FILE *fp; 18554359Sroberto#ifdef DOSYNCTODR 18654359Sroberto struct timeval tv; 18754359Sroberto#if !defined(VMS) 188290000Sglebius int prio_set; 18954359Sroberto#endif 19054359Sroberto#ifdef HAVE_GETCLOCK 191290000Sglebius struct timespec ts; 19254359Sroberto#endif 193290000Sglebius int o_prio; 19454359Sroberto 19554359Sroberto /* 19654359Sroberto * Sometimes having a Sun can be a drag. 19754359Sroberto * 19854359Sroberto * The kernel variable dosynctodr controls whether the system's 19954359Sroberto * soft clock is kept in sync with the battery clock. If it 20054359Sroberto * is zero, then the soft clock is not synced, and the battery 20154359Sroberto * clock is simply left to rot. That means that when the system 20254359Sroberto * reboots, the battery clock (which has probably gone wacky) 20354359Sroberto * sets the soft clock. That means ntpd starts off with a very 20454359Sroberto * confused idea of what time it is. It then takes a large 20554359Sroberto * amount of time to figure out just how wacky the battery clock 20654359Sroberto * has made things drift, etc, etc. The solution is to make the 20754359Sroberto * battery clock sync up to system time. The way to do THAT is 20854359Sroberto * to simply set the time of day to the current time of day, but 20954359Sroberto * as quickly as possible. This may, or may not be a sensible 21054359Sroberto * thing to do. 21154359Sroberto * 21254359Sroberto * CAVEAT: settimeofday() steps the sun clock by about 800 us, 213290000Sglebius * so setting DOSYNCTODR seems a bad idea in the 214290000Sglebius * case of us resolution 21554359Sroberto */ 21654359Sroberto 21754359Sroberto#if !defined(VMS) 218290000Sglebius /* 219290000Sglebius * (prr) getpriority returns -1 on error, but -1 is also a valid 220132451Sroberto * return value (!), so instead we have to zero errno before the 221132451Sroberto * call and check it for non-zero afterwards. 22254359Sroberto */ 22354359Sroberto errno = 0; 22454359Sroberto prio_set = 0; 22554359Sroberto o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ 22654359Sroberto 227132451Sroberto /* 228132451Sroberto * (prr) if getpriority succeeded, call setpriority to raise 22954359Sroberto * scheduling priority as high as possible. If that succeeds 23054359Sroberto * as well, set the prio_set flag so we remember to reset 231132451Sroberto * priority to its previous value below. Note that on Solaris 232132451Sroberto * 2.6 (and beyond?), both getpriority and setpriority will fail 233132451Sroberto * with ESRCH, because sched_setscheduler (called from main) put 234132451Sroberto * us in the real-time scheduling class which setpriority 235132451Sroberto * doesn't know about. Being in the real-time class is better 236132451Sroberto * than anything setpriority can do, anyhow, so this error is 237132451Sroberto * silently ignored. 23854359Sroberto */ 23954359Sroberto if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) 240132451Sroberto prio_set = 1; /* overdrive */ 24154359Sroberto#endif /* VMS */ 24254359Sroberto#ifdef HAVE_GETCLOCK 243290000Sglebius (void) getclock(TIMEOFDAY, &ts); 244290000Sglebius tv.tv_sec = ts.tv_sec; 245290000Sglebius tv.tv_usec = ts.tv_nsec / 1000; 24654359Sroberto#else /* not HAVE_GETCLOCK */ 24754359Sroberto GETTIMEOFDAY(&tv,(struct timezone *)NULL); 24854359Sroberto#endif /* not HAVE_GETCLOCK */ 249290000Sglebius if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) 25054359Sroberto msyslog(LOG_ERR, "can't sync battery time: %m"); 25154359Sroberto#if !defined(VMS) 25254359Sroberto if (prio_set) 253132451Sroberto setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ 25454359Sroberto#endif /* VMS */ 25554359Sroberto#endif /* DOSYNCTODR */ 256132451Sroberto record_sys_stats(); 25754359Sroberto if (stats_drift_file != 0) { 258290000Sglebius 259290000Sglebius /* 260290000Sglebius * When the frequency file is written, initialize the 261290000Sglebius * prev_drift_comp and wander_resid. Thereafter, 262290000Sglebius * reduce the wander_resid by half each hour. When 263290000Sglebius * the difference between the prev_drift_comp and 264290000Sglebius * drift_comp is less than the wander_resid, update 265290000Sglebius * the frequncy file. This minimizes the file writes to 266290000Sglebius * nonvolaile storage. 267290000Sglebius */ 268290000Sglebius#ifdef DEBUG 269290000Sglebius if (debug) 270290000Sglebius printf("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n", 271290000Sglebius (prev_drift_comp - drift_comp) * 1e6, wander_resid * 272290000Sglebius 1e6, drift_comp * 1e6); 273290000Sglebius#endif 274290000Sglebius if (fabs(prev_drift_comp - drift_comp) < wander_resid) { 275290000Sglebius wander_resid *= 0.5; 276290000Sglebius return; 277290000Sglebius } 278290000Sglebius prev_drift_comp = drift_comp; 279290000Sglebius wander_resid = wander_threshold; 28054359Sroberto if ((fp = fopen(stats_temp_file, "w")) == NULL) { 281290000Sglebius msyslog(LOG_ERR, "frequency file %s: %m", 28254359Sroberto stats_temp_file); 28354359Sroberto return; 28454359Sroberto } 28554359Sroberto fprintf(fp, "%.3f\n", drift_comp * 1e6); 28654359Sroberto (void)fclose(fp); 28754359Sroberto /* atomic */ 28854359Sroberto#ifdef SYS_WINNT 289290000Sglebius if (_unlink(stats_drift_file)) /* rename semantics differ under NT */ 290290000Sglebius msyslog(LOG_WARNING, 291290000Sglebius "Unable to remove prior drift file %s, %m", 292290000Sglebius stats_drift_file); 29354359Sroberto#endif /* SYS_WINNT */ 29454359Sroberto 29554359Sroberto#ifndef NO_RENAME 296290000Sglebius if (rename(stats_temp_file, stats_drift_file)) 297290000Sglebius msyslog(LOG_WARNING, 298290000Sglebius "Unable to rename temp drift file %s to %s, %m", 299290000Sglebius stats_temp_file, stats_drift_file); 30054359Sroberto#else 301182007Sroberto /* we have no rename NFS of ftp in use */ 302290000Sglebius if ((fp = fopen(stats_drift_file, "w")) == 303290000Sglebius NULL) { 304290000Sglebius msyslog(LOG_ERR, 305290000Sglebius "frequency file %s: %m", 30654359Sroberto stats_drift_file); 30754359Sroberto return; 30854359Sroberto } 30954359Sroberto#endif 31054359Sroberto 31154359Sroberto#if defined(VMS) 31254359Sroberto /* PURGE */ 31354359Sroberto { 31454359Sroberto $DESCRIPTOR(oldvers,";-1"); 31554359Sroberto struct dsc$descriptor driftdsc = { 316290000Sglebius strlen(stats_drift_file), 0, 0, 317290000Sglebius stats_drift_file }; 318290000Sglebius while(lib$delete_file(&oldvers, 319290000Sglebius &driftdsc) & 1); 32054359Sroberto } 32154359Sroberto#endif 32254359Sroberto } 32354359Sroberto} 32454359Sroberto 32554359Sroberto 32654359Sroberto/* 32754359Sroberto * stats_config - configure the stats operation 32854359Sroberto */ 32954359Srobertovoid 33054359Srobertostats_config( 33154359Sroberto int item, 332182007Sroberto const char *invalue /* only one type so far */ 33354359Sroberto ) 33454359Sroberto{ 335290000Sglebius FILE *fp; 336182007Sroberto const char *value; 337293894Sglebius size_t len; 338290000Sglebius double old_drift; 339290000Sglebius l_fp now; 340290000Sglebius time_t ttnow; 341290000Sglebius#ifndef VMS 342290000Sglebius const char temp_ext[] = ".TEMP"; 343290000Sglebius#else 344290000Sglebius const char temp_ext[] = "-TEMP"; 345290000Sglebius#endif 34654359Sroberto 347132451Sroberto /* 348132451Sroberto * Expand environment strings under Windows NT, since the 349132451Sroberto * command interpreter doesn't do this, the program must. 35054359Sroberto */ 35154359Sroberto#ifdef SYS_WINNT 35254359Sroberto char newvalue[MAX_PATH], parameter[MAX_PATH]; 35354359Sroberto 354132451Sroberto if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) { 355290000Sglebius switch (item) { 356290000Sglebius case STATS_FREQ_FILE: 357290000Sglebius strlcpy(parameter, "STATS_FREQ_FILE", 358290000Sglebius sizeof(parameter)); 35954359Sroberto break; 360290000Sglebius 361290000Sglebius case STATS_LEAP_FILE: 362290000Sglebius strlcpy(parameter, "STATS_LEAP_FILE", 363290000Sglebius sizeof(parameter)); 36454359Sroberto break; 365290000Sglebius 366290000Sglebius case STATS_STATSDIR: 367290000Sglebius strlcpy(parameter, "STATS_STATSDIR", 368290000Sglebius sizeof(parameter)); 36954359Sroberto break; 370290000Sglebius 371290000Sglebius case STATS_PID_FILE: 372290000Sglebius strlcpy(parameter, "STATS_PID_FILE", 373290000Sglebius sizeof(parameter)); 37454359Sroberto break; 375290000Sglebius 376290000Sglebius default: 377290000Sglebius strlcpy(parameter, "UNKNOWN", 378290000Sglebius sizeof(parameter)); 379290000Sglebius break; 38054359Sroberto } 38154359Sroberto value = invalue; 38254359Sroberto msyslog(LOG_ERR, 383290000Sglebius "ExpandEnvironmentStrings(%s) failed: %m\n", 384290000Sglebius parameter); 385132451Sroberto } else { 386132451Sroberto value = newvalue; 38754359Sroberto } 388290000Sglebius#else 38954359Sroberto value = invalue; 39054359Sroberto#endif /* SYS_WINNT */ 39154359Sroberto 392290000Sglebius switch (item) { 39354359Sroberto 394290000Sglebius /* 395290000Sglebius * Open and read frequency file. 396290000Sglebius */ 397290000Sglebius case STATS_FREQ_FILE: 398290000Sglebius if (!value || (len = strlen(value)) == 0) 399290000Sglebius break; 40054359Sroberto 401290000Sglebius stats_drift_file = erealloc(stats_drift_file, len + 1); 402290000Sglebius stats_temp_file = erealloc(stats_temp_file, 403290000Sglebius len + sizeof(".TEMP")); 404290000Sglebius memcpy(stats_drift_file, value, (size_t)(len+1)); 405290000Sglebius memcpy(stats_temp_file, value, (size_t)len); 406290000Sglebius memcpy(stats_temp_file + len, temp_ext, sizeof(temp_ext)); 40754359Sroberto 40854359Sroberto /* 409132451Sroberto * Open drift file and read frequency. If the file is 410132451Sroberto * missing or contains errors, tell the loop to reset. 41154359Sroberto */ 412290000Sglebius if ((fp = fopen(stats_drift_file, "r")) == NULL) 41354359Sroberto break; 414290000Sglebius 41554359Sroberto if (fscanf(fp, "%lf", &old_drift) != 1) { 416290000Sglebius msyslog(LOG_ERR, 417290000Sglebius "format error frequency file %s", 418290000Sglebius stats_drift_file); 419132451Sroberto fclose(fp); 42054359Sroberto break; 421290000Sglebius 42254359Sroberto } 423132451Sroberto fclose(fp); 424290000Sglebius loop_config(LOOP_FREQ, old_drift); 425290000Sglebius prev_drift_comp = drift_comp; 42654359Sroberto break; 427290000Sglebius 428290000Sglebius /* 429290000Sglebius * Specify statistics directory. 430290000Sglebius */ 431290000Sglebius case STATS_STATSDIR: 432290000Sglebius 433290000Sglebius /* - 1 since value may be missing the DIR_SEP. */ 434290000Sglebius if (strlen(value) >= sizeof(statsdir) - 1) { 43554359Sroberto msyslog(LOG_ERR, 436290000Sglebius "statsdir too long (>%d, sigh)", 437290000Sglebius (int)sizeof(statsdir) - 2); 43854359Sroberto } else { 439290000Sglebius int add_dir_sep; 440293894Sglebius size_t value_l; 44154359Sroberto 442290000Sglebius /* Add a DIR_SEP unless we already have one. */ 443290000Sglebius value_l = strlen(value); 444290000Sglebius if (0 == value_l) 445290000Sglebius add_dir_sep = FALSE; 446290000Sglebius else 447290000Sglebius add_dir_sep = (DIR_SEP != 448290000Sglebius value[value_l - 1]); 449290000Sglebius 450290000Sglebius if (add_dir_sep) 451290000Sglebius snprintf(statsdir, sizeof(statsdir), 452290000Sglebius "%s%c", value, DIR_SEP); 453290000Sglebius else 454290000Sglebius snprintf(statsdir, sizeof(statsdir), 455290000Sglebius "%s", value); 456290000Sglebius filegen_statsdir(); 45754359Sroberto } 45854359Sroberto break; 45954359Sroberto 460290000Sglebius /* 461290000Sglebius * Open pid file. 462290000Sglebius */ 463290000Sglebius case STATS_PID_FILE: 46454359Sroberto if ((fp = fopen(value, "w")) == NULL) { 465290000Sglebius msyslog(LOG_ERR, "pid file %s: %m", 466290000Sglebius value); 46754359Sroberto break; 46854359Sroberto } 469290000Sglebius fprintf(fp, "%d", (int)getpid()); 470290000Sglebius fclose(fp); 47154359Sroberto break; 47254359Sroberto 473290000Sglebius /* 474290000Sglebius * Read leapseconds file. 475290000Sglebius * 476290000Sglebius * Note: Currently a leap file without SHA1 signature is 477290000Sglebius * accepted, but if there is a signature line, the signature 478290000Sglebius * must be valid or the file is rejected. 479290000Sglebius */ 480290000Sglebius case STATS_LEAP_FILE: 481290000Sglebius if (!value || (len = strlen(value)) == 0) 482290000Sglebius break; 483290000Sglebius 484290000Sglebius leapfile_name = erealloc(leapfile_name, len + 1); 485290000Sglebius memcpy(leapfile_name, value, len + 1); 486290000Sglebius 487290000Sglebius if (leapsec_load_file( 488290000Sglebius leapfile_name, &leapfile_stat, TRUE, TRUE)) 489290000Sglebius { 490290000Sglebius leap_signature_t lsig; 491290000Sglebius 492290000Sglebius get_systime(&now); 493290000Sglebius time(&ttnow); 494290000Sglebius leapsec_getsig(&lsig); 495290000Sglebius mprintf_event(EVNT_TAI, NULL, 496290000Sglebius "%d leap %s %s %s", 497290000Sglebius lsig.taiof, 498290000Sglebius fstostr(lsig.ttime), 499290000Sglebius leapsec_expired(now.l_ui, NULL) 500290000Sglebius ? "expired" 501290000Sglebius : "expires", 502290000Sglebius fstostr(lsig.etime)); 503290000Sglebius 504290000Sglebius have_leapfile = TRUE; 505290000Sglebius 506290000Sglebius /* force an immediate daily expiration check of 507290000Sglebius * the leap seconds table 508290000Sglebius */ 509290000Sglebius check_leap_expiration(TRUE, now.l_ui, &ttnow); 510290000Sglebius } 511290000Sglebius break; 512290000Sglebius 513290000Sglebius default: 51454359Sroberto /* oh well */ 51554359Sroberto break; 51654359Sroberto } 51754359Sroberto} 51854359Sroberto 519290000Sglebius 52054359Sroberto/* 52154359Sroberto * record_peer_stats - write peer statistics to file 52254359Sroberto * 52354359Sroberto * file format: 524290000Sglebius * day (MJD) 52554359Sroberto * time (s past UTC midnight) 526290000Sglebius * IP address 527290000Sglebius * status word (hex) 528290000Sglebius * offset 529290000Sglebius * delay 530290000Sglebius * dispersion 531290000Sglebius * jitter 53254359Sroberto*/ 53354359Srobertovoid 53454359Srobertorecord_peer_stats( 535290000Sglebius sockaddr_u *addr, 536132451Sroberto int status, 537290000Sglebius double offset, /* offset */ 538290000Sglebius double delay, /* delay */ 539290000Sglebius double dispersion, /* dispersion */ 540290000Sglebius double jitter /* jitter */ 54154359Sroberto ) 54254359Sroberto{ 543132451Sroberto l_fp now; 544132451Sroberto u_long day; 54554359Sroberto 54654359Sroberto if (!stats_control) 54754359Sroberto return; 54854359Sroberto 549132451Sroberto get_systime(&now); 550132451Sroberto filegen_setup(&peerstats, now.l_ui); 551132451Sroberto day = now.l_ui / 86400 + MJD_1900; 552132451Sroberto now.l_ui %= 86400; 55354359Sroberto if (peerstats.fp != NULL) { 55454359Sroberto fprintf(peerstats.fp, 555290000Sglebius "%lu %s %s %x %.9f %.9f %.9f %.9f\n", day, 556290000Sglebius ulfptoa(&now, 3), stoa(addr), status, offset, 557290000Sglebius delay, dispersion, jitter); 55854359Sroberto fflush(peerstats.fp); 55954359Sroberto } 56054359Sroberto} 561182007Sroberto 562290000Sglebius 56354359Sroberto/* 56454359Sroberto * record_loop_stats - write loop filter statistics to file 56554359Sroberto * 56654359Sroberto * file format: 567290000Sglebius * day (MJD) 56854359Sroberto * time (s past midnight) 569290000Sglebius * offset 570290000Sglebius * frequency (PPM) 571290000Sglebius * jitter 572290000Sglebius * wnder (PPM) 573290000Sglebius * time constant (log2) 57454359Sroberto */ 57554359Srobertovoid 57682498Srobertorecord_loop_stats( 577290000Sglebius double offset, /* offset */ 578290000Sglebius double freq, /* frequency (PPM) */ 579290000Sglebius double jitter, /* jitter */ 580290000Sglebius double wander, /* wander (PPM) */ 581132451Sroberto int spoll 58282498Sroberto ) 58354359Sroberto{ 584132451Sroberto l_fp now; 585132451Sroberto u_long day; 58654359Sroberto 58754359Sroberto if (!stats_control) 58854359Sroberto return; 58954359Sroberto 590132451Sroberto get_systime(&now); 591132451Sroberto filegen_setup(&loopstats, now.l_ui); 592132451Sroberto day = now.l_ui / 86400 + MJD_1900; 593132451Sroberto now.l_ui %= 86400; 59454359Sroberto if (loopstats.fp != NULL) { 595182007Sroberto fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n", 596132451Sroberto day, ulfptoa(&now, 3), offset, freq * 1e6, jitter, 597290000Sglebius wander * 1e6, spoll); 59854359Sroberto fflush(loopstats.fp); 59954359Sroberto } 60054359Sroberto} 60154359Sroberto 602290000Sglebius 60354359Sroberto/* 60454359Sroberto * record_clock_stats - write clock statistics to file 60554359Sroberto * 60654359Sroberto * file format: 607290000Sglebius * day (MJD) 60854359Sroberto * time (s past midnight) 609290000Sglebius * IP address 61054359Sroberto * text message 61154359Sroberto */ 61254359Srobertovoid 61354359Srobertorecord_clock_stats( 614290000Sglebius sockaddr_u *addr, 615290000Sglebius const char *text /* timecode string */ 61654359Sroberto ) 61754359Sroberto{ 618132451Sroberto l_fp now; 619132451Sroberto u_long day; 62054359Sroberto 62154359Sroberto if (!stats_control) 62254359Sroberto return; 62354359Sroberto 624132451Sroberto get_systime(&now); 625132451Sroberto filegen_setup(&clockstats, now.l_ui); 626132451Sroberto day = now.l_ui / 86400 + MJD_1900; 627132451Sroberto now.l_ui %= 86400; 62854359Sroberto if (clockstats.fp != NULL) { 629290000Sglebius fprintf(clockstats.fp, "%lu %s %s %s\n", day, 630290000Sglebius ulfptoa(&now, 3), stoa(addr), text); 63154359Sroberto fflush(clockstats.fp); 63254359Sroberto } 63354359Sroberto} 63454359Sroberto 635290000Sglebius 63654359Sroberto/* 637290000Sglebius * mprintf_clock_stats - write clock statistics to file with 638290000Sglebius * msnprintf-style formatting. 639290000Sglebius */ 640290000Sglebiusint 641290000Sglebiusmprintf_clock_stats( 642290000Sglebius sockaddr_u *addr, 643290000Sglebius const char *fmt, 644290000Sglebius ... 645290000Sglebius ) 646290000Sglebius{ 647290000Sglebius va_list ap; 648290000Sglebius int rc; 649290000Sglebius char msg[512]; 650290000Sglebius 651290000Sglebius va_start(ap, fmt); 652290000Sglebius rc = mvsnprintf(msg, sizeof(msg), fmt, ap); 653290000Sglebius va_end(ap); 654290000Sglebius if (stats_control) 655290000Sglebius record_clock_stats(addr, msg); 656290000Sglebius 657290000Sglebius return rc; 658290000Sglebius} 659290000Sglebius 660290000Sglebius/* 66154359Sroberto * record_raw_stats - write raw timestamps to file 66254359Sroberto * 66354359Sroberto * file format 664290000Sglebius * day (MJD) 66554359Sroberto * time (s past midnight) 66654359Sroberto * peer ip address 667290000Sglebius * IP address 66854359Sroberto * t1 t2 t3 t4 timestamps 66954359Sroberto */ 67054359Srobertovoid 67154359Srobertorecord_raw_stats( 672290000Sglebius sockaddr_u *srcadr, 673290000Sglebius sockaddr_u *dstadr, 674290000Sglebius l_fp *t1, /* originate timestamp */ 675290000Sglebius l_fp *t2, /* receive timestamp */ 676290000Sglebius l_fp *t3, /* transmit timestamp */ 677290000Sglebius l_fp *t4, /* destination timestamp */ 678290000Sglebius int leap, 679290000Sglebius int version, 680290000Sglebius int mode, 681290000Sglebius int stratum, 682290000Sglebius int ppoll, 683290000Sglebius int precision, 684290000Sglebius double root_delay, /* seconds */ 685290000Sglebius double root_dispersion,/* seconds */ 686290000Sglebius u_int32 refid 68754359Sroberto ) 68854359Sroberto{ 689132451Sroberto l_fp now; 690132451Sroberto u_long day; 69154359Sroberto 69254359Sroberto if (!stats_control) 69354359Sroberto return; 69454359Sroberto 695132451Sroberto get_systime(&now); 696132451Sroberto filegen_setup(&rawstats, now.l_ui); 697132451Sroberto day = now.l_ui / 86400 + MJD_1900; 698132451Sroberto now.l_ui %= 86400; 69954359Sroberto if (rawstats.fp != NULL) { 700290000Sglebius fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s\n", 701290000Sglebius day, ulfptoa(&now, 3), 702290000Sglebius stoa(srcadr), dstadr ? stoa(dstadr) : "-", 703290000Sglebius ulfptoa(t1, 9), ulfptoa(t2, 9), 704290000Sglebius ulfptoa(t3, 9), ulfptoa(t4, 9), 705290000Sglebius leap, version, mode, stratum, ppoll, precision, 706290000Sglebius root_delay, root_dispersion, refid_str(refid, stratum)); 70754359Sroberto fflush(rawstats.fp); 70854359Sroberto } 70954359Sroberto} 71054359Sroberto 711132451Sroberto 71254359Sroberto/* 713132451Sroberto * record_sys_stats - write system statistics to file 714132451Sroberto * 715132451Sroberto * file format 716290000Sglebius * day (MJD) 717132451Sroberto * time (s past midnight) 718290000Sglebius * time since reset 719132451Sroberto * packets recieved 720290000Sglebius * packets for this host 721132451Sroberto * current version 722290000Sglebius * old version 723132451Sroberto * access denied 724132451Sroberto * bad length or format 725132451Sroberto * bad authentication 726290000Sglebius * declined 727132451Sroberto * rate exceeded 728290000Sglebius * KoD sent 729132451Sroberto */ 730132451Srobertovoid 731132451Srobertorecord_sys_stats(void) 732132451Sroberto{ 733132451Sroberto l_fp now; 734132451Sroberto u_long day; 735132451Sroberto 736132451Sroberto if (!stats_control) 737132451Sroberto return; 738132451Sroberto 739132451Sroberto get_systime(&now); 740132451Sroberto filegen_setup(&sysstats, now.l_ui); 741132451Sroberto day = now.l_ui / 86400 + MJD_1900; 742132451Sroberto now.l_ui %= 86400; 743132451Sroberto if (sysstats.fp != NULL) { 744290000Sglebius fprintf(sysstats.fp, 745290000Sglebius "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", 746290000Sglebius day, ulfptoa(&now, 3), current_time - sys_stattime, 747290000Sglebius sys_received, sys_processed, sys_newversion, 748290000Sglebius sys_oldversion, sys_restricted, sys_badlength, 749290000Sglebius sys_badauth, sys_declined, sys_limitrejected, 750290000Sglebius sys_kodsent); 751132451Sroberto fflush(sysstats.fp); 752132451Sroberto proto_clr_stats(); 753132451Sroberto } 754132451Sroberto} 755132451Sroberto 756132451Sroberto 757132451Sroberto/* 758290000Sglebius * record_proto_stats - write system statistics to file 759290000Sglebius * 760290000Sglebius * file format 761290000Sglebius * day (MJD) 762290000Sglebius * time (s past midnight) 763290000Sglebius * text message 764290000Sglebius */ 765290000Sglebiusvoid 766290000Sglebiusrecord_proto_stats( 767290000Sglebius char *str /* text string */ 768290000Sglebius ) 769290000Sglebius{ 770290000Sglebius l_fp now; 771290000Sglebius u_long day; 772290000Sglebius 773290000Sglebius if (!stats_control) 774290000Sglebius return; 775290000Sglebius 776290000Sglebius get_systime(&now); 777290000Sglebius filegen_setup(&protostats, now.l_ui); 778290000Sglebius day = now.l_ui / 86400 + MJD_1900; 779290000Sglebius now.l_ui %= 86400; 780290000Sglebius if (protostats.fp != NULL) { 781290000Sglebius fprintf(protostats.fp, "%lu %s %s\n", day, 782290000Sglebius ulfptoa(&now, 3), str); 783290000Sglebius fflush(protostats.fp); 784290000Sglebius } 785290000Sglebius} 786290000Sglebius 787290000Sglebius 788290000Sglebius#ifdef AUTOKEY 789290000Sglebius/* 790132451Sroberto * record_crypto_stats - write crypto statistics to file 791132451Sroberto * 792132451Sroberto * file format: 793132451Sroberto * day (mjd) 794132451Sroberto * time (s past midnight) 795290000Sglebius * peer ip address 796132451Sroberto * text message 797132451Sroberto */ 798132451Srobertovoid 799132451Srobertorecord_crypto_stats( 800290000Sglebius sockaddr_u *addr, 801290000Sglebius const char *text /* text message */ 802132451Sroberto ) 803132451Sroberto{ 804132451Sroberto l_fp now; 805132451Sroberto u_long day; 806132451Sroberto 807132451Sroberto if (!stats_control) 808132451Sroberto return; 809132451Sroberto 810132451Sroberto get_systime(&now); 811132451Sroberto filegen_setup(&cryptostats, now.l_ui); 812132451Sroberto day = now.l_ui / 86400 + MJD_1900; 813132451Sroberto now.l_ui %= 86400; 814132451Sroberto if (cryptostats.fp != NULL) { 815132451Sroberto if (addr == NULL) 816290000Sglebius fprintf(cryptostats.fp, "%lu %s 0.0.0.0 %s\n", 817132451Sroberto day, ulfptoa(&now, 3), text); 818132451Sroberto else 819132451Sroberto fprintf(cryptostats.fp, "%lu %s %s %s\n", 820132451Sroberto day, ulfptoa(&now, 3), stoa(addr), text); 821132451Sroberto fflush(cryptostats.fp); 822132451Sroberto } 823132451Sroberto} 824290000Sglebius#endif /* AUTOKEY */ 825132451Sroberto 826290000Sglebius 827182007Sroberto#ifdef DEBUG_TIMING 828182007Sroberto/* 829290000Sglebius * record_timing_stats - write timing statistics to file 830182007Sroberto * 831182007Sroberto * file format: 832182007Sroberto * day (mjd) 833182007Sroberto * time (s past midnight) 834182007Sroberto * text message 835182007Sroberto */ 836182007Srobertovoid 837182007Srobertorecord_timing_stats( 838290000Sglebius const char *text /* text message */ 839182007Sroberto ) 840182007Sroberto{ 841182007Sroberto static unsigned int flshcnt; 842182007Sroberto l_fp now; 843182007Sroberto u_long day; 844132451Sroberto 845182007Sroberto if (!stats_control) 846182007Sroberto return; 847182007Sroberto 848182007Sroberto get_systime(&now); 849182007Sroberto filegen_setup(&timingstats, now.l_ui); 850182007Sroberto day = now.l_ui / 86400 + MJD_1900; 851182007Sroberto now.l_ui %= 86400; 852182007Sroberto if (timingstats.fp != NULL) { 853290000Sglebius fprintf(timingstats.fp, "%lu %s %s\n", day, lfptoa(&now, 854290000Sglebius 3), text); 855182007Sroberto if (++flshcnt % 100 == 0) 856182007Sroberto fflush(timingstats.fp); 857182007Sroberto } 858182007Sroberto} 859182007Sroberto#endif 860290000Sglebius 861290000Sglebius 862132451Sroberto/* 863290000Sglebius * check_leap_file - See if the leapseconds file has been updated. 864290000Sglebius * 865290000Sglebius * Returns: n/a 866290000Sglebius * 867290000Sglebius * Note: This loads a new leapfile on the fly. Currently a leap file 868290000Sglebius * without SHA1 signature is accepted, but if there is a signature line, 869290000Sglebius * the signature must be valid or the file is rejected. 870290000Sglebius */ 871290000Sglebiusvoid 872290000Sglebiuscheck_leap_file( 873290000Sglebius int is_daily_check, 874290000Sglebius uint32_t ntptime , 875290000Sglebius const time_t *systime 876290000Sglebius ) 877290000Sglebius{ 878290000Sglebius /* just do nothing if there is no leap file */ 879290000Sglebius if ( ! (leapfile_name && *leapfile_name)) 880290000Sglebius return; 881290000Sglebius 882290000Sglebius /* try to load leapfile, force it if no leapfile loaded yet */ 883290000Sglebius if (leapsec_load_file( 884290000Sglebius leapfile_name, &leapfile_stat, 885290000Sglebius !have_leapfile, is_daily_check)) 886290000Sglebius have_leapfile = TRUE; 887290000Sglebius else if (!have_leapfile) 888290000Sglebius return; 889290000Sglebius 890290000Sglebius check_leap_expiration(is_daily_check, ntptime, systime); 891290000Sglebius} 892290000Sglebius 893290000Sglebius/* 894290000Sglebius * check expiration of a loaded leap table 895290000Sglebius */ 896290000Sglebiusstatic void 897290000Sglebiuscheck_leap_expiration( 898290000Sglebius int is_daily_check, 899290000Sglebius uint32_t ntptime , 900290000Sglebius const time_t *systime 901290000Sglebius ) 902290000Sglebius{ 903290000Sglebius static const char * const logPrefix = "leapsecond file"; 904290000Sglebius int rc; 905290000Sglebius 906290000Sglebius /* test the expiration of the leap data and log with proper 907290000Sglebius * level and frequency (once/hour or once/day, depending on the 908290000Sglebius * state. 909290000Sglebius */ 910290000Sglebius rc = leapsec_daystolive(ntptime, systime); 911290000Sglebius if (rc == 0) { 912290000Sglebius msyslog(LOG_WARNING, 913290000Sglebius "%s ('%s'): will expire in less than one day", 914290000Sglebius logPrefix, leapfile_name); 915290000Sglebius } else if (is_daily_check && rc < 28) { 916290000Sglebius if (rc < 0) 917290000Sglebius msyslog(LOG_ERR, 918290000Sglebius "%s ('%s'): expired less than %d day%s ago", 919290000Sglebius logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s")); 920290000Sglebius else 921290000Sglebius msyslog(LOG_WARNING, 922290000Sglebius "%s ('%s'): will expire in less than %d days", 923290000Sglebius logPrefix, leapfile_name, 1+rc); 924290000Sglebius } 925290000Sglebius} 926290000Sglebius 927290000Sglebius 928290000Sglebius/* 92954359Sroberto * getauthkeys - read the authentication keys from the specified file 93054359Sroberto */ 93154359Srobertovoid 93254359Srobertogetauthkeys( 933182007Sroberto const char *keyfile 93454359Sroberto ) 93554359Sroberto{ 936293894Sglebius size_t len; 93754359Sroberto 93854359Sroberto len = strlen(keyfile); 939290000Sglebius if (!len) 94054359Sroberto return; 94154359Sroberto 94254359Sroberto#ifndef SYS_WINNT 943290000Sglebius key_file_name = erealloc(key_file_name, len + 1); 944290000Sglebius memcpy(key_file_name, keyfile, len + 1); 94554359Sroberto#else 946290000Sglebius key_file_name = erealloc(key_file_name, _MAX_PATH); 947290000Sglebius if (len + 1 > _MAX_PATH) 948290000Sglebius return; 949290000Sglebius if (!ExpandEnvironmentStrings(keyfile, key_file_name, 950290000Sglebius _MAX_PATH)) { 95154359Sroberto msyslog(LOG_ERR, 952290000Sglebius "ExpandEnvironmentStrings(KEY_FILE) failed: %m"); 953290000Sglebius strlcpy(key_file_name, keyfile, _MAX_PATH); 95454359Sroberto } 955290000Sglebius key_file_name = erealloc(key_file_name, 956290000Sglebius 1 + strlen(key_file_name)); 95754359Sroberto#endif /* SYS_WINNT */ 95854359Sroberto 95954359Sroberto authreadkeys(key_file_name); 96054359Sroberto} 96154359Sroberto 96254359Sroberto 96354359Sroberto/* 96454359Sroberto * rereadkeys - read the authentication key file over again. 96554359Sroberto */ 96654359Srobertovoid 96754359Srobertorereadkeys(void) 96854359Sroberto{ 969290000Sglebius if (NULL != key_file_name) 970290000Sglebius authreadkeys(key_file_name); 97154359Sroberto} 972132451Sroberto 973290000Sglebius 974290000Sglebius#if notyet 975132451Sroberto/* 976290000Sglebius * ntp_exit - document explicitly that ntpd has exited 977132451Sroberto */ 978290000Sglebiusvoid 979290000Sglebiusntp_exit(int retval) 980290000Sglebius{ 981290000Sglebius msyslog(LOG_ERR, "EXITING with return code %d", retval); 982290000Sglebius exit(retval); 983290000Sglebius} 984290000Sglebius#endif 985290000Sglebius 986290000Sglebius/* 987290000Sglebius * fstostr - prettyprint NTP seconds 988290000Sglebius */ 989290000Sglebiuschar * fstostr( 990290000Sglebius time_t ntp_stamp 991132451Sroberto ) 992132451Sroberto{ 993290000Sglebius char * buf; 994290000Sglebius struct calendar tm; 995132451Sroberto 996290000Sglebius LIB_GETBUF(buf); 997290000Sglebius if (ntpcal_ntp_to_date(&tm, (u_int32)ntp_stamp, NULL) < 0) 998290000Sglebius snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error", 999290000Sglebius (long)ntp_stamp); 1000290000Sglebius else 1001290000Sglebius snprintf(buf, LIB_BUFLENGTH, "%04d%02d%02d%02d%02d", 1002290000Sglebius tm.year, tm.month, tm.monthday, 1003290000Sglebius tm.hour, tm.minute); 1004290000Sglebius return buf; 1005290000Sglebius} 1006132451Sroberto 1007132451Sroberto 1008182007Sroberto/* 1009290000Sglebius * ntpd_time_stepped is called back by step_systime(), allowing ntpd 1010290000Sglebius * to do any one-time processing necessitated by the step. 1011182007Sroberto */ 1012182007Srobertovoid 1013290000Sglebiusntpd_time_stepped(void) 1014182007Sroberto{ 1015290000Sglebius u_int saved_mon_enabled; 1016290000Sglebius 1017290000Sglebius /* 1018290000Sglebius * flush the monitor MRU list which contains l_fp timestamps 1019290000Sglebius * which should not be compared across the step. 1020290000Sglebius */ 1021290000Sglebius if (MON_OFF != mon_enabled) { 1022290000Sglebius saved_mon_enabled = mon_enabled; 1023290000Sglebius mon_stop(MON_OFF); 1024290000Sglebius mon_start(saved_mon_enabled); 1025290000Sglebius } 1026290000Sglebius 1027290000Sglebius /* inform interpolating Windows code to allow time to go back */ 1028290000Sglebius#ifdef SYS_WINNT 1029290000Sglebius win_time_stepped(); 1030290000Sglebius#endif 1031182007Sroberto} 1032