1 /* $NetBSD: ntptime.c,v 1.9 2020/05/25 20:47:37 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_US 0xfffff000 /* mask to usec, for time stamps */ 59 #define TS_ROUNDBIT_US 0x00000800 /* round at this bit */ 60 #define TS_DIGITS_US 6 61 62 #define TS_MASK_NS 0xfffffffc /* 1/2^30, for nsec */ 63 #define TS_ROUNDBIT_NS 0x00000002 64 #define TS_DIGITS_NS 9 65 66 /* 67 * Function prototypes 68 */ 69 const char * sprintb (u_int, const char *); 70 const char * timex_state (int); 71 72 #ifdef SIGSYS 73 void pll_trap (int); 74 75 static struct sigaction newsigsys; /* new sigaction status */ 76 static struct sigaction sigsys; /* current sigaction status */ 77 static sigjmp_buf env; /* environment var. for pll_trap() */ 78 #endif 79 80 static volatile int pll_control; /* (0) daemon, (1) kernel loop */ 81 static volatile int status; /* most recent status bits */ 82 static volatile int flash; /* most recent ntp_adjtime() bits */ 83 char const * progname; 84 static char optargs[] = "MNT:cde:f:hm:o:rs:t:"; 85 86 int 87 main( 88 int argc, 89 char *argv[] 90 ) 91 { 92 extern int ntp_optind; 93 extern char *ntp_optarg; 94 #ifdef SUBST_ADJTIMEX 95 struct timex ntv; 96 #else 97 struct ntptimeval ntv; 98 #endif 99 struct timeval tv; 100 struct timex ntx, _ntx; 101 int times[20] = { 0 }; 102 double ftemp, gtemp, htemp; 103 volatile double nscale = 1.0; /* assume usec scale for now */ 104 long time_frac; /* ntv.time.tv_frac_sec (us/ns) */ 105 l_fp ts; 106 volatile unsigned ts_mask = TS_MASK_US; /* defaults to 20 bits (us) */ 107 volatile unsigned ts_roundbit = TS_ROUNDBIT_US; /* defaults to 20 bits (us) */ 108 volatile int fdigits = TS_DIGITS_US; /* fractional digits for us */ 109 size_t c; 110 int ch; 111 int errflg = 0; 112 int cost = 0; 113 volatile int rawtime = 0; 114 115 ZERO(ntx); 116 progname = argv[0]; 117 while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) { 118 switch (ch) { 119 #ifdef MOD_MICRO 120 case 'M': 121 ntx.modes |= MOD_MICRO; 122 break; 123 #endif 124 #ifdef MOD_NANO 125 case 'N': 126 ntx.modes |= MOD_NANO; 127 break; 128 #endif 129 #if defined(NTP_API) && NTP_API > 3 130 case 'T': 131 ntx.modes = MOD_TAI; 132 ntx.constant = atoi(ntp_optarg); 133 break; 134 #endif 135 case 'c': 136 cost++; 137 break; 138 139 case 'e': 140 ntx.modes |= MOD_ESTERROR; 141 ntx.esterror = atoi(ntp_optarg); 142 break; 143 144 case 'f': 145 ntx.modes |= MOD_FREQUENCY; 146 ntx.freq = (long)(atof(ntp_optarg) * SCALE_FREQ); 147 break; 148 149 case 'm': 150 ntx.modes |= MOD_MAXERROR; 151 ntx.maxerror = atoi(ntp_optarg); 152 break; 153 154 case 'o': 155 ntx.modes |= MOD_OFFSET; 156 ntx.offset = atoi(ntp_optarg); 157 break; 158 159 case 'r': 160 rawtime++; 161 break; 162 163 case 's': 164 ntx.modes |= MOD_STATUS; 165 ntx.status = atoi(ntp_optarg); 166 if (ntx.status < 0 || ntx.status >= 0x100) 167 errflg++; 168 break; 169 170 case 't': 171 ntx.modes |= MOD_TIMECONST; 172 ntx.constant = atoi(ntp_optarg); 173 break; 174 175 default: 176 errflg++; 177 } 178 } 179 if (errflg || (ntp_optind != argc)) { 180 fprintf(stderr, 181 "usage: %s [-%s]\n\n\ 182 %s%s%s\ 183 -c display the time taken to call ntp_gettime (us)\n\ 184 -e esterror estimate of the error (us)\n\ 185 -f frequency Frequency error (-500 .. 500) (ppm)\n\ 186 -h display this help info\n\ 187 -m maxerror max possible error (us)\n\ 188 -o offset current offset (ms)\n\ 189 -r print the unix and NTP time raw\n\ 190 -s status Set the status bits\n\ 191 -t timeconstant log2 of PLL time constant (0 .. %d)\n", 192 progname, optargs, 193 #ifdef MOD_MICRO 194 "-M switch to microsecond mode\n", 195 #else 196 "", 197 #endif 198 #ifdef MOD_NANO 199 "-N switch to nanosecond mode\n", 200 #else 201 "", 202 #endif 203 #ifdef NTP_API 204 # if NTP_API > 3 205 "-T tai_offset set TAI offset\n", 206 # else 207 "", 208 # endif 209 #else 210 "", 211 #endif 212 MAXTC); 213 exit(2); 214 } 215 216 #ifdef SIGSYS 217 /* 218 * Test to make sure the sigaction() works in case of invalid 219 * syscall codes. 220 */ 221 newsigsys.sa_handler = pll_trap; 222 newsigsys.sa_flags = 0; 223 if (sigaction(SIGSYS, &newsigsys, &sigsys)) { 224 perror("sigaction() fails to save SIGSYS trap"); 225 exit(1); 226 } 227 #endif /* SIGSYS */ 228 229 #ifdef BADCALL 230 /* 231 * Make sure the trapcatcher works. 232 */ 233 pll_control = 1; 234 #ifdef SIGSYS 235 if (sigsetjmp(env, 1) == 0) 236 #endif 237 { 238 status = syscall(BADCALL, &ntv); /* dummy parameter */ 239 if ((status < 0) && (errno == ENOSYS)) 240 --pll_control; 241 } 242 if (pll_control) 243 printf("sigaction() failed to catch an invalid syscall\n"); 244 #endif /* BADCALL */ 245 246 if (cost) { 247 #ifdef SIGSYS 248 if (sigsetjmp(env, 1) == 0) 249 #endif 250 { 251 for (c = 0; c < COUNTOF(times); c++) { 252 status = ntp_gettime(&ntv); 253 if ((status < 0) && (errno == ENOSYS)) 254 --pll_control; 255 if (pll_control < 0) 256 break; 257 times[c] = ntv.time.tv_frac_sec; 258 } 259 } 260 if (pll_control >= 0) { 261 printf("[ us %06d:", times[0]); 262 for (c = 1; c < COUNTOF(times); c++) 263 printf(" %d", times[c] - times[c - 1]); 264 printf(" ]\n"); 265 } 266 } 267 #ifdef SIGSYS 268 if (sigsetjmp(env, 1) == 0) 269 #endif 270 { 271 status = ntp_gettime(&ntv); 272 if ((status < 0) && (errno == ENOSYS)) 273 --pll_control; 274 } 275 _ntx.modes = 0; /* Ensure nothing is set */ 276 #ifdef SIGSYS 277 if (sigsetjmp(env, 1) == 0) 278 #endif 279 { 280 status = ntp_adjtime(&_ntx); 281 if ((status < 0) && (errno == ENOSYS)) 282 --pll_control; 283 flash = _ntx.status; 284 } 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 = TS_MASK_NS; 304 ts_roundbit = TS_ROUNDBIT_NS; 305 fdigits = TS_DIGITS_NS; 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 %ld us, estimated error %ld us", 317 ntv.maxerror, 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 defined(NTP_API) && 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 #ifdef STA_NANO 341 if (flash & STA_NANO) 342 nscale = 1e-3; 343 #endif 344 ftemp = (double)ntx.offset * nscale; 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 %ld us, estimated error %ld us,\n", 350 ntx.maxerror, 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 * nscale; 354 printf( 355 " time constant %lu, precision %.3f us, tolerance %.0f ppm,\n", 356 (u_long)ntx.constant, gtemp, ftemp); 357 if (ntx.shift == 0) 358 exit(0); 359 ftemp = (double)ntx.ppsfreq / SCALE_FREQ; 360 gtemp = (double)ntx.stabil / SCALE_FREQ; 361 htemp = (double)ntx.jitter * nscale; 362 printf(" pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n", 363 ftemp, gtemp, htemp); 364 printf(" intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n", 365 (u_long)ntx.calcnt, (u_long)ntx.jitcnt, 366 (u_long)ntx.stbcnt, (u_long)ntx.errcnt); 367 return 0; 368 } 369 370 /* 371 * Put things back together the way we found them. 372 */ 373 lexit: 374 #ifdef SIGSYS 375 if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) { 376 perror("sigaction() fails to restore SIGSYS trap"); 377 exit(1); 378 } 379 #endif 380 exit(0); 381 } 382 383 #ifdef SIGSYS 384 /* 385 * pll_trap - trap processor for undefined syscalls 386 */ 387 void 388 pll_trap( 389 int arg 390 ) 391 { 392 pll_control--; 393 siglongjmp(env, 1); 394 } 395 #endif 396 397 /* 398 * Print a value a la the %b format of the kernel's printf 399 */ 400 const char * 401 sprintb( 402 u_int v, 403 const char * bits 404 ) 405 { 406 char *cp; 407 char *cplim; 408 int i; 409 int any; 410 char c; 411 static char buf[132]; 412 413 if (bits != NULL && *bits == 8) 414 snprintf(buf, sizeof(buf), "0%o", v); 415 else 416 snprintf(buf, sizeof(buf), "0x%x", v); 417 cp = buf + strlen(buf); 418 cplim = buf + sizeof(buf); 419 if (bits != NULL) { 420 bits++; 421 *cp++ = ' '; 422 *cp++ = '('; 423 any = FALSE; 424 while ((i = *bits++) != 0) { 425 if (v & (1 << (i - 1))) { 426 if (any) { 427 *cp++ = ','; 428 if (cp >= cplim) 429 goto overrun; 430 } 431 any = TRUE; 432 for (; (c = *bits) > 32; bits++) { 433 *cp++ = c; 434 if (cp >= cplim) 435 goto overrun; 436 } 437 } else { 438 for (; *bits > 32; bits++) 439 continue; 440 } 441 } 442 *cp++ = ')'; 443 if (cp >= cplim) 444 goto overrun; 445 } 446 *cp = '\0'; 447 return buf; 448 449 overrun: 450 return "sprintb buffer too small"; 451 } 452 453 const char * const timex_states[] = { 454 "OK", "INS", "DEL", "OOP", "WAIT", "ERROR" 455 }; 456 457 const char * 458 timex_state( 459 int s 460 ) 461 { 462 static char buf[32]; 463 464 if ((size_t)s < COUNTOF(timex_states)) 465 return timex_states[s]; 466 snprintf(buf, sizeof(buf), "TIME-#%d", s); 467 return buf; 468 } 469