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