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