1 /* $NetBSD: ntp_leapsec.c,v 1.1.1.1 2013/12/27 23:31:03 christos Exp $ */ 2 3 /* 4 * ntp_leapsec.c - leap second processing for NTPD 5 * 6 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 7 * The contents of 'html/copyright.html' apply. 8 * ---------------------------------------------------------------------- 9 * This is an attempt to get the leap second handling into a dedicated 10 * module to make the somewhat convoluted logic testable. 11 */ 12 13 #include <config.h> 14 #include <sys/types.h> 15 #include <ctype.h> 16 17 #include "ntp_types.h" 18 #include "ntp_fp.h" 19 #include "ntp_stdlib.h" 20 #include "ntp_calendar.h" 21 #include "ntp_leapsec.h" 22 #include "ntp.h" 23 24 /* --------------------------------------------------------------------- 25 * GCC is rather sticky with its 'const' attribute. We have to do it more 26 * explicit than with a cast if we want to get rid of a CONST qualifier. 27 * Greetings from the PASCAL world, where casting was only possible via 28 * untagged unions... 29 */ 30 static void* noconst(const void* ptr) 31 { 32 union { 33 const void * cp; 34 void * vp; 35 } tmp; 36 tmp.cp = ptr; 37 return tmp.vp; 38 } 39 40 /* --------------------------------------------------------------------- 41 * Things to put into libntp... 42 */ 43 44 vint64 45 strtouv64( 46 const char * begp, 47 char ** endp, 48 int base) 49 { 50 vint64 res; 51 u_char digit; 52 int sig, num; 53 const u_char *src; 54 55 num = sig = 0; 56 src = (const u_char*)begp; 57 while (isspace(*src)) 58 src++; 59 60 if (*src == '-') { 61 src++; 62 sig = 1; 63 } else if (*src == '+') { 64 src++; 65 } 66 67 if (base == 0) { 68 base = 10; 69 if (*src == '0') { 70 base = 8; 71 if (toupper(*++src) == 'X') { 72 src++; 73 base = 16; 74 } 75 } 76 } else if (base == 16) { /* remove optional leading '0x' or '0X' */ 77 if (src[0] == '0' && toupper(src[1]) == 'X') 78 src += 2; 79 } else if (base <= 2 || base > 36) { 80 memset(&res, 0xFF, sizeof(res)); 81 errno = ERANGE; 82 return res; 83 } 84 85 memset(&res, 0, sizeof(res)); 86 while (*src) { 87 if (isdigit(*src)) 88 digit = *src - '0'; 89 else if (isupper(*src)) 90 digit = *src - 'A' + 10; 91 else if (islower(*src)) 92 digit = *src - 'a' + 10; 93 else 94 break; 95 if (digit >= base) 96 break; 97 num = 1; 98 #if defined(HAVE_INT64) 99 res.Q_s = res.Q_s * base + digit; 100 #else 101 /* res *= base, using 16x16->32 bit 102 * multiplication. Slow but portable. 103 */ 104 { 105 uint32_t accu; 106 accu = (uint32_t)res.W_s.ll * base; 107 res.W_s.ll = (uint16_t)accu; 108 accu = (accu >> 16) 109 + (uint32_t)res.W_s.lh * base; 110 res.W_s.lh = (uint16_t)accu; 111 /* the upper bits can be done in one step: */ 112 res.D_s.hi = res.D_s.hi * base + (accu >> 16); 113 } 114 M_ADD(res.D_s.hi, res.D_s.lo, 0, digit); 115 #endif 116 src++; 117 } 118 if (!num) 119 errno = EINVAL; 120 if (endp) 121 *endp = (char*)noconst(src); 122 if (sig) 123 M_NEG(res.D_s.hi, res.D_s.lo); 124 return res; 125 } 126 127 int icmpv64( 128 const vint64 * lhs, 129 const vint64 * rhs) 130 { 131 int res; 132 133 #if defined(HAVE_INT64) 134 res = (lhs->q_s > rhs->q_s) 135 - (lhs->q_s < rhs->q_s); 136 #else 137 res = (lhs->d_s.hi > rhs->d_s.hi) 138 - (lhs->d_s.hi < rhs->d_s.hi); 139 if ( ! res ) 140 res = (lhs->D_s.lo > rhs->D_s.lo) 141 - (lhs->D_s.lo < rhs->D_s.lo); 142 #endif 143 144 return res; 145 } 146 147 148 int ucmpv64( 149 const vint64 * lhs, 150 const vint64 * rhs) 151 { 152 int res; 153 154 #if defined(HAVE_INT64) 155 res = (lhs->Q_s > rhs->Q_s) 156 - (lhs->Q_s < rhs->Q_s); 157 #else 158 res = (lhs->D_s.hi > rhs->D_s.hi) 159 - (lhs->D_s.hi < rhs->D_s.hi); 160 if ( ! res ) 161 res = (lhs->D_s.lo > rhs->D_s.lo) 162 - (lhs->D_s.lo < rhs->D_s.lo); 163 #endif 164 return res; 165 } 166 167 #if 0 168 static vint64 169 addv64( 170 const vint64 *lhs, 171 const vint64 *rhs) 172 { 173 vint64 res; 174 175 #if defined(HAVE_INT64) 176 res.Q_s = lhs->Q_s + rhs->Q_s; 177 #else 178 res = *lhs; 179 M_ADD(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo); 180 #endif 181 return res; 182 } 183 #endif 184 185 static vint64 186 subv64( 187 const vint64 *lhs, 188 const vint64 *rhs) 189 { 190 vint64 res; 191 192 #if defined(HAVE_INT64) 193 res.Q_s = lhs->Q_s - rhs->Q_s; 194 #else 195 res = *lhs; 196 M_SUB(res.D_s.hi, res.D_s.lo, rhs->D_s.hi, rhs->D_s.lo); 197 #endif 198 return res; 199 } 200 201 static vint64 202 addv64i32( 203 const vint64 * lhs, 204 int32_t rhs) 205 { 206 vint64 res; 207 208 res = *lhs; 209 #if defined(HAVE_INT64) 210 res.q_s += rhs; 211 #else 212 M_ADD(res.D_s.hi, res.D_s.lo, -(rhs < 0), rhs); 213 #endif 214 return res; 215 } 216 217 #if 0 218 static vint64 219 subv64i32( 220 const vint64 * lhs, 221 int32_t rhs) 222 { 223 vint64 res; 224 225 res = *lhs; 226 #if defined(HAVE_INT64) 227 res.q_s -= rhs; 228 #else 229 M_SUB(res.D_s.hi, res.D_s.lo, -(rhs < 0), rhs); 230 #endif 231 return res; 232 } 233 #endif 234 235 #if 0 236 static vint64 237 addv64u32( 238 const vint64 * lhs, 239 uint32_t rhs) 240 { 241 vint64 res; 242 243 res = *lhs; 244 #if defined(HAVE_INT64) 245 res.Q_s += rhs; 246 #else 247 M_ADD(res.D_s.hi, res.D_s.lo, 0, rhs); 248 #endif 249 return res; 250 } 251 #endif 252 253 static vint64 254 subv64u32( 255 const vint64 * lhs, 256 uint32_t rhs) 257 { 258 vint64 res; 259 260 res = *lhs; 261 #if defined(HAVE_INT64) 262 res.Q_s -= rhs; 263 #else 264 M_SUB(res.D_s.hi, res.D_s.lo, 0, rhs); 265 #endif 266 return res; 267 } 268 269 /* --------------------------------------------------------------------- 270 * Things to put into ntp_calendar... (and consequently into libntp...) 271 */ 272 273 /* ------------------------------------------------------------------ */ 274 static int 275 ntpcal_ntp64_to_date( 276 struct calendar *jd, 277 const vint64 *ntp) 278 { 279 ntpcal_split ds; 280 281 ds = ntpcal_daysplit(ntp); 282 ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 283 284 return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 285 } 286 287 /* ------------------------------------------------------------------ */ 288 static vint64 289 ntpcal_date_to_ntp64( 290 const struct calendar *jd) 291 { 292 return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 293 ntpcal_date_to_daysec(jd)); 294 } 295 296 297 /* --------------------------------------------------------------------- 298 * Our internal data structure 299 */ 300 #define MAX_HIST 10 /* history of leap seconds */ 301 302 struct leap_info { 303 vint64 ttime; /* transition time (after the step, ntp scale) */ 304 uint32_t stime; /* schedule limit (a month before transition) */ 305 int16_t taiof; /* TAI offset on and after the transition */ 306 uint8_t dynls; /* dynamic: inserted on peer/clock request */ 307 }; 308 typedef struct leap_info leap_info_t; 309 310 struct leap_head { 311 vint64 update; /* time of information update */ 312 vint64 expire; /* table expiration time */ 313 uint16_t size; /* number of infos in table */ 314 int16_t base_tai; /* total leaps before first entry */ 315 int16_t this_tai; /* current TAI offset */ 316 int16_t next_tai; /* TAI offset after 'when' */ 317 vint64 dtime; /* due time (current era end) */ 318 vint64 ttime; /* nominal transition time (next era start) */ 319 vint64 stime; /* schedule time (when we take notice) */ 320 vint64 ebase; /* base time of this leap era */ 321 uint8_t dynls; /* next leap is dynamic (by peer request) */ 322 }; 323 typedef struct leap_head leap_head_t; 324 325 struct leap_table { 326 leap_signature_t lsig; 327 leap_head_t head; 328 leap_info_t info[MAX_HIST]; 329 }; 330 331 /* Where we store our tables */ 332 static leap_table_t _ltab[2], *_lptr; 333 static int/*BOOL*/ _electric; 334 335 /* Forward decls of local helpers */ 336 static int add_range(leap_table_t*, const leap_info_t*); 337 static char * get_line(leapsec_reader, void*, char*, size_t); 338 static char * skipws(const char*); 339 static int parsefail(const char * cp, const char * ep); 340 static void reload_limits(leap_table_t*, const vint64*); 341 static int betweenu32(uint32_t, uint32_t, uint32_t); 342 static void reset_times(leap_table_t*); 343 static int leapsec_add(leap_table_t*, const vint64*, int); 344 static int leapsec_raw(leap_table_t*, const vint64 *, int, int); 345 346 /* ===================================================================== 347 * Get & Set the current leap table 348 */ 349 350 /* ------------------------------------------------------------------ */ 351 leap_table_t * 352 leapsec_get_table( 353 int alternate) 354 { 355 leap_table_t *p1, *p2; 356 357 p1 = _lptr; 358 p1 = &_ltab[p1 == &_ltab[1]]; 359 p2 = &_ltab[p1 == &_ltab[0]]; 360 if (alternate) { 361 memcpy(p2, p1, sizeof(leap_table_t)); 362 p1 = p2; 363 } 364 365 return p1; 366 } 367 368 /* ------------------------------------------------------------------ */ 369 int/*BOOL*/ 370 leapsec_set_table( 371 leap_table_t * pt) 372 { 373 if (pt == &_ltab[0] || pt == &_ltab[1]) 374 _lptr = pt; 375 return _lptr == pt; 376 } 377 378 /* ------------------------------------------------------------------ */ 379 int/*BOOL*/ 380 leapsec_electric( 381 int/*BOOL*/ on) 382 { 383 int res = _electric; 384 if (on < 0) 385 return res; 386 387 _electric = (on != 0); 388 if (_electric == res) 389 return res; 390 391 if (_lptr == &_ltab[0] || _lptr == &_ltab[1]) 392 reset_times(_lptr); 393 394 return res; 395 } 396 397 /* ===================================================================== 398 * API functions that operate on tables 399 */ 400 401 /* --------------------------------------------------------------------- 402 * Clear all leap second data. Use it for init & cleanup 403 */ 404 void 405 leapsec_clear( 406 leap_table_t * pt) 407 { 408 memset(&pt->lsig, 0, sizeof(pt->lsig)); 409 memset(&pt->head, 0, sizeof(pt->head)); 410 reset_times(pt); 411 } 412 413 /* --------------------------------------------------------------------- 414 * Load a leap second file and check expiration on the go 415 */ 416 int/*BOOL*/ 417 leapsec_load( 418 leap_table_t * pt , 419 leapsec_reader func, 420 void * farg, 421 int use_build_limit) 422 { 423 char *cp, *ep, linebuf[50]; 424 vint64 ttime, limit; 425 long taiof; 426 struct calendar build; 427 428 leapsec_clear(pt); 429 if (use_build_limit && ntpcal_get_build_date(&build)) 430 limit = ntpcal_date_to_ntp64(&build); 431 else 432 memset(&limit, 0, sizeof(limit)); 433 434 while (get_line(func, farg, linebuf, sizeof(linebuf))) { 435 cp = linebuf; 436 if (*cp == '#') { 437 cp++; 438 if (*cp == '@') { 439 cp = skipws(cp+1); 440 pt->head.expire = strtouv64(cp, &ep, 10); 441 if (parsefail(cp, ep)) 442 goto fail_read; 443 pt->lsig.etime = pt->head.expire.D_s.lo; 444 } else if (*cp == '$') { 445 cp = skipws(cp+1); 446 pt->head.update = strtouv64(cp, &ep, 10); 447 if (parsefail(cp, ep)) 448 goto fail_read; 449 } 450 } else if (isdigit((u_char)*cp)) { 451 ttime = strtouv64(cp, &ep, 10); 452 if (parsefail(cp, ep)) 453 goto fail_read; 454 cp = skipws(ep); 455 taiof = strtol(cp, &ep, 10); 456 if ( parsefail(cp, ep) 457 || taiof > SHRT_MAX || taiof < SHRT_MIN) 458 goto fail_read; 459 if (ucmpv64(&ttime, &limit) >= 0) { 460 if (!leapsec_raw(pt, &ttime, 461 taiof, FALSE)) 462 goto fail_insn; 463 } else { 464 pt->head.base_tai = (int16_t)taiof; 465 } 466 pt->lsig.ttime = ttime.D_s.lo; 467 pt->lsig.taiof = (int16_t)taiof; 468 } 469 } 470 return TRUE; 471 472 fail_read: 473 errno = EILSEQ; 474 fail_insn: 475 leapsec_clear(pt); 476 return FALSE; 477 } 478 479 /* --------------------------------------------------------------------- 480 * Dump a table in human-readable format. Use 'fprintf' and a FILE 481 * pointer if you want to get it printed into a stream. 482 */ 483 void 484 leapsec_dump( 485 const leap_table_t * pt , 486 leapsec_dumper func, 487 void * farg) 488 { 489 int idx; 490 vint64 ts; 491 struct calendar atb, ttb; 492 493 ntpcal_ntp64_to_date(&ttb, &pt->head.expire); 494 (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n", 495 pt->head.size, 496 ttb.year, ttb.month, ttb.monthday); 497 idx = pt->head.size; 498 while (idx-- != 0) { 499 ts = pt->info[idx].ttime; 500 ntpcal_ntp64_to_date(&ttb, &ts); 501 ts = subv64u32(&ts, pt->info[idx].stime); 502 ntpcal_ntp64_to_date(&atb, &ts); 503 504 (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n", 505 ttb.year, ttb.month, ttb.monthday, 506 "-*"[pt->info[idx].dynls != 0], 507 atb.year, atb.month, atb.monthday, 508 pt->info[idx].taiof); 509 } 510 } 511 512 /* ===================================================================== 513 * usecase driven API functions 514 */ 515 516 int/*BOOL*/ 517 leapsec_query( 518 leap_result_t * qr , 519 uint32_t ts32 , 520 const time_t * pivot) 521 { 522 leap_table_t * pt; 523 vint64 ts64, last, next; 524 uint32_t due32; 525 int fired; 526 527 /* preset things we use later on... */ 528 fired = FALSE; 529 ts64 = ntpcal_ntp_to_ntp(ts32, pivot); 530 pt = leapsec_get_table(FALSE); 531 memset(qr, 0, sizeof(leap_result_t)); 532 533 if (ucmpv64(&ts64, &pt->head.ebase) < 0) { 534 /* Most likely after leap frame reset. Could also be a 535 * backstep of the system clock. Anyway, get the new 536 * leap era frame. 537 */ 538 reload_limits(pt, &ts64); 539 } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) { 540 /* Boundary crossed in forward direction. This might 541 * indicate a leap transition, so we prepare for that 542 * case. 543 * 544 * Some operations below are actually NOPs in electric 545 * mode, but having only one code path that works for 546 * both modes is easier to maintain. 547 */ 548 last = pt->head.ttime; 549 qr->warped = (int16_t)(last.D_s.lo - 550 pt->head.dtime.D_s.lo); 551 next = addv64i32(&ts64, qr->warped); 552 reload_limits(pt, &next); 553 fired = ucmpv64(&pt->head.ebase, &last) == 0; 554 if (fired) { 555 ts64 = next; 556 ts32 = next.D_s.lo; 557 } else { 558 qr->warped = 0; 559 } 560 } 561 562 qr->tai_offs = pt->head.this_tai; 563 564 /* If before the next scheduling alert, we're done. */ 565 if (ucmpv64(&ts64, &pt->head.stime) < 0) 566 return fired; 567 568 /* now start to collect the remaing data */ 569 due32 = pt->head.dtime.D_s.lo; 570 571 qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 572 qr->ttime = pt->head.ttime; 573 qr->ddist = due32 - ts32; 574 qr->dynamic = pt->head.dynls; 575 qr->proximity = LSPROX_SCHEDULE; 576 577 /* if not in the last day before transition, we're done. */ 578 if (!betweenu32(due32 - SECSPERDAY, ts32, due32)) 579 return fired; 580 581 qr->proximity = LSPROX_ANNOUNCE; 582 if (!betweenu32(due32 - 10, ts32, due32)) 583 return fired; 584 585 /* The last 10s before the transition. Prepare for action! */ 586 qr->proximity = LSPROX_ALERT; 587 return fired; 588 } 589 590 /* ------------------------------------------------------------------ */ 591 int/*BOOL*/ 592 leapsec_frame( 593 leap_result_t *qr) 594 { 595 const leap_table_t * pt; 596 597 memset(qr, 0, sizeof(leap_result_t)); 598 pt = leapsec_get_table(FALSE); 599 if (ucmpv64(&pt->head.ttime, &pt->head.stime) <= 0) 600 return FALSE; 601 602 qr->tai_offs = pt->head.this_tai; 603 qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 604 qr->ttime = pt->head.ttime; 605 qr->dynamic = pt->head.dynls; 606 607 return TRUE; 608 } 609 610 /* ------------------------------------------------------------------ */ 611 /* Reset the current leap frame */ 612 void 613 leapsec_reset_frame(void) 614 { 615 reset_times(leapsec_get_table(FALSE)); 616 } 617 618 /* ------------------------------------------------------------------ */ 619 int/*BOOL*/ 620 leapsec_load_file( 621 FILE * ifp , 622 int blimit) 623 { 624 leap_table_t * pt; 625 626 pt = leapsec_get_table(TRUE); 627 return leapsec_load(pt, (leapsec_reader)getc, ifp, blimit) 628 && leapsec_set_table(pt); 629 } 630 631 /* ------------------------------------------------------------------ */ 632 void 633 leapsec_getsig( 634 leap_signature_t * psig) 635 { 636 const leap_table_t * pt; 637 638 pt = leapsec_get_table(FALSE); 639 memcpy(psig, &pt->lsig, sizeof(leap_signature_t)); 640 } 641 642 /* ------------------------------------------------------------------ */ 643 int/*BOOL*/ 644 leapsec_expired( 645 uint32_t when, 646 const time_t * tpiv) 647 { 648 const leap_table_t * pt; 649 vint64 limit; 650 651 pt = leapsec_get_table(FALSE); 652 limit = ntpcal_ntp_to_ntp(when, tpiv); 653 return ucmpv64(&limit, &pt->head.expire) >= 0; 654 } 655 656 /* ------------------------------------------------------------------ */ 657 int32_t 658 leapsec_daystolive( 659 uint32_t when, 660 const time_t * tpiv) 661 { 662 const leap_table_t * pt; 663 vint64 limit; 664 665 pt = leapsec_get_table(FALSE); 666 limit = ntpcal_ntp_to_ntp(when, tpiv); 667 limit = subv64(&pt->head.expire, &limit); 668 return ntpcal_daysplit(&limit).hi; 669 } 670 671 /* ------------------------------------------------------------------ */ 672 int/*BOOL*/ 673 leapsec_add_fix( 674 int total, 675 uint32_t ttime, 676 uint32_t etime, 677 const time_t * pivot) 678 { 679 time_t tpiv; 680 leap_table_t * pt; 681 vint64 tt64, et64; 682 683 if (pivot == NULL) { 684 time(&tpiv); 685 pivot = &tpiv; 686 } 687 688 et64 = ntpcal_ntp_to_ntp(etime, pivot); 689 tt64 = ntpcal_ntp_to_ntp(ttime, pivot); 690 pt = leapsec_get_table(TRUE); 691 692 if ( ucmpv64(&et64, &pt->head.expire) <= 0 693 || !leapsec_raw(pt, &tt64, total, FALSE) ) 694 return FALSE; 695 696 pt->lsig.etime = etime; 697 pt->lsig.ttime = ttime; 698 pt->lsig.taiof = (int16_t)total; 699 700 pt->head.expire = et64; 701 702 return leapsec_set_table(pt); 703 } 704 705 /* ------------------------------------------------------------------ */ 706 int/*BOOL*/ 707 leapsec_add_dyn( 708 int insert, 709 uint32_t ntpnow, 710 const time_t * pivot ) 711 { 712 leap_table_t * pt; 713 vint64 now64; 714 715 pt = leapsec_get_table(TRUE); 716 now64 = ntpcal_ntp_to_ntp(ntpnow, pivot); 717 return leapsec_add(pt, &now64, (insert != 0)) 718 && leapsec_set_table(pt); 719 } 720 721 /* ===================================================================== 722 * internal helpers 723 */ 724 725 /* [internal] Reset / init the time window in the leap processor to 726 * force reload on next query. Since a leap transition cannot take place 727 * at an odd second, the value chosen avoids spurious leap transition 728 * triggers. Making all three times equal forces a reload. Using the 729 * maximum value for unsigned 64 bits makes finding the next leap frame 730 * a bit easier. 731 */ 732 static void 733 reset_times( 734 leap_table_t * pt) 735 { 736 memset(&pt->head.ebase, 0xFF, sizeof(vint64)); 737 pt->head.stime = pt->head.ebase; 738 pt->head.ttime = pt->head.ebase; 739 pt->head.dtime = pt->head.ebase; 740 } 741 742 /* [internal] Add raw data to the table, removing old entries on the 743 * fly. This cannot fail currently. 744 */ 745 static int/*BOOL*/ 746 add_range( 747 leap_table_t * pt, 748 const leap_info_t * pi) 749 { 750 /* If the table is full, make room by throwing out the oldest 751 * entry. But remember the accumulated leap seconds! 752 */ 753 if (pt->head.size >= MAX_HIST) { 754 pt->head.size = MAX_HIST - 1; 755 pt->head.base_tai = pt->info[pt->head.size].taiof; 756 } 757 758 /* make room in lower end and insert item */ 759 memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info)); 760 pt->info[0] = *pi; 761 pt->head.size++; 762 763 /* invalidate the cached limit data -- we might have news ;-) 764 * 765 * This blocks a spurious transition detection. OTOH, if you add 766 * a value after the last query before a leap transition was 767 * expected to occur, this transition trigger is lost. But we 768 * can probably live with that. 769 */ 770 reset_times(pt); 771 return TRUE; 772 } 773 774 /* [internal] given a reader function, read characters into a buffer 775 * until either EOL or EOF is reached. Makes sure that the buffer is 776 * always NUL terminated, but silently truncates excessive data. The 777 * EOL-marker ('\n') is *not* stored in the buffer. 778 * 779 * Returns the pointer to the buffer, unless EOF was reached when trying 780 * to read the first character of a line. 781 */ 782 static char * 783 get_line( 784 leapsec_reader func, 785 void * farg, 786 char * buff, 787 size_t size) 788 { 789 int ch; 790 char *ptr; 791 792 /* if we cannot even store the delimiter, declare failure */ 793 if (buff == NULL || size == 0) 794 return NULL; 795 796 ptr = buff; 797 while (EOF != (ch = (*func)(farg)) && '\n' != ch) 798 if (size > 1) { 799 size--; 800 *ptr++ = (char)ch; 801 } 802 /* discard trailing whitespace */ 803 while (ptr != buff && isspace((u_char)ptr[-1])) 804 ptr--; 805 *ptr = '\0'; 806 return (ptr == buff && ch == EOF) ? NULL : buff; 807 } 808 809 /* [internal] skips whitespace characters from a character buffer. */ 810 static char * 811 skipws( 812 const char *ptr) 813 { 814 while (isspace((u_char)*ptr)) 815 ptr++; 816 return (char*)noconst(ptr); 817 } 818 819 /* [internal] check if a strtoXYZ ended at EOL or whistespace and 820 * converted something at all. Return TRUE if something went wrong. 821 */ 822 static int/*BOOL*/ 823 parsefail( 824 const char * cp, 825 const char * ep) 826 { 827 return (cp == ep) 828 || (*ep && *ep != '#' && !isspace((u_char)*ep)); 829 } 830 831 /* [internal] reload the table limits around the given time stamp. This 832 * is where the real work is done when it comes to table lookup and 833 * evaluation. Some care has been taken to have correct code for dealing 834 * with boundary conditions and empty tables. 835 * 836 * In electric mode, transition and trip time are the same. In dumb 837 * mode, the difference of the TAI offsets must be taken into account 838 * and trip time and transition time become different. The difference 839 * becomes the warping distance when the trip time is reached. 840 */ 841 static void 842 reload_limits( 843 leap_table_t * pt, 844 const vint64 * ts) 845 { 846 int idx; 847 848 /* Get full time and search the true lower bound. Use a 849 * simple loop here, since the number of entries does 850 * not warrant a binary search. This also works for an empty 851 * table, so there is no shortcut for that case. 852 */ 853 for (idx = 0; idx != pt->head.size; idx++) 854 if (ucmpv64(ts, &pt->info[idx].ttime) >= 0) 855 break; 856 857 /* get time limits with proper bound conditions. Note that the 858 * bounds of the table will be observed even if the table is 859 * empty -- no undefined condition must arise from this code. 860 */ 861 if (idx >= pt->head.size) { 862 memset(&pt->head.ebase, 0x00, sizeof(vint64)); 863 pt->head.this_tai = pt->head.base_tai; 864 } else { 865 pt->head.ebase = pt->info[idx].ttime; 866 pt->head.this_tai = pt->info[idx].taiof; 867 } 868 if (--idx >= 0) { 869 pt->head.next_tai = pt->info[idx].taiof; 870 pt->head.dynls = pt->info[idx].dynls; 871 pt->head.ttime = pt->info[idx].ttime; 872 873 if (_electric) 874 pt->head.dtime = pt->head.ttime; 875 else 876 pt->head.dtime = addv64i32( 877 &pt->head.ttime, 878 pt->head.next_tai - pt->head.this_tai); 879 880 pt->head.stime = subv64u32( 881 &pt->head.ttime, pt->info[idx].stime); 882 883 } else { 884 memset(&pt->head.ttime, 0xFF, sizeof(vint64)); 885 pt->head.stime = pt->head.ttime; 886 pt->head.dtime = pt->head.ttime; 887 pt->head.next_tai = pt->head.this_tai; 888 pt->head.dynls = 0; 889 } 890 } 891 892 /* [internal] Take a time stamp and create a leap second frame for 893 * it. This will schedule a leap second for the beginning of the next 894 * month, midnight UTC. The 'insert' argument tells if a leap second is 895 * added (!=0) or removed (==0). We do not handle multiple inserts 896 * (yet?) 897 * 898 * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 899 * insert a leap second into the current history -- only appending 900 * towards the future is allowed!) 901 */ 902 static int/*BOOL*/ 903 leapsec_add( 904 leap_table_t* pt , 905 const vint64 * now64 , 906 int insert) 907 { 908 vint64 ttime, stime; 909 struct calendar fts; 910 leap_info_t li; 911 912 /* Check against the table expiration and the lates available 913 * leap entry. Do not permit inserts, only appends, and only if 914 * the extend the table beyond the expiration! 915 */ 916 if ( ucmpv64(now64, &pt->head.expire) < 0 917 || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) { 918 errno = ERANGE; 919 return FALSE; 920 } 921 922 ntpcal_ntp64_to_date(&fts, now64); 923 /* To guard against dangling leap flags: do not accept leap 924 * second request on the 1st hour of the 1st day of the month. 925 */ 926 if (fts.monthday == 1 && fts.hour == 0) { 927 errno = EINVAL; 928 return FALSE; 929 } 930 931 /* Ok, do the remaining calculations */ 932 fts.monthday = 1; 933 fts.hour = 0; 934 fts.minute = 0; 935 fts.second = 0; 936 stime = ntpcal_date_to_ntp64(&fts); 937 fts.month++; 938 ttime = ntpcal_date_to_ntp64(&fts); 939 940 li.ttime = ttime; 941 li.stime = ttime.D_s.lo - stime.D_s.lo; 942 li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai) 943 + (insert ? 1 : -1); 944 li.dynls = 1; 945 return add_range(pt, &li); 946 } 947 948 /* [internal] Given a time stamp for a leap insertion (the exact begin 949 * of the new leap era), create new leap frame and put it into the 950 * table. This is the work horse for reading a leap file and getting a 951 * leap second update via authenticated network packet. 952 */ 953 int/*BOOL*/ 954 leapsec_raw( 955 leap_table_t * pt, 956 const vint64 * ttime, 957 int taiof, 958 int dynls) 959 { 960 vint64 stime; 961 struct calendar fts; 962 leap_info_t li; 963 964 /* Check that we only extend the table. Paranoia rulez! */ 965 if (pt->head.size && ucmpv64(ttime, &pt->info[0].ttime) <= 0) { 966 errno = ERANGE; 967 return FALSE; 968 } 969 970 ntpcal_ntp64_to_date(&fts, ttime); 971 /* If this does not match the exact month start, bail out. */ 972 if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { 973 errno = EINVAL; 974 return FALSE; 975 } 976 fts.month--; /* was in range 1..12, no overflow here! */ 977 stime = ntpcal_date_to_ntp64(&fts); 978 li.ttime = *ttime; 979 li.stime = ttime->D_s.lo - stime.D_s.lo; 980 li.taiof = (int16_t)taiof; 981 li.dynls = (dynls != 0); 982 return add_range(pt, &li); 983 } 984 985 /* [internal] Do a wrap-around save range inclusion check. 986 * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full 987 * handling of an overflow / wrap-around. 988 */ 989 static int/*BOOL*/ 990 betweenu32( 991 uint32_t lo, 992 uint32_t x, 993 uint32_t hi) 994 { 995 int rc; 996 if (lo <= hi) 997 rc = (lo <= x) && (x < hi); 998 else 999 rc = (lo <= x) || (x < hi); 1000 return rc; 1001 } 1002 1003 /* -*- that's all folks! -*- */ 1004