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