154359Sroberto/* 254359Sroberto * NTP test program 354359Sroberto * 454359Sroberto * This program tests to see if the NTP user interface routines 554359Sroberto * ntp_gettime() and ntp_adjtime() have been implemented in the kernel. 654359Sroberto * If so, each of these routines is called to display current timekeeping 754359Sroberto * data. 854359Sroberto * 954359Sroberto * For more information, see the README.kern file in the doc directory 1054359Sroberto * of the xntp3 distribution. 1154359Sroberto */ 1282498Sroberto 1354359Sroberto#ifdef HAVE_CONFIG_H 1454359Sroberto# include <config.h> 1554359Sroberto#endif /* HAVE_CONFIG_H */ 1654359Sroberto 1782498Sroberto#include "ntp_fp.h" 18290000Sglebius#include "timevalops.h" 1982498Sroberto#include "ntp_syscall.h" 2082498Sroberto#include "ntp_stdlib.h" 2182498Sroberto 2254359Sroberto#include <stdio.h> 2354359Sroberto#include <ctype.h> 2454359Sroberto#include <signal.h> 2554359Sroberto#include <setjmp.h> 2654359Sroberto 2754359Sroberto#ifdef NTP_SYSCALLS_STD 2854359Sroberto# ifndef SYS_DECOSF1 2954359Sroberto# define BADCALL -1 /* this is supposed to be a bad syscall */ 3054359Sroberto# endif /* SYS_DECOSF1 */ 3154359Sroberto#endif 3254359Sroberto 3382498Sroberto#ifdef HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC 3454359Sroberto#define tv_frac_sec tv_nsec 3554359Sroberto#else 3654359Sroberto#define tv_frac_sec tv_usec 3754359Sroberto#endif 3854359Sroberto 3954359Sroberto 4054359Sroberto#define TIMEX_MOD_BITS \ 4154359Sroberto"\20\1OFFSET\2FREQUENCY\3MAXERROR\4ESTERROR\5STATUS\6TIMECONST\ 4254359Sroberto\13PLL\14FLL\15MICRO\16NANO\17CLKB\20CLKA" 4354359Sroberto 4454359Sroberto#define TIMEX_STA_BITS \ 4554359Sroberto"\20\1PLL\2PPSFREQ\3PPSTIME\4FLL\5INS\6DEL\7UNSYNC\10FREQHOLD\ 4654359Sroberto\11PPSSIGNAL\12PPSJITTER\13PPSWANDER\14PPSERROR\15CLOCKERR\ 4754359Sroberto\16NANO\17MODE\20CLK" 4854359Sroberto 4954359Sroberto#define SCALE_FREQ 65536 /* frequency scale */ 5054359Sroberto 51290000Sglebius/* 52290000Sglebius * These constants are used to round the time stamps computed from 53290000Sglebius * a struct timeval to the microsecond (more or less). This keeps 54290000Sglebius * things neat. 55290000Sglebius */ 56290000Sglebius#define TS_MASK 0xfffff000 /* mask to usec, for time stamps */ 57290000Sglebius#define TS_ROUNDBIT 0x00000800 /* round at this bit */ 5854359Sroberto 5954359Sroberto/* 6054359Sroberto * Function prototypes 6154359Sroberto */ 62290000Sglebiusconst char * sprintb (u_int, const char *); 63290000Sglebiusconst char * timex_state (int); 6454359Sroberto 6554359Sroberto#ifdef SIGSYS 66290000Sglebiusvoid pll_trap (int); 6754359Sroberto 6854359Srobertostatic struct sigaction newsigsys; /* new sigaction status */ 6954359Srobertostatic struct sigaction sigsys; /* current sigaction status */ 7054359Srobertostatic sigjmp_buf env; /* environment var. for pll_trap() */ 7154359Sroberto#endif 7254359Sroberto 7354359Srobertostatic volatile int pll_control; /* (0) daemon, (1) kernel loop */ 7454359Srobertostatic volatile int status; /* most recent status bits */ 7554359Srobertostatic volatile int flash; /* most recent ntp_adjtime() bits */ 76290000Sglebiuschar const * progname; 7782498Srobertostatic char optargs[] = "MNT:cde:f:hm:o:rs:t:"; 7854359Sroberto 7954359Srobertoint 8054359Srobertomain( 8154359Sroberto int argc, 8254359Sroberto char *argv[] 8354359Sroberto ) 8454359Sroberto{ 8554359Sroberto extern int ntp_optind; 8654359Sroberto extern char *ntp_optarg; 8754359Sroberto#ifdef SUBST_ADJTIMEX 88290000Sglebius struct timex ntv; 8954359Sroberto#else 9054359Sroberto struct ntptimeval ntv; 9154359Sroberto#endif 9254359Sroberto struct timeval tv; 9354359Sroberto struct timex ntx, _ntx; 94290000Sglebius int times[20] = { 0 }; 9554359Sroberto double ftemp, gtemp, htemp; 9654359Sroberto long time_frac; /* ntv.time.tv_frac_sec (us/ns) */ 9754359Sroberto l_fp ts; 98132451Sroberto volatile unsigned ts_mask = TS_MASK; /* defaults to 20 bits (us) */ 99132451Sroberto volatile unsigned ts_roundbit = TS_ROUNDBIT; /* defaults to 20 bits (us) */ 100132451Sroberto volatile int fdigits = 6; /* fractional digits for us */ 101290000Sglebius size_t c; 102290000Sglebius int ch; 10354359Sroberto int errflg = 0; 10454359Sroberto int cost = 0; 105132451Sroberto volatile int rawtime = 0; 10654359Sroberto 107290000Sglebius ZERO(ntx); 10854359Sroberto progname = argv[0]; 109290000Sglebius while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) { 110290000Sglebius switch (ch) { 11182498Sroberto#ifdef MOD_MICRO 112290000Sglebius case 'M': 113290000Sglebius ntx.modes |= MOD_MICRO; 114290000Sglebius break; 11582498Sroberto#endif 11682498Sroberto#ifdef MOD_NANO 117290000Sglebius case 'N': 118290000Sglebius ntx.modes |= MOD_NANO; 119290000Sglebius break; 12082498Sroberto#endif 12182498Sroberto#ifdef NTP_API 12282498Sroberto# if NTP_API > 3 123290000Sglebius case 'T': 124290000Sglebius ntx.modes = MOD_TAI; 125290000Sglebius ntx.constant = atoi(ntp_optarg); 126290000Sglebius break; 12782498Sroberto# endif 12882498Sroberto#endif 129290000Sglebius case 'c': 130290000Sglebius cost++; 131290000Sglebius break; 132290000Sglebius 133290000Sglebius case 'e': 134290000Sglebius ntx.modes |= MOD_ESTERROR; 135290000Sglebius ntx.esterror = atoi(ntp_optarg); 136290000Sglebius break; 137290000Sglebius 138290000Sglebius case 'f': 139290000Sglebius ntx.modes |= MOD_FREQUENCY; 140290000Sglebius ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ); 141290000Sglebius break; 142290000Sglebius 143290000Sglebius case 'm': 144290000Sglebius ntx.modes |= MOD_MAXERROR; 145290000Sglebius ntx.maxerror = atoi(ntp_optarg); 146290000Sglebius break; 147290000Sglebius 148290000Sglebius case 'o': 149290000Sglebius ntx.modes |= MOD_OFFSET; 150290000Sglebius ntx.offset = atoi(ntp_optarg); 151290000Sglebius break; 152290000Sglebius 153290000Sglebius case 'r': 154290000Sglebius rawtime++; 155290000Sglebius break; 156290000Sglebius 157290000Sglebius case 's': 158290000Sglebius ntx.modes |= MOD_STATUS; 159290000Sglebius ntx.status = atoi(ntp_optarg); 160290000Sglebius if (ntx.status < 0 || ntx.status >= 0x100) 161290000Sglebius errflg++; 162290000Sglebius break; 163290000Sglebius 164290000Sglebius case 't': 165290000Sglebius ntx.modes |= MOD_TIMECONST; 166290000Sglebius ntx.constant = atoi(ntp_optarg); 167290000Sglebius break; 168290000Sglebius 169290000Sglebius default: 170290000Sglebius errflg++; 171290000Sglebius } 17254359Sroberto } 17354359Sroberto if (errflg || (ntp_optind != argc)) { 174290000Sglebius fprintf(stderr, 175290000Sglebius "usage: %s [-%s]\n\n\ 17682498Sroberto%s%s%s\ 17754359Sroberto-c display the time taken to call ntp_gettime (us)\n\ 17854359Sroberto-e esterror estimate of the error (us)\n\ 17954359Sroberto-f frequency Frequency error (-500 .. 500) (ppm)\n\ 18054359Sroberto-h display this help info\n\ 18154359Sroberto-m maxerror max possible error (us)\n\ 18254359Sroberto-o offset current offset (ms)\n\ 18354359Sroberto-r print the unix and NTP time raw\n\ 184132451Sroberto-s status Set the status bits\n\ 18554359Sroberto-t timeconstant log2 of PLL time constant (0 .. %d)\n", 186290000Sglebius progname, optargs, 18782498Sroberto#ifdef MOD_MICRO 18882498Sroberto"-M switch to microsecond mode\n", 18982498Sroberto#else 19082498Sroberto"", 19182498Sroberto#endif 19282498Sroberto#ifdef MOD_NANO 19382498Sroberto"-N switch to nanosecond mode\n", 19482498Sroberto#else 19582498Sroberto"", 19682498Sroberto#endif 19782498Sroberto#ifdef NTP_API 19882498Sroberto# if NTP_API > 3 19982498Sroberto"-T tai_offset set TAI offset\n", 20082498Sroberto# else 20182498Sroberto"", 20282498Sroberto# endif 20382498Sroberto#else 20482498Sroberto"", 20582498Sroberto#endif 206290000Sglebius MAXTC); 20754359Sroberto exit(2); 20854359Sroberto } 20954359Sroberto 21054359Sroberto#ifdef SIGSYS 21154359Sroberto /* 21254359Sroberto * Test to make sure the sigaction() works in case of invalid 21354359Sroberto * syscall codes. 21454359Sroberto */ 21554359Sroberto newsigsys.sa_handler = pll_trap; 21654359Sroberto newsigsys.sa_flags = 0; 21754359Sroberto if (sigaction(SIGSYS, &newsigsys, &sigsys)) { 21854359Sroberto perror("sigaction() fails to save SIGSYS trap"); 21954359Sroberto exit(1); 22054359Sroberto } 22154359Sroberto#endif /* SIGSYS */ 22254359Sroberto 22354359Sroberto#ifdef BADCALL 22454359Sroberto /* 22554359Sroberto * Make sure the trapcatcher works. 22654359Sroberto */ 22754359Sroberto pll_control = 1; 22854359Sroberto#ifdef SIGSYS 229290000Sglebius if (sigsetjmp(env, 1) == 0) { 23054359Sroberto#endif 23154359Sroberto status = syscall(BADCALL, &ntv); /* dummy parameter */ 23254359Sroberto if ((status < 0) && (errno == ENOSYS)) 23354359Sroberto --pll_control; 23454359Sroberto#ifdef SIGSYS 23554359Sroberto } 23654359Sroberto#endif 23754359Sroberto if (pll_control) 23854359Sroberto printf("sigaction() failed to catch an invalid syscall\n"); 23954359Sroberto#endif /* BADCALL */ 24054359Sroberto 24154359Sroberto if (cost) { 24254359Sroberto#ifdef SIGSYS 24354359Sroberto if (sigsetjmp(env, 1) == 0) { 24454359Sroberto#endif 245290000Sglebius for (c = 0; c < COUNTOF(times); c++) { 24654359Sroberto status = ntp_gettime(&ntv); 24754359Sroberto if ((status < 0) && (errno == ENOSYS)) 24854359Sroberto --pll_control; 24954359Sroberto if (pll_control < 0) 25054359Sroberto break; 25154359Sroberto times[c] = ntv.time.tv_frac_sec; 25254359Sroberto } 25354359Sroberto#ifdef SIGSYS 25454359Sroberto } 25554359Sroberto#endif 25654359Sroberto if (pll_control >= 0) { 25754359Sroberto printf("[ us %06d:", times[0]); 258290000Sglebius for (c = 1; c < COUNTOF(times); c++) 25954359Sroberto printf(" %d", times[c] - times[c - 1]); 26054359Sroberto printf(" ]\n"); 26154359Sroberto } 26254359Sroberto } 26354359Sroberto#ifdef SIGSYS 26454359Sroberto if (sigsetjmp(env, 1) == 0) { 26554359Sroberto#endif 26654359Sroberto status = ntp_gettime(&ntv); 26754359Sroberto if ((status < 0) && (errno == ENOSYS)) 26854359Sroberto --pll_control; 26954359Sroberto#ifdef SIGSYS 27054359Sroberto } 27154359Sroberto#endif 27254359Sroberto _ntx.modes = 0; /* Ensure nothing is set */ 27354359Sroberto#ifdef SIGSYS 27454359Sroberto if (sigsetjmp(env, 1) == 0) { 27554359Sroberto#endif 27654359Sroberto status = ntp_adjtime(&_ntx); 27754359Sroberto if ((status < 0) && (errno == ENOSYS)) 27854359Sroberto --pll_control; 27954359Sroberto flash = _ntx.status; 28054359Sroberto#ifdef SIGSYS 28154359Sroberto } 28254359Sroberto#endif 28354359Sroberto if (pll_control < 0) { 28454359Sroberto printf("NTP user interface routines are not configured in this kernel.\n"); 28554359Sroberto goto lexit; 28654359Sroberto } 28754359Sroberto 28854359Sroberto /* 28954359Sroberto * Fetch timekeeping data and display. 29054359Sroberto */ 29154359Sroberto status = ntp_gettime(&ntv); 292290000Sglebius if (status < 0) { 29354359Sroberto perror("ntp_gettime() call fails"); 294290000Sglebius } else { 29554359Sroberto printf("ntp_gettime() returns code %d (%s)\n", 29654359Sroberto status, timex_state(status)); 29754359Sroberto time_frac = ntv.time.tv_frac_sec; 29854359Sroberto#ifdef STA_NANO 29954359Sroberto if (flash & STA_NANO) { 30054359Sroberto ntv.time.tv_frac_sec /= 1000; 30154359Sroberto ts_mask = 0xfffffffc; /* 1/2^30 */ 30254359Sroberto ts_roundbit = 0x00000002; 30354359Sroberto fdigits = 9; 30454359Sroberto } 30554359Sroberto#endif 30654359Sroberto tv.tv_sec = ntv.time.tv_sec; 30754359Sroberto tv.tv_usec = ntv.time.tv_frac_sec; 30854359Sroberto TVTOTS(&tv, &ts); 30954359Sroberto ts.l_ui += JAN_1970; 31054359Sroberto ts.l_uf += ts_roundbit; 31154359Sroberto ts.l_uf &= ts_mask; 31254359Sroberto printf(" time %s, (.%0*d),\n", 313290000Sglebius prettydate(&ts), fdigits, (int)time_frac); 31482498Sroberto printf(" maximum error %lu us, estimated error %lu us", 31554359Sroberto (u_long)ntv.maxerror, (u_long)ntv.esterror); 31682498Sroberto if (rawtime) 317290000Sglebius printf(" ntptime=%x.%x unixtime=%x.%0*d %s", 318290000Sglebius (u_int)ts.l_ui, (u_int)ts.l_uf, 319290000Sglebius (int)ntv.time.tv_sec, fdigits, 320290000Sglebius (int)time_frac, 321290000Sglebius ctime((time_t *)&ntv.time.tv_sec)); 32282498Sroberto#if NTP_API > 3 323132451Sroberto printf(", TAI offset %ld\n", (long)ntv.tai); 32482498Sroberto#else 32582498Sroberto printf("\n"); 32682498Sroberto#endif /* NTP_API */ 32754359Sroberto } 32854359Sroberto status = ntp_adjtime(&ntx); 329290000Sglebius if (status < 0) { 33054359Sroberto perror((errno == EPERM) ? 33154359Sroberto "Must be root to set kernel values\nntp_adjtime() call fails" : 33254359Sroberto "ntp_adjtime() call fails"); 333290000Sglebius } else { 33454359Sroberto flash = ntx.status; 33554359Sroberto printf("ntp_adjtime() returns code %d (%s)\n", 33654359Sroberto status, timex_state(status)); 33754359Sroberto printf(" modes %s,\n", sprintb(ntx.modes, TIMEX_MOD_BITS)); 33854359Sroberto ftemp = (double)ntx.offset; 33954359Sroberto#ifdef STA_NANO 34054359Sroberto if (flash & STA_NANO) 34154359Sroberto ftemp /= 1000.0; 34254359Sroberto#endif 34354359Sroberto printf(" offset %.3f", ftemp); 34454359Sroberto ftemp = (double)ntx.freq / SCALE_FREQ; 34554359Sroberto printf(" us, frequency %.3f ppm, interval %d s,\n", 34654359Sroberto ftemp, 1 << ntx.shift); 34754359Sroberto printf(" maximum error %lu us, estimated error %lu us,\n", 34854359Sroberto (u_long)ntx.maxerror, (u_long)ntx.esterror); 34954359Sroberto printf(" status %s,\n", sprintb((u_int)ntx.status, TIMEX_STA_BITS)); 35054359Sroberto ftemp = (double)ntx.tolerance / SCALE_FREQ; 35154359Sroberto gtemp = (double)ntx.precision; 35254359Sroberto#ifdef STA_NANO 35354359Sroberto if (flash & STA_NANO) 35454359Sroberto gtemp /= 1000.0; 35554359Sroberto#endif 35654359Sroberto printf( 35754359Sroberto " time constant %lu, precision %.3f us, tolerance %.0f ppm,\n", 35854359Sroberto (u_long)ntx.constant, gtemp, ftemp); 35954359Sroberto if (ntx.shift == 0) 360290000Sglebius exit(0); 36154359Sroberto ftemp = (double)ntx.ppsfreq / SCALE_FREQ; 36254359Sroberto gtemp = (double)ntx.stabil / SCALE_FREQ; 36354359Sroberto htemp = (double)ntx.jitter; 36454359Sroberto#ifdef STA_NANO 36554359Sroberto if (flash & STA_NANO) 36654359Sroberto htemp /= 1000.0; 36754359Sroberto#endif 36854359Sroberto printf( 36954359Sroberto " pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n", 37054359Sroberto ftemp, gtemp, htemp); 37154359Sroberto printf(" intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n", 37254359Sroberto (u_long)ntx.calcnt, (u_long)ntx.jitcnt, 37354359Sroberto (u_long)ntx.stbcnt, (u_long)ntx.errcnt); 374290000Sglebius return 0; 37554359Sroberto } 37654359Sroberto 37754359Sroberto /* 37854359Sroberto * Put things back together the way we found them. 37954359Sroberto */ 38054359Sroberto lexit: 38154359Sroberto#ifdef SIGSYS 38254359Sroberto if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) { 38354359Sroberto perror("sigaction() fails to restore SIGSYS trap"); 38454359Sroberto exit(1); 38554359Sroberto } 38654359Sroberto#endif 38754359Sroberto exit(0); 38854359Sroberto} 38954359Sroberto 39054359Sroberto#ifdef SIGSYS 39154359Sroberto/* 39254359Sroberto * pll_trap - trap processor for undefined syscalls 39354359Sroberto */ 39454359Srobertovoid 39554359Srobertopll_trap( 39654359Sroberto int arg 39754359Sroberto ) 39854359Sroberto{ 39954359Sroberto pll_control--; 40054359Sroberto siglongjmp(env, 1); 40154359Sroberto} 40254359Sroberto#endif 40354359Sroberto 40454359Sroberto/* 40554359Sroberto * Print a value a la the %b format of the kernel's printf 40654359Sroberto */ 407290000Sglebiusconst char * 40854359Srobertosprintb( 409290000Sglebius u_int v, 410290000Sglebius const char * bits 41154359Sroberto ) 41254359Sroberto{ 413290000Sglebius char *cp; 414290000Sglebius char *cplim; 415290000Sglebius int i; 416290000Sglebius int any; 417290000Sglebius char c; 41854359Sroberto static char buf[132]; 41954359Sroberto 420290000Sglebius if (bits != NULL && *bits == 8) 421290000Sglebius snprintf(buf, sizeof(buf), "0%o", v); 42254359Sroberto else 423290000Sglebius snprintf(buf, sizeof(buf), "0x%x", v); 42454359Sroberto cp = buf + strlen(buf); 425290000Sglebius cplim = buf + sizeof(buf); 426290000Sglebius if (bits != NULL) { 427182007Sroberto bits++; 42854359Sroberto *cp++ = ' '; 42954359Sroberto *cp++ = '('; 430290000Sglebius any = FALSE; 43154359Sroberto while ((i = *bits++) != 0) { 432290000Sglebius if (v & (1 << (i - 1))) { 433290000Sglebius if (any) { 434290000Sglebius *cp++ = ','; 435290000Sglebius if (cp >= cplim) 436290000Sglebius goto overrun; 437290000Sglebius } 438290000Sglebius any = TRUE; 439290000Sglebius for (; (c = *bits) > 32; bits++) { 440290000Sglebius *cp++ = c; 441290000Sglebius if (cp >= cplim) 442290000Sglebius goto overrun; 443290000Sglebius } 444290000Sglebius } else { 445290000Sglebius for (; *bits > 32; bits++) 446290000Sglebius continue; 447290000Sglebius } 44854359Sroberto } 44954359Sroberto *cp++ = ')'; 450290000Sglebius if (cp >= cplim) 451290000Sglebius goto overrun; 45254359Sroberto } 45354359Sroberto *cp = '\0'; 454290000Sglebius return buf; 455290000Sglebius 456290000Sglebius overrun: 457290000Sglebius return "sprintb buffer too small"; 45854359Sroberto} 45954359Sroberto 460290000Sglebiusconst char * const timex_states[] = { 46154359Sroberto "OK", "INS", "DEL", "OOP", "WAIT", "ERROR" 46254359Sroberto}; 46354359Sroberto 46454359Srobertoconst char * 46554359Srobertotimex_state( 466290000Sglebius int s 46754359Sroberto ) 46854359Sroberto{ 46954359Sroberto static char buf[32]; 47054359Sroberto 471290000Sglebius if ((size_t)s < COUNTOF(timex_states)) 472290000Sglebius return timex_states[s]; 473290000Sglebius snprintf(buf, sizeof(buf), "TIME-#%d", s); 474290000Sglebius return buf; 47554359Sroberto} 476