ntptime.c revision 290001
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		0xfffff000	/* mask to usec, for time stamps */
57#define	TS_ROUNDBIT	0x00000800	/* round at this bit */
58
59/*
60 * Function prototypes
61 */
62const char *	sprintb		(u_int, const char *);
63const char *	timex_state	(int);
64
65#ifdef SIGSYS
66void pll_trap		(int);
67
68static struct sigaction newsigsys;	/* new sigaction status */
69static struct sigaction sigsys;		/* current sigaction status */
70static sigjmp_buf env;		/* environment var. for pll_trap() */
71#endif
72
73static volatile int pll_control; /* (0) daemon, (1) kernel loop */
74static volatile int status;	/* most recent status bits */
75static volatile int flash;	/* most recent ntp_adjtime() bits */
76char const * progname;
77static char optargs[] = "MNT:cde:f:hm:o:rs:t:";
78
79int
80main(
81	int argc,
82	char *argv[]
83	)
84{
85	extern int ntp_optind;
86	extern char *ntp_optarg;
87#ifdef SUBST_ADJTIMEX
88	struct timex ntv;
89#else
90	struct ntptimeval ntv;
91#endif
92	struct timeval tv;
93	struct timex ntx, _ntx;
94	int	times[20] = { 0 };
95	double ftemp, gtemp, htemp;
96	long time_frac;				/* ntv.time.tv_frac_sec (us/ns) */
97	l_fp ts;
98	volatile unsigned ts_mask = TS_MASK;		/* defaults to 20 bits (us) */
99	volatile unsigned ts_roundbit = TS_ROUNDBIT;	/* defaults to 20 bits (us) */
100	volatile int fdigits = 6;			/* fractional digits for us */
101	size_t c;
102	int ch;
103	int errflg	= 0;
104	int cost	= 0;
105	volatile int rawtime	= 0;
106
107	ZERO(ntx);
108	progname = argv[0];
109	while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) {
110		switch (ch) {
111#ifdef MOD_MICRO
112		case 'M':
113			ntx.modes |= MOD_MICRO;
114			break;
115#endif
116#ifdef MOD_NANO
117		case 'N':
118			ntx.modes |= MOD_NANO;
119			break;
120#endif
121#ifdef NTP_API
122# if NTP_API > 3
123		case 'T':
124			ntx.modes = MOD_TAI;
125			ntx.constant = atoi(ntp_optarg);
126			break;
127# endif
128#endif
129		case 'c':
130			cost++;
131			break;
132
133		case 'e':
134			ntx.modes |= MOD_ESTERROR;
135			ntx.esterror = atoi(ntp_optarg);
136			break;
137
138		case 'f':
139			ntx.modes |= MOD_FREQUENCY;
140			ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ);
141			break;
142
143		case 'm':
144			ntx.modes |= MOD_MAXERROR;
145			ntx.maxerror = atoi(ntp_optarg);
146			break;
147
148		case 'o':
149			ntx.modes |= MOD_OFFSET;
150			ntx.offset = atoi(ntp_optarg);
151			break;
152
153		case 'r':
154			rawtime++;
155			break;
156
157		case 's':
158			ntx.modes |= MOD_STATUS;
159			ntx.status = atoi(ntp_optarg);
160			if (ntx.status < 0 || ntx.status >= 0x100)
161				errflg++;
162			break;
163
164		case 't':
165			ntx.modes |= MOD_TIMECONST;
166			ntx.constant = atoi(ntp_optarg);
167			break;
168
169		default:
170			errflg++;
171		}
172	}
173	if (errflg || (ntp_optind != argc)) {
174		fprintf(stderr,
175			"usage: %s [-%s]\n\n\
176%s%s%s\
177-c		display the time taken to call ntp_gettime (us)\n\
178-e esterror	estimate of the error (us)\n\
179-f frequency	Frequency error (-500 .. 500) (ppm)\n\
180-h		display this help info\n\
181-m maxerror	max possible error (us)\n\
182-o offset	current offset (ms)\n\
183-r		print the unix and NTP time raw\n\
184-s status	Set the status bits\n\
185-t timeconstant	log2 of PLL time constant (0 .. %d)\n",
186			progname, optargs,
187#ifdef MOD_MICRO
188"-M		switch to microsecond mode\n",
189#else
190"",
191#endif
192#ifdef MOD_NANO
193"-N		switch to nanosecond mode\n",
194#else
195"",
196#endif
197#ifdef NTP_API
198# if NTP_API > 3
199"-T tai_offset	set TAI offset\n",
200# else
201"",
202# endif
203#else
204"",
205#endif
206			MAXTC);
207		exit(2);
208	}
209
210#ifdef SIGSYS
211	/*
212	 * Test to make sure the sigaction() works in case of invalid
213	 * syscall codes.
214	 */
215	newsigsys.sa_handler = pll_trap;
216	newsigsys.sa_flags = 0;
217	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
218		perror("sigaction() fails to save SIGSYS trap");
219		exit(1);
220	}
221#endif /* SIGSYS */
222
223#ifdef	BADCALL
224	/*
225	 * Make sure the trapcatcher works.
226	 */
227	pll_control = 1;
228#ifdef SIGSYS
229	if (sigsetjmp(env, 1) == 0) {
230#endif
231		status = syscall(BADCALL, &ntv); /* dummy parameter */
232		if ((status < 0) && (errno == ENOSYS))
233			--pll_control;
234#ifdef SIGSYS
235	}
236#endif
237	if (pll_control)
238	    printf("sigaction() failed to catch an invalid syscall\n");
239#endif /* BADCALL */
240
241	if (cost) {
242#ifdef SIGSYS
243		if (sigsetjmp(env, 1) == 0) {
244#endif
245			for (c = 0; c < COUNTOF(times); c++) {
246				status = ntp_gettime(&ntv);
247				if ((status < 0) && (errno == ENOSYS))
248					--pll_control;
249				if (pll_control < 0)
250					break;
251				times[c] = ntv.time.tv_frac_sec;
252			}
253#ifdef SIGSYS
254		}
255#endif
256		if (pll_control >= 0) {
257			printf("[ us %06d:", times[0]);
258			for (c = 1; c < COUNTOF(times); c++)
259			    printf(" %d", times[c] - times[c - 1]);
260			printf(" ]\n");
261		}
262	}
263#ifdef SIGSYS
264	if (sigsetjmp(env, 1) == 0) {
265#endif
266		status = ntp_gettime(&ntv);
267		if ((status < 0) && (errno == ENOSYS))
268			--pll_control;
269#ifdef SIGSYS
270	}
271#endif
272	_ntx.modes = 0;				/* Ensure nothing is set */
273#ifdef SIGSYS
274	if (sigsetjmp(env, 1) == 0) {
275#endif
276		status = ntp_adjtime(&_ntx);
277		if ((status < 0) && (errno == ENOSYS))
278			--pll_control;
279		flash = _ntx.status;
280#ifdef SIGSYS
281	}
282#endif
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 = 0xfffffffc;	/* 1/2^30 */
302			ts_roundbit = 0x00000002;
303			fdigits = 9;
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 %lu us, estimated error %lu us",
315		       (u_long)ntv.maxerror, (u_long)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 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		ftemp = (double)ntx.offset;
339#ifdef STA_NANO
340		if (flash & STA_NANO)
341			ftemp /= 1000.0;
342#endif
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 %lu us, estimated error %lu us,\n",
348		     (u_long)ntx.maxerror, (u_long)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;
352#ifdef STA_NANO
353		if (flash & STA_NANO)
354			gtemp /= 1000.0;
355#endif
356		printf(
357		    "  time constant %lu, precision %.3f us, tolerance %.0f ppm,\n",
358		    (u_long)ntx.constant, gtemp, ftemp);
359		if (ntx.shift == 0)
360			exit(0);
361		ftemp = (double)ntx.ppsfreq / SCALE_FREQ;
362		gtemp = (double)ntx.stabil / SCALE_FREQ;
363		htemp = (double)ntx.jitter;
364#ifdef STA_NANO
365		if (flash & STA_NANO)
366			htemp /= 1000.0;
367#endif
368		printf(
369		    "  pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n",
370		    ftemp, gtemp, htemp);
371		printf("  intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n",
372		    (u_long)ntx.calcnt, (u_long)ntx.jitcnt,
373		    (u_long)ntx.stbcnt, (u_long)ntx.errcnt);
374		return 0;
375	}
376
377	/*
378	 * Put things back together the way we found them.
379	 */
380    lexit:
381#ifdef SIGSYS
382	if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) {
383		perror("sigaction() fails to restore SIGSYS trap");
384		exit(1);
385	}
386#endif
387	exit(0);
388}
389
390#ifdef SIGSYS
391/*
392 * pll_trap - trap processor for undefined syscalls
393 */
394void
395pll_trap(
396	int arg
397	)
398{
399	pll_control--;
400	siglongjmp(env, 1);
401}
402#endif
403
404/*
405 * Print a value a la the %b format of the kernel's printf
406 */
407const char *
408sprintb(
409	u_int		v,
410	const char *	bits
411	)
412{
413	char *cp;
414	char *cplim;
415	int i;
416	int any;
417	char c;
418	static char buf[132];
419
420	if (bits != NULL && *bits == 8)
421		snprintf(buf, sizeof(buf), "0%o", v);
422	else
423		snprintf(buf, sizeof(buf), "0x%x", v);
424	cp = buf + strlen(buf);
425	cplim = buf + sizeof(buf);
426	if (bits != NULL) {
427		bits++;
428		*cp++ = ' ';
429		*cp++ = '(';
430		any = FALSE;
431		while ((i = *bits++) != 0) {
432			if (v & (1 << (i - 1))) {
433				if (any) {
434					*cp++ = ',';
435					if (cp >= cplim)
436						goto overrun;
437				}
438				any = TRUE;
439				for (; (c = *bits) > 32; bits++) {
440					*cp++ = c;
441					if (cp >= cplim)
442						goto overrun;
443				}
444			} else {
445				for (; *bits > 32; bits++)
446					continue;
447			}
448		}
449		*cp++ = ')';
450		if (cp >= cplim)
451			goto overrun;
452	}
453	*cp = '\0';
454	return buf;
455
456    overrun:
457	return "sprintb buffer too small";
458}
459
460const char * const timex_states[] = {
461	"OK", "INS", "DEL", "OOP", "WAIT", "ERROR"
462};
463
464const char *
465timex_state(
466	int s
467	)
468{
469	static char buf[32];
470
471	if ((size_t)s < COUNTOF(timex_states))
472		return timex_states[s];
473	snprintf(buf, sizeof(buf), "TIME-#%d", s);
474	return buf;
475}
476