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