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 */ 69const char * sprintb (u_int, const char *); 70const char * timex_state (int); 71 72#ifdef SIGSYS 73void pll_trap (int); 74 75static struct sigaction newsigsys; /* new sigaction status */ 76static struct sigaction sigsys; /* current sigaction status */ 77static sigjmp_buf env; /* environment var. for pll_trap() */ 78#endif 79 80static volatile int pll_control; /* (0) daemon, (1) kernel loop */ 81static volatile int status; /* most recent status bits */ 82static volatile int flash; /* most recent ntp_adjtime() bits */ 83char const * progname; 84static char optargs[] = "MNT:cde:f:hm:o:rs:t:"; 85 86int 87main( 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 */ 387void 388pll_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 */ 400const char * 401sprintb( 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 453const char * const timex_states[] = { 454 "OK", "INS", "DEL", "OOP", "WAIT", "ERROR" 455}; 456 457const char * 458timex_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