xref: /freebsd/contrib/ntp/util/ntptime.c (revision 61e21613)
1 /*
2  * NTP test program
3  *
4  * This program tests to see if the NTP user interface routines
5  * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
6  * If so, each of these routines is called to display current timekeeping
7  * data.
8  *
9  * For more information, see the README.kern file in the doc directory
10  * of the xntp3 distribution.
11  */
12 
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif /* HAVE_CONFIG_H */
16 
17 #include "ntp_fp.h"
18 #include "timevalops.h"
19 #include "ntp_syscall.h"
20 #include "ntp_stdlib.h"
21 
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <signal.h>
25 #include <setjmp.h>
26 
27 #ifdef NTP_SYSCALLS_STD
28 # ifndef SYS_DECOSF1
29 #  define BADCALL -1		/* this is supposed to be a bad syscall */
30 # endif /* SYS_DECOSF1 */
31 #endif
32 
33 #ifdef HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC
34 #define tv_frac_sec tv_nsec
35 #else
36 #define tv_frac_sec tv_usec
37 #endif
38 
39 
40 #define TIMEX_MOD_BITS \
41 "\20\1OFFSET\2FREQUENCY\3MAXERROR\4ESTERROR\5STATUS\6TIMECONST\
42 \13PLL\14FLL\15MICRO\16NANO\17CLKB\20CLKA"
43 
44 #define TIMEX_STA_BITS \
45 "\20\1PLL\2PPSFREQ\3PPSTIME\4FLL\5INS\6DEL\7UNSYNC\10FREQHOLD\
46 \11PPSSIGNAL\12PPSJITTER\13PPSWANDER\14PPSERROR\15CLOCKERR\
47 \16NANO\17MODE\20CLK"
48 
49 #define SCALE_FREQ 65536		/* frequency scale */
50 
51 /*
52  * These constants are used to round the time stamps computed from
53  * a struct timeval to the microsecond (more or less).  This keeps
54  * things neat.
55  */
56 #define	TS_MASK_US	0xfffff000	/* mask to usec, for time stamps */
57 #define	TS_ROUNDBIT_US	0x00000800	/* round at this bit */
58 #define	TS_DIGITS_US	6
59 
60 #define	TS_MASK_NS	0xfffffffc	/* 1/2^30, for nsec */
61 #define	TS_ROUNDBIT_NS	0x00000002
62 #define	TS_DIGITS_NS	9
63 
64 /*
65  * Function prototypes
66  */
67 const char *	sprintb		(u_int, const char *);
68 const char *	timex_state	(int);
69 
70 #ifdef SIGSYS
71 void pll_trap		(int);
72 
73 static struct sigaction newsigsys;	/* new sigaction status */
74 static struct sigaction sigsys;		/* current sigaction status */
75 static sigjmp_buf env;		/* environment var. for pll_trap() */
76 #endif
77 
78 static volatile int pll_control; /* (0) daemon, (1) kernel loop */
79 static volatile int status;	/* most recent status bits */
80 static volatile int flash;	/* most recent ntp_adjtime() bits */
81 char const * progname;
82 static char optargs[] = "MNT:cde:f:hm:o:rs:t:";
83 
84 int
85 main(
86 	int argc,
87 	char *argv[]
88 	)
89 {
90 	extern int ntp_optind;
91 	extern char *ntp_optarg;
92 #ifdef SUBST_ADJTIMEX
93 	struct timex ntv;
94 #else
95 	struct ntptimeval ntv;
96 #endif
97 	struct timeval tv;
98 	struct timex ntx, _ntx;
99 	int	times[20] = { 0 };
100 	double ftemp, gtemp, htemp;
101 	double nscale = 1.0;			/* assume usec scale for now */
102 	long time_frac;				/* ntv.time.tv_frac_sec (us/ns) */
103 	l_fp ts;
104 	volatile unsigned ts_mask = TS_MASK_US;		/* defaults to 20 bits (us) */
105 	volatile unsigned ts_roundbit = TS_ROUNDBIT_US;	/* defaults to 20 bits (us) */
106 	volatile int fdigits = TS_DIGITS_US;		/* fractional digits for us */
107 	size_t c;
108 	int ch;
109 	int errflg	= 0;
110 	int cost	= 0;
111 	volatile int rawtime	= 0;
112 
113 	ZERO(ntx);
114 	progname = argv[0];
115 	while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) {
116 		switch (ch) {
117 #ifdef MOD_MICRO
118 		case 'M':
119 			ntx.modes |= MOD_MICRO;
120 			break;
121 #endif
122 #ifdef MOD_NANO
123 		case 'N':
124 			ntx.modes |= MOD_NANO;
125 			break;
126 #endif
127 #if defined(NTP_API) && NTP_API > 3
128 		case 'T':
129 			ntx.modes = MOD_TAI;
130 			ntx.constant = atoi(ntp_optarg);
131 			break;
132 #endif
133 		case 'c':
134 			cost++;
135 			break;
136 
137 		case 'e':
138 			ntx.modes |= MOD_ESTERROR;
139 			ntx.esterror = atoi(ntp_optarg);
140 			break;
141 
142 		case 'f':
143 			ntx.modes |= MOD_FREQUENCY;
144 			ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ);
145 			break;
146 
147 		case 'm':
148 			ntx.modes |= MOD_MAXERROR;
149 			ntx.maxerror = atoi(ntp_optarg);
150 			break;
151 
152 		case 'o':
153 			ntx.modes |= MOD_OFFSET;
154 			ntx.offset = atoi(ntp_optarg);
155 			break;
156 
157 		case 'r':
158 			rawtime++;
159 			break;
160 
161 		case 's':
162 			ntx.modes |= MOD_STATUS;
163 			ntx.status = atoi(ntp_optarg);
164 			if (ntx.status < 0 || ntx.status >= 0x100)
165 				errflg++;
166 			break;
167 
168 		case 't':
169 			ntx.modes |= MOD_TIMECONST;
170 			ntx.constant = atoi(ntp_optarg);
171 			break;
172 
173 		default:
174 			errflg++;
175 		}
176 	}
177 	if (errflg || (ntp_optind != argc)) {
178 		fprintf(stderr,
179 			"usage: %s [-%s]\n\n\
180 %s%s%s\
181 -c		display the time taken to call ntp_gettime (us)\n\
182 -e esterror	estimate of the error (us)\n\
183 -f frequency	Frequency error (-500 .. 500) (ppm)\n\
184 -h		display this help info\n\
185 -m maxerror	max possible error (us)\n\
186 -o offset	current offset (ms)\n\
187 -r		print the unix and NTP time raw\n\
188 -s status	Set the status bits\n\
189 -t timeconstant	log2 of PLL time constant (0 .. %d)\n",
190 			progname, optargs,
191 #ifdef MOD_MICRO
192 "-M		switch to microsecond mode\n",
193 #else
194 "",
195 #endif
196 #ifdef MOD_NANO
197 "-N		switch to nanosecond mode\n",
198 #else
199 "",
200 #endif
201 #ifdef NTP_API
202 # if NTP_API > 3
203 "-T tai_offset	set TAI offset\n",
204 # else
205 "",
206 # endif
207 #else
208 "",
209 #endif
210 			MAXTC);
211 		exit(2);
212 	}
213 
214 #ifdef SIGSYS
215 	/*
216 	 * Test to make sure the sigaction() works in case of invalid
217 	 * syscall codes.
218 	 */
219 	newsigsys.sa_handler = pll_trap;
220 	newsigsys.sa_flags = 0;
221 	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
222 		perror("sigaction() fails to save SIGSYS trap");
223 		exit(1);
224 	}
225 #endif /* SIGSYS */
226 
227 #ifdef	BADCALL
228 	/*
229 	 * Make sure the trapcatcher works.
230 	 */
231 	pll_control = 1;
232 #ifdef SIGSYS
233 	if (sigsetjmp(env, 1) == 0)
234 #endif
235 	{
236 		status = syscall(BADCALL, &ntv); /* dummy parameter */
237 		if ((status < 0) && (errno == ENOSYS))
238 			--pll_control;
239 	}
240 	if (pll_control)
241 	    printf("sigaction() failed to catch an invalid syscall\n");
242 #endif /* BADCALL */
243 
244 	if (cost) {
245 #ifdef SIGSYS
246 		if (sigsetjmp(env, 1) == 0)
247 #endif
248 		{
249 			for (c = 0; c < COUNTOF(times); c++) {
250 				status = ntp_gettime(&ntv);
251 				if ((status < 0) && (errno == ENOSYS))
252 					--pll_control;
253 				if (pll_control < 0)
254 					break;
255 				times[c] = ntv.time.tv_frac_sec;
256 			}
257 		}
258 		if (pll_control >= 0) {
259 			printf("[ us %06d:", times[0]);
260 			for (c = 1; c < COUNTOF(times); c++)
261 			    printf(" %d", times[c] - times[c - 1]);
262 			printf(" ]\n");
263 		}
264 	}
265 #ifdef SIGSYS
266 	if (sigsetjmp(env, 1) == 0)
267 #endif
268 	{
269 		status = ntp_gettime(&ntv);
270 		if ((status < 0) && (errno == ENOSYS))
271 			--pll_control;
272 	}
273 	_ntx.modes = 0;				/* Ensure nothing is set */
274 #ifdef SIGSYS
275 	if (sigsetjmp(env, 1) == 0)
276 #endif
277 	{
278 		status = ntp_adjtime(&_ntx);
279 		if ((status < 0) && (errno == ENOSYS))
280 			--pll_control;
281 		flash = _ntx.status;
282 	}
283 	if (pll_control < 0) {
284 		printf("NTP user interface routines are not configured in this kernel.\n");
285 		goto lexit;
286 	}
287 
288 	/*
289 	 * Fetch timekeeping data and display.
290 	 */
291 	status = ntp_gettime(&ntv);
292 	if (status < 0) {
293 		perror("ntp_gettime() call fails");
294 	} else {
295 		printf("ntp_gettime() returns code %d (%s)\n",
296 		    status, timex_state(status));
297 		time_frac = ntv.time.tv_frac_sec;
298 #ifdef STA_NANO
299 		if (flash & STA_NANO) {
300 			ntv.time.tv_frac_sec /= 1000;
301 			ts_mask = TS_MASK_NS;
302 			ts_roundbit = TS_ROUNDBIT_NS;
303 			fdigits = TS_DIGITS_NS;
304 		}
305 #endif
306 		tv.tv_sec = ntv.time.tv_sec;
307 		tv.tv_usec = ntv.time.tv_frac_sec;
308 		TVTOTS(&tv, &ts);
309 		ts.l_ui += JAN_1970;
310 		ts.l_uf += ts_roundbit;
311 		ts.l_uf &= ts_mask;
312 		printf("  time %s, (.%0*d),\n",
313 		       prettydate(&ts), fdigits, (int)time_frac);
314 		printf("  maximum error %ld us, estimated error %ld us",
315 		       ntv.maxerror, ntv.esterror);
316 		if (rawtime)
317 			printf("  ntptime=%x.%x unixtime=%x.%0*d %s",
318 			       (u_int)ts.l_ui, (u_int)ts.l_uf,
319 			       (int)ntv.time.tv_sec, fdigits,
320 			       (int)time_frac,
321 			       ctime((time_t *)&ntv.time.tv_sec));
322 #if defined(NTP_API) && NTP_API > 3
323 		printf(", TAI offset %ld\n", (long)ntv.tai);
324 #else
325 		printf("\n");
326 #endif /* NTP_API */
327 	}
328 	status = ntp_adjtime(&ntx);
329 	if (status < 0) {
330 		perror((errno == EPERM) ?
331 		   "Must be root to set kernel values\nntp_adjtime() call fails" :
332 		   "ntp_adjtime() call fails");
333 	} else {
334 		flash = ntx.status;
335 		printf("ntp_adjtime() returns code %d (%s)\n",
336 		     status, timex_state(status));
337 		printf("  modes %s,\n", sprintb(ntx.modes, TIMEX_MOD_BITS));
338 #ifdef STA_NANO
339 		if (flash & STA_NANO)
340 			nscale = 1e-3;
341 #endif
342 		ftemp = (double)ntx.offset * nscale;
343 		printf("  offset %.3f", ftemp);
344 		ftemp = (double)ntx.freq / SCALE_FREQ;
345 		printf(" us, frequency %.3f ppm, interval %d s,\n",
346 		       ftemp, 1 << ntx.shift);
347 		printf("  maximum error %ld us, estimated error %ld us,\n",
348 		     ntx.maxerror, ntx.esterror);
349 		printf("  status %s,\n", sprintb((u_int)ntx.status, TIMEX_STA_BITS));
350 		ftemp = (double)ntx.tolerance / SCALE_FREQ;
351 		gtemp = (double)ntx.precision * nscale;
352 		printf(
353 			"  time constant %lu, precision %.3f us, tolerance %.0f ppm,\n",
354 			(u_long)ntx.constant, gtemp, ftemp);
355 		if (ntx.shift == 0)
356 			exit(0);
357 		ftemp = (double)ntx.ppsfreq / SCALE_FREQ;
358 		gtemp = (double)ntx.stabil / SCALE_FREQ;
359 		htemp = (double)ntx.jitter * nscale;
360 		printf("  pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n",
361 		       ftemp, gtemp, htemp);
362 		printf("  intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n",
363 		       (u_long)ntx.calcnt, (u_long)ntx.jitcnt,
364 		       (u_long)ntx.stbcnt, (u_long)ntx.errcnt);
365 		return 0;
366 	}
367 
368 	/*
369 	 * Put things back together the way we found them.
370 	 */
371     lexit:
372 #ifdef SIGSYS
373 	if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) {
374 		perror("sigaction() fails to restore SIGSYS trap");
375 		exit(1);
376 	}
377 #endif
378 	exit(0);
379 }
380 
381 #ifdef SIGSYS
382 /*
383  * pll_trap - trap processor for undefined syscalls
384  */
385 void
386 pll_trap(
387 	int arg
388 	)
389 {
390 	pll_control--;
391 	siglongjmp(env, 1);
392 }
393 #endif
394 
395 /*
396  * Print a value a la the %b format of the kernel's printf
397  */
398 const char *
399 sprintb(
400 	u_int		v,
401 	const char *	bits
402 	)
403 {
404 	char *cp;
405 	char *cplim;
406 	int i;
407 	int any;
408 	char c;
409 	static char buf[132];
410 
411 	if (bits != NULL && *bits == 8)
412 		snprintf(buf, sizeof(buf), "0%o", v);
413 	else
414 		snprintf(buf, sizeof(buf), "0x%x", v);
415 	cp = buf + strlen(buf);
416 	cplim = buf + sizeof(buf);
417 	if (bits != NULL) {
418 		bits++;
419 		*cp++ = ' ';
420 		*cp++ = '(';
421 		any = FALSE;
422 		while ((i = *bits++) != 0) {
423 			if (v & (1 << (i - 1))) {
424 				if (any) {
425 					*cp++ = ',';
426 					if (cp >= cplim)
427 						goto overrun;
428 				}
429 				any = TRUE;
430 				for (; (c = *bits) > 32; bits++) {
431 					*cp++ = c;
432 					if (cp >= cplim)
433 						goto overrun;
434 				}
435 			} else {
436 				for (; *bits > 32; bits++)
437 					continue;
438 			}
439 		}
440 		*cp++ = ')';
441 		if (cp >= cplim)
442 			goto overrun;
443 	}
444 	*cp = '\0';
445 	return buf;
446 
447     overrun:
448 	return "sprintb buffer too small";
449 }
450 
451 const char * const timex_states[] = {
452 	"OK", "INS", "DEL", "OOP", "WAIT", "ERROR"
453 };
454 
455 const char *
456 timex_state(
457 	int s
458 	)
459 {
460 	static char buf[32];
461 
462 	if ((size_t)s < COUNTOF(timex_states))
463 		return timex_states[s];
464 	snprintf(buf, sizeof(buf), "TIME-#%d", s);
465 	return buf;
466 }
467