1 /* $NetBSD: ntptime.c,v 1.1.1.1 2009/12/13 16:57:31 kardel 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 "ntp_unixtime.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 /* 55 * Function prototypes 56 */ 57 char *sprintb (u_int, const char *); 58 const char *timex_state (int); 59 60 #ifdef SIGSYS 61 void pll_trap (int); 62 63 static struct sigaction newsigsys; /* new sigaction status */ 64 static struct sigaction sigsys; /* current sigaction status */ 65 static sigjmp_buf env; /* environment var. for pll_trap() */ 66 #endif 67 68 static volatile int pll_control; /* (0) daemon, (1) kernel loop */ 69 static volatile int status; /* most recent status bits */ 70 static volatile int flash; /* most recent ntp_adjtime() bits */ 71 char* progname; 72 volatile int debug; /* for libntp */ 73 static char optargs[] = "MNT:cde:f:hm:o:rs:t:"; 74 75 int 76 main( 77 int argc, 78 char *argv[] 79 ) 80 { 81 extern int ntp_optind; 82 extern char *ntp_optarg; 83 #ifdef SUBST_ADJTIMEX 84 struct timex ntv; 85 #else 86 struct ntptimeval ntv; 87 #endif 88 struct timeval tv; 89 struct timex ntx, _ntx; 90 int times[20]; 91 double ftemp, gtemp, htemp; 92 long time_frac; /* ntv.time.tv_frac_sec (us/ns) */ 93 l_fp ts; 94 volatile unsigned ts_mask = TS_MASK; /* defaults to 20 bits (us) */ 95 volatile unsigned ts_roundbit = TS_ROUNDBIT; /* defaults to 20 bits (us) */ 96 volatile int fdigits = 6; /* fractional digits for us */ 97 int c; 98 int errflg = 0; 99 int cost = 0; 100 volatile int rawtime = 0; 101 102 memset((char *)&ntx, 0, sizeof(ntx)); 103 progname = argv[0]; 104 while ((c = ntp_getopt(argc, argv, optargs)) != EOF) switch (c) { 105 #ifdef MOD_MICRO 106 case 'M': 107 ntx.modes |= MOD_MICRO; 108 break; 109 #endif 110 #ifdef MOD_NANO 111 case 'N': 112 ntx.modes |= MOD_NANO; 113 break; 114 #endif 115 #ifdef NTP_API 116 # if NTP_API > 3 117 case 'T': 118 ntx.modes = MOD_TAI; 119 ntx.constant = atoi(ntp_optarg); 120 break; 121 # endif 122 #endif 123 case 'c': 124 cost++; 125 break; 126 case 'e': 127 ntx.modes |= MOD_ESTERROR; 128 ntx.esterror = atoi(ntp_optarg); 129 break; 130 case 'f': 131 ntx.modes |= MOD_FREQUENCY; 132 ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ); 133 break; 134 case 'm': 135 ntx.modes |= MOD_MAXERROR; 136 ntx.maxerror = atoi(ntp_optarg); 137 break; 138 case 'o': 139 ntx.modes |= MOD_OFFSET; 140 ntx.offset = atoi(ntp_optarg); 141 break; 142 case 'r': 143 rawtime++; 144 break; 145 case 's': 146 ntx.modes |= MOD_STATUS; 147 ntx.status = atoi(ntp_optarg); 148 if (ntx.status < 0 || ntx.status >= 0x100) errflg++; 149 break; 150 case 't': 151 ntx.modes |= MOD_TIMECONST; 152 ntx.constant = atoi(ntp_optarg); 153 break; 154 default: 155 errflg++; 156 } 157 if (errflg || (ntp_optind != argc)) { 158 (void) fprintf(stderr, 159 "usage: %s [-%s]\n\n\ 160 %s%s%s\ 161 -c display the time taken to call ntp_gettime (us)\n\ 162 -e esterror estimate of the error (us)\n\ 163 -f frequency Frequency error (-500 .. 500) (ppm)\n\ 164 -h display this help info\n\ 165 -m maxerror max possible error (us)\n\ 166 -o offset current offset (ms)\n\ 167 -r print the unix and NTP time raw\n\ 168 -s status Set the status bits\n\ 169 -t timeconstant log2 of PLL time constant (0 .. %d)\n", 170 progname, optargs, 171 #ifdef MOD_MICRO 172 "-M switch to microsecond mode\n", 173 #else 174 "", 175 #endif 176 #ifdef MOD_NANO 177 "-N switch to nanosecond mode\n", 178 #else 179 "", 180 #endif 181 #ifdef NTP_API 182 # if NTP_API > 3 183 "-T tai_offset set TAI offset\n", 184 # else 185 "", 186 # endif 187 #else 188 "", 189 #endif 190 MAXTC); 191 exit(2); 192 } 193 194 #ifdef SIGSYS 195 /* 196 * Test to make sure the sigaction() works in case of invalid 197 * syscall codes. 198 */ 199 newsigsys.sa_handler = pll_trap; 200 newsigsys.sa_flags = 0; 201 if (sigaction(SIGSYS, &newsigsys, &sigsys)) { 202 perror("sigaction() fails to save SIGSYS trap"); 203 exit(1); 204 } 205 #endif /* SIGSYS */ 206 207 #ifdef BADCALL 208 /* 209 * Make sure the trapcatcher works. 210 */ 211 pll_control = 1; 212 #ifdef SIGSYS 213 if (sigsetjmp(env, 1) == 0) 214 { 215 #endif 216 status = syscall(BADCALL, &ntv); /* dummy parameter */ 217 if ((status < 0) && (errno == ENOSYS)) 218 --pll_control; 219 #ifdef SIGSYS 220 } 221 #endif 222 if (pll_control) 223 printf("sigaction() failed to catch an invalid syscall\n"); 224 #endif /* BADCALL */ 225 226 if (cost) { 227 #ifdef SIGSYS 228 if (sigsetjmp(env, 1) == 0) { 229 #endif 230 for (c = 0; c < sizeof times / sizeof times[0]; c++) { 231 status = ntp_gettime(&ntv); 232 if ((status < 0) && (errno == ENOSYS)) 233 --pll_control; 234 if (pll_control < 0) 235 break; 236 times[c] = ntv.time.tv_frac_sec; 237 } 238 #ifdef SIGSYS 239 } 240 #endif 241 if (pll_control >= 0) { 242 printf("[ us %06d:", times[0]); 243 for (c = 1; c < sizeof times / sizeof times[0]; c++) 244 printf(" %d", times[c] - times[c - 1]); 245 printf(" ]\n"); 246 } 247 } 248 #ifdef SIGSYS 249 if (sigsetjmp(env, 1) == 0) { 250 #endif 251 status = ntp_gettime(&ntv); 252 if ((status < 0) && (errno == ENOSYS)) 253 --pll_control; 254 #ifdef SIGSYS 255 } 256 #endif 257 _ntx.modes = 0; /* Ensure nothing is set */ 258 #ifdef SIGSYS 259 if (sigsetjmp(env, 1) == 0) { 260 #endif 261 status = ntp_adjtime(&_ntx); 262 if ((status < 0) && (errno == ENOSYS)) 263 --pll_control; 264 flash = _ntx.status; 265 #ifdef SIGSYS 266 } 267 #endif 268 if (pll_control < 0) { 269 printf("NTP user interface routines are not configured in this kernel.\n"); 270 goto lexit; 271 } 272 273 /* 274 * Fetch timekeeping data and display. 275 */ 276 status = ntp_gettime(&ntv); 277 if (status < 0) 278 perror("ntp_gettime() call fails"); 279 else { 280 printf("ntp_gettime() returns code %d (%s)\n", 281 status, timex_state(status)); 282 time_frac = ntv.time.tv_frac_sec; 283 #ifdef STA_NANO 284 if (flash & STA_NANO) { 285 ntv.time.tv_frac_sec /= 1000; 286 ts_mask = 0xfffffffc; /* 1/2^30 */ 287 ts_roundbit = 0x00000002; 288 fdigits = 9; 289 } 290 #endif 291 tv.tv_sec = ntv.time.tv_sec; 292 tv.tv_usec = ntv.time.tv_frac_sec; 293 TVTOTS(&tv, &ts); 294 ts.l_ui += JAN_1970; 295 ts.l_uf += ts_roundbit; 296 ts.l_uf &= ts_mask; 297 printf(" time %s, (.%0*d),\n", 298 prettydate(&ts), fdigits, (int) time_frac); 299 printf(" maximum error %lu us, estimated error %lu us", 300 (u_long)ntv.maxerror, (u_long)ntv.esterror); 301 if (rawtime) 302 printf(" ntptime=%x.%x unixtime=%x.%0*d %s", 303 (unsigned int) ts.l_ui, (unsigned int) ts.l_uf, 304 (int) ntv.time.tv_sec, fdigits, (int) time_frac, 305 ctime((const time_t *) &ntv.time.tv_sec)); 306 #if NTP_API > 3 307 printf(", TAI offset %ld\n", (long)ntv.tai); 308 #else 309 printf("\n"); 310 #endif /* NTP_API */ 311 } 312 status = ntp_adjtime(&ntx); 313 if (status < 0) 314 perror((errno == EPERM) ? 315 "Must be root to set kernel values\nntp_adjtime() call fails" : 316 "ntp_adjtime() call fails"); 317 else { 318 flash = ntx.status; 319 printf("ntp_adjtime() returns code %d (%s)\n", 320 status, timex_state(status)); 321 printf(" modes %s,\n", sprintb(ntx.modes, TIMEX_MOD_BITS)); 322 ftemp = (double)ntx.offset; 323 #ifdef STA_NANO 324 if (flash & STA_NANO) 325 ftemp /= 1000.0; 326 #endif 327 printf(" offset %.3f", ftemp); 328 ftemp = (double)ntx.freq / SCALE_FREQ; 329 printf(" us, frequency %.3f ppm, interval %d s,\n", 330 ftemp, 1 << ntx.shift); 331 printf(" maximum error %lu us, estimated error %lu us,\n", 332 (u_long)ntx.maxerror, (u_long)ntx.esterror); 333 printf(" status %s,\n", sprintb((u_int)ntx.status, TIMEX_STA_BITS)); 334 ftemp = (double)ntx.tolerance / SCALE_FREQ; 335 gtemp = (double)ntx.precision; 336 #ifdef STA_NANO 337 if (flash & STA_NANO) 338 gtemp /= 1000.0; 339 #endif 340 printf( 341 " time constant %lu, precision %.3f us, tolerance %.0f ppm,\n", 342 (u_long)ntx.constant, gtemp, ftemp); 343 if (ntx.shift == 0) 344 exit (0); 345 ftemp = (double)ntx.ppsfreq / SCALE_FREQ; 346 gtemp = (double)ntx.stabil / SCALE_FREQ; 347 htemp = (double)ntx.jitter; 348 #ifdef STA_NANO 349 if (flash & STA_NANO) 350 htemp /= 1000.0; 351 #endif 352 printf( 353 " pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n", 354 ftemp, gtemp, htemp); 355 printf(" intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n", 356 (u_long)ntx.calcnt, (u_long)ntx.jitcnt, 357 (u_long)ntx.stbcnt, (u_long)ntx.errcnt); 358 return (0); 359 } 360 361 /* 362 * Put things back together the way we found them. 363 */ 364 lexit: 365 #ifdef SIGSYS 366 if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) { 367 perror("sigaction() fails to restore SIGSYS trap"); 368 exit(1); 369 } 370 #endif 371 exit(0); 372 } 373 374 #ifdef SIGSYS 375 /* 376 * pll_trap - trap processor for undefined syscalls 377 */ 378 void 379 pll_trap( 380 int arg 381 ) 382 { 383 pll_control--; 384 siglongjmp(env, 1); 385 } 386 #endif 387 388 /* 389 * Print a value a la the %b format of the kernel's printf 390 */ 391 char * 392 sprintb( 393 register u_int v, 394 register const char *bits 395 ) 396 { 397 register char *cp; 398 register int i, any = 0; 399 register char c; 400 static char buf[132]; 401 402 if (bits && *bits == 8) 403 (void)sprintf(buf, "0%o", v); 404 else 405 (void)sprintf(buf, "0x%x", v); 406 cp = buf + strlen(buf); 407 if (bits) { 408 bits++; 409 *cp++ = ' '; 410 *cp++ = '('; 411 while ((i = *bits++) != 0) { 412 if (v & (1 << (i-1))) { 413 if (any) 414 *cp++ = ','; 415 any = 1; 416 for (; (c = *bits) > 32; bits++) 417 *cp++ = c; 418 } else 419 for (; *bits > 32; bits++) 420 continue; 421 } 422 *cp++ = ')'; 423 } 424 *cp = '\0'; 425 return (buf); 426 } 427 428 const char *timex_states[] = { 429 "OK", "INS", "DEL", "OOP", "WAIT", "ERROR" 430 }; 431 432 const char * 433 timex_state( 434 register int s 435 ) 436 { 437 static char buf[32]; 438 439 if (s >= 0 && s < sizeof(timex_states) / sizeof(timex_states[0])) 440 return (timex_states[s]); 441 sprintf(buf, "TIME-#%d", s); 442 return (buf); 443 } 444