ntptime.c revision 358659
11573Srgrimes/*
21573Srgrimes * NTP test program
31573Srgrimes *
41573Srgrimes * This program tests to see if the NTP user interface routines
51573Srgrimes * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
61573Srgrimes * If so, each of these routines is called to display current timekeeping
71573Srgrimes * data.
81573Srgrimes *
91573Srgrimes * For more information, see the README.kern file in the doc directory
101573Srgrimes * of the xntp3 distribution.
111573Srgrimes */
121573Srgrimes
13251069Semaste#ifdef HAVE_CONFIG_H
141573Srgrimes# include <config.h>
151573Srgrimes#endif /* HAVE_CONFIG_H */
161573Srgrimes
171573Srgrimes#include "ntp_fp.h"
181573Srgrimes#include "timevalops.h"
191573Srgrimes#include "ntp_syscall.h"
201573Srgrimes#include "ntp_stdlib.h"
211573Srgrimes
221573Srgrimes#include <stdio.h>
231573Srgrimes#include <ctype.h>
241573Srgrimes#include <signal.h>
251573Srgrimes#include <setjmp.h>
261573Srgrimes
271573Srgrimes#ifdef NTP_SYSCALLS_STD
281573Srgrimes# ifndef SYS_DECOSF1
291573Srgrimes#  define BADCALL -1		/* this is supposed to be a bad syscall */
301573Srgrimes# endif /* SYS_DECOSF1 */
311573Srgrimes#endif
321573Srgrimes
3386170Sobrien#ifdef HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC
3486170Sobrien#define tv_frac_sec tv_nsec
351573Srgrimes#else
361573Srgrimes#define tv_frac_sec tv_usec
37102637Srobert#endif
38102637Srobert
391573Srgrimes
40229368Sed#define TIMEX_MOD_BITS \
411573Srgrimes"\20\1OFFSET\2FREQUENCY\3MAXERROR\4ESTERROR\5STATUS\6TIMECONST\
42123631Sjkh\13PLL\14FLL\15MICRO\16NANO\17CLKB\20CLKA"
43123631Sjkh
44123631Sjkh#define TIMEX_STA_BITS \
451573Srgrimes"\20\1PLL\2PPSFREQ\3PPSTIME\4FLL\5INS\6DEL\7UNSYNC\10FREQHOLD\
46123584Sjkh\11PPSSIGNAL\12PPSJITTER\13PPSWANDER\14PPSERROR\15CLOCKERR\
47102637Srobert\16NANO\17MODE\20CLK"
48102637Srobert
49102637Srobert#define SCALE_FREQ 65536		/* frequency scale */
501573Srgrimes
511573Srgrimes/*
521573Srgrimes * These constants are used to round the time stamps computed from
53229368Sed * a struct timeval to the microsecond (more or less).  This keeps
54229571Sed * 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 */
67const char *	sprintb		(u_int, const char *);
68const char *	timex_state	(int);
69
70#ifdef SIGSYS
71void pll_trap		(int);
72
73static struct sigaction newsigsys;	/* new sigaction status */
74static struct sigaction sigsys;		/* current sigaction status */
75static sigjmp_buf env;		/* environment var. for pll_trap() */
76#endif
77
78static volatile int pll_control; /* (0) daemon, (1) kernel loop */
79static volatile int status;	/* most recent status bits */
80static volatile int flash;	/* most recent ntp_adjtime() bits */
81char const * progname;
82static char optargs[] = "MNT:cde:f:hm:o:rs:t:";
83
84int
85main(
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 */
385void
386pll_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 */
398const char *
399sprintb(
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
451const char * const timex_states[] = {
452	"OK", "INS", "DEL", "OOP", "WAIT", "ERROR"
453};
454
455const char *
456timex_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