xref: /freebsd/contrib/ntp/ntpd/ntp_util.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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