Home | History | Annotate | Line # | Download | only in ntpd
check_y2k.c revision 1.1.1.3
      1 /* check_y2k.c -- test ntp code constructs for Y2K correctness 	Y2KFixes [*/
      2 
      3   /*
      4 	Code invoked by `make check`. Not part of ntpd and not to be
      5 	installed.
      6 
      7 	On any code I even wonder about, I've cut and pasted the code
      8 	here and ran it as a test case just to be sure.
      9 
     10 	For code not in "ntpd" proper, we have tried to call most
     11 	repaired functions from herein to properly test them
     12 	(something never done before!). This has found several bugs,
     13 	not normal Y2K bugs, that will strike in Y2K so repair them
     14 	we did.
     15 
     16 	Program exits with 0 on success, 1 on Y2K failure (stdout messages).
     17 	Exit of 2 indicates internal logic bug detected OR failure of
     18 	what should be our correct formulas.
     19 
     20 	While "make check" should only check logic for source within that
     21 	specific directory, this check goes outside the scope of the local
     22 	directory.  It's not a perfect world (besides, there is a lot of
     23 	interdependence here, and it really needs to be tested in
     24 	a controled order).
     25    */
     26 
     27 /* { definitions lifted from ntpd.c to allow us to complie with
     28      "#include ntp.h".  I have not taken the time to reduce the clutter. */
     29 
     30 #ifdef HAVE_CONFIG_H
     31 # include <config.h>
     32 #endif
     33 
     34 #include "ntpd.h"
     35 
     36 #ifdef HAVE_UNISTD_H
     37 # include <unistd.h>
     38 #endif
     39 #ifdef HAVE_SYS_STAT_H
     40 # include <sys/stat.h>
     41 #endif
     42 #include <stdio.h>
     43 #include <errno.h>
     44 #ifndef SYS_WINNT
     45 # if !defined(VMS)	/*wjm*/
     46 #  include <sys/param.h>
     47 # endif /* VMS */
     48 # if HAVE_SYS_SIGNAL_H
     49 #  include <sys/signal.h>
     50 # endif /* HAVE_SYS_SIGNAL_H */
     51 # include <sys/signal.h>
     52 # ifdef HAVE_SYS_IOCTL_H
     53 #  include <sys/ioctl.h>
     54 # endif /* HAVE_SYS_IOCTL_H */
     55 # if !defined(VMS)	/*wjm*/
     56 #  include <sys/resource.h>
     57 # endif /* VMS */
     58 #else
     59 # include <signal.h>
     60 # include <process.h>
     61 # include <io.h>
     62 # include "../libntp/log.h"
     63 #endif /* SYS_WINNT */
     64 #if defined(HAVE_RTPRIO)
     65 # ifdef HAVE_SYS_RESOURCE_H
     66 #  include <sys/resource.h>
     67 # endif
     68 # ifdef HAVE_SYS_LOCK_H
     69 #  include <sys/lock.h>
     70 # endif
     71 # include <sys/rtprio.h>
     72 #else
     73 # ifdef HAVE_PLOCK
     74 #  ifdef HAVE_SYS_LOCK_H
     75 #	include <sys/lock.h>
     76 #  endif
     77 # endif
     78 #endif
     79 #if defined(HAVE_SCHED_SETSCHEDULER)
     80 # ifdef HAVE_SCHED_H
     81 #  include <sched.h>
     82 # else
     83 #  ifdef HAVE_SYS_SCHED_H
     84 #   include <sys/sched.h>
     85 #  endif
     86 # endif
     87 #endif
     88 #if defined(HAVE_SYS_MMAN_H)
     89 # include <sys/mman.h>
     90 #endif
     91 
     92 #ifdef HAVE_TERMIOS_H
     93 # include <termios.h>
     94 #endif
     95 
     96 #ifdef SYS_DOMAINOS
     97 # include <apollo/base.h>
     98 #endif /* SYS_DOMAINOS */
     99 
    100 /* } end definitions lifted from ntpd.c */
    101 
    102 #include "ntp_calendar.h"
    103 #include "parse.h"
    104 
    105 #define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 )
    106 
    107 char const *progname = "check_y2k";
    108 
    109 long
    110 Days ( int Year )		/* return number of days since year "0" */
    111 {
    112     long  Return;
    113 		/* this is a known to be good algorithm */
    114     Return = Year * 365;	/* first aproximation to the value */
    115     if ( Year >= 1 )
    116     {		/* see notes in libparse/parse.c if you want a PROPER
    117 		 * **generic algorithm. */
    118 	Return += (Year+3) / 4;		/* add in (too many) leap days */
    119 	Return -= (Year-1) / 100;	/* reduce by (too many) centurys */
    120 	Return += (Year-1) / 400;	/* get final answer */
    121     }
    122 
    123     return Return;
    124 }
    125 
    126 static int  year0 = 1900;	/* sarting year for NTP time */
    127 static int  yearend;		/* ending year we test for NTP time.
    128 				    * 32-bit systems: through 2036, the
    129 				      **year in which NTP time overflows.
    130 				    * 64-bit systems: a reasonable upper
    131 				      **limit (well, maybe somewhat beyond
    132 				      **reasonable, but well before the
    133 				      **max time, by which time the earth
    134 				      **will be dead.) */
    135 static time_t Time;
    136 static struct tm LocalTime;
    137 
    138 #define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \
    139 	Warnings++; else Fatals++
    140 
    141 int
    142 main( void )
    143 {
    144     int Fatals;
    145     int Warnings;
    146     int  year;
    147 
    148     Time = time( (time_t *)NULL )
    149 #ifdef TESTTIMEOFFSET
    150 		+ test_time_offset
    151 #endif
    152 	;
    153     LocalTime = *localtime( &Time );
    154 
    155     year = ( sizeof( u_long ) > 4 ) 	/* save max span using year as temp */
    156 		? ( 400 * 3 ) 		/* three greater gregorian cycles */
    157 		: ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/
    158 			/* NOTE: will automacially expand test years on
    159 			 * 64 bit machines.... this may cause some of the
    160 			 * existing ntp logic to fail for years beyond
    161 			 * 2036 (the current 32-bit limit). If all checks
    162 			 * fail ONLY beyond year 2036 you may ignore such
    163 			 * errors, at least for a decade or so. */
    164     yearend = year0 + year;
    165 
    166     puts( " internal self check" );
    167   {		/* verify our own logic used to verify repairs */
    168     unsigned long days;
    169 
    170     if ( year0 >= yearend )
    171     {
    172 	fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d  (span=%d)\n",
    173 		(int)year0, (int)yearend, (int)year );
    174 	exit(2);
    175     }
    176 
    177    {
    178     int  save_year;
    179 
    180     save_year = LocalTime.tm_year;	/* save current year */
    181 
    182     year = 1980;
    183     LocalTime.tm_year = year - 1900;
    184     Fatals = Warnings = 0;
    185     Error(year);		/* should increment Fatals */
    186     if ( Fatals == 0 )
    187     {
    188 	fprintf( stdout,
    189 	    "%4d: %s(%d): FATAL DID NOT INCREMENT  (Fatals=%d Warnings=%d)\n",
    190 	    (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
    191 	exit(2);
    192     }
    193 
    194     year = 2100;		/* test year > limit but CURRENT year < limit */
    195     Fatals = Warnings = 0;
    196     Error(year);		/* should increment Fatals */
    197     if ( Warnings == 0 )
    198     {
    199 	fprintf( stdout,
    200 	    "%4d: %s(%d): WARNING DID NOT INCREMENT  (Fatals=%d Warnings=%d)\n",
    201 	    (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
    202 	exit(2);
    203     }
    204     Fatals = Warnings = 0;
    205     LocalTime.tm_year = year - 1900;	/* everything > limit */
    206     Error(1980);		/* should increment Fatals */
    207     if ( Fatals == 0 )
    208     {
    209 	fprintf( stdout,
    210 	    "%4d: %s(%d): FATALS DID NOT INCREMENT  (Fatals=%d Warnings=%d)\n",
    211 	    (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
    212 	exit(2);
    213     }
    214 
    215     LocalTime.tm_year = save_year;
    216    }
    217 
    218     days = 365+1;		/* days in year 0 + 1 more day */
    219     for ( year = 1; year <= 2500; year++ )
    220     {
    221 	long   Test;
    222 	Test = Days( year );
    223 	if ( days != Test )
    224 	{
    225 	    fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n",
    226 		year, (long)days, (long)Test );
    227 	    exit(2);		/* would throw off many other tests */
    228 	}
    229 
    230 	Test = julian0(year);		/* compare with julian0() macro */
    231 	if ( days != Test )
    232 	{
    233 	    fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n",
    234 		year, (long)days, (long)Test );
    235 	    exit(2);		/* would throw off many other tests */
    236 	}
    237 
    238 	days += 365;
    239 	if ( isleap_4(year) ) days++;
    240     }
    241 
    242     if ( isleap_4(1999) )
    243     {
    244 	fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
    245 	exit(2);
    246     }
    247     if ( !isleap_4(2000) )
    248     {
    249 	fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" );
    250 	exit(2);
    251     }
    252     if ( isleap_4(2001) )
    253     {
    254 	fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
    255 	exit(2);
    256     }
    257 
    258     if ( !isleap_tm(2000-1900) )
    259     {
    260 	fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" );
    261 	exit(2);
    262     }
    263   }
    264 
    265     Fatals = Warnings = 0;
    266 
    267     puts( " include/ntp.h" );
    268   {		/* test our new isleap_*() #define "functions" */
    269 
    270     for ( year = 1400; year <= 2200; year++ )
    271     {
    272 	int  LeapSw;
    273 	int  IsLeapSw;
    274 
    275 	LeapSw = GoodLeap(year);
    276 	IsLeapSw = isleap_4(year);
    277 
    278 	if ( !!LeapSw != !!IsLeapSw )
    279 	{
    280 	    Error(year);
    281 	    fprintf( stdout,
    282 		"  %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
    283 	    break;
    284 	}
    285 
    286 	IsLeapSw = isleap_tm(year-1900);
    287 
    288 	if ( !!LeapSw != !!IsLeapSw )
    289 	{
    290 	    Error(year);
    291 	    fprintf( stdout,
    292 		"  %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
    293 	    break;
    294 	}
    295     }
    296   }
    297 
    298     puts( " include/ntp_calendar.h" );
    299   {		/* I belive this is good, but just to be sure... */
    300 
    301 	/* we are testing this #define */
    302 #define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0)))
    303 
    304     for ( year = 1400; year <= 2200; year++ )
    305     {
    306 	int  LeapSw;
    307 
    308 	LeapSw = GoodLeap(year);
    309 
    310 	if ( !(!LeapSw) != !(!is_leapyear(year)) )
    311 	{
    312 	    Error(year);
    313 	    fprintf( stdout,
    314 		"  %4d %2d *** ERROR\n", year, LeapSw );
    315 	    break;
    316 	}
    317     }
    318   }
    319 
    320 
    321     puts( " libparse/parse.c" );
    322   {
    323     long Days1970;	/* days from 1900 to 1970 */
    324 
    325     struct ParseTime	/* womp up a test structure to all cut/paste code */
    326     {
    327        int   year;
    328     } Clock_Time, *clock_time;
    329 
    330     clock_time = &Clock_Time;
    331 
    332 	/* first test this #define */
    333 #define days_per_year(x)  ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366))
    334 
    335     for ( year = 1400; year <= 2200; year++ )
    336     {
    337 	int  LeapSw;
    338 	int  DayCnt;
    339 
    340 	LeapSw = GoodLeap(year);
    341 	DayCnt = (int)days_per_year(year);
    342 
    343 	if ( ( LeapSw ? 366 : 365 ) != DayCnt )
    344 	{
    345 	    Error(year);
    346 	    fprintf( stdout,
    347 		    "  days_per_year() %4d %2d %3d *** ERROR\n",
    348 		    year, LeapSw, DayCnt );
    349 	    break;
    350 	}
    351     }
    352 
    353     /* test (what is now julian0) calculations */
    354 
    355     Days1970 = Days( 1970 );	/* get days since 1970 using a known good */
    356 
    357     for ( year = 1970; year < yearend; year++ )
    358     {
    359 	unsigned long t;
    360 	long DaysYear ;
    361 
    362 	clock_time->year = year;
    363 
    364 	/* here is the code we are testing, cut and pasted out of the source */
    365 #if 0		/* old BUGGY code that has Y2K (and many other) failures */
    366 	    /* ghealton: this logic FAILED with great frequency when run
    367 	     * over a period of time, including for year 2000. True, it
    368 	     * had more successes than failures, but that's not really good
    369 	     * enough for critical time distribution software.
    370 	     * It is so awful I wonder if it has had a history of failure
    371 	     * and fixes? */
    372         t =  (clock_time->year - 1970) * 365;
    373         t += (clock_time->year >> 2) - (1970 >> 2);
    374         t -= clock_time->year / 100 - 1970 / 100;
    375         t += clock_time->year / 400 - 1970 / 400;
    376 
    377 		/* (immediate feare of rounding errors on integer
    378 		 * **divisions proved well founded) */
    379 
    380 #else
    381 	/* my replacement, based on Days() above */
    382 	t = julian0(year) - julian0(1970);
    383 #endif
    384 
    385 	/* compare result in t against trusted calculations */
    386 	DaysYear = Days( year );	/* get days to this year */
    387 	if ( t != DaysYear - Days1970 )
    388 	{
    389 	    Error(year);
    390 	    fprintf( stdout,
    391 		"  %4d 1970=%-8ld %4d=%-8ld %-3ld  t=%-8ld  *** ERROR ***\n",
    392 		  year,      (long)Days1970,
    393 				 year,
    394 				     (long)DaysYear,
    395 					   (long)(DaysYear - Days1970),
    396 						   (long)t );
    397 	}
    398     }
    399 
    400 #if 1		/* { */
    401    {
    402     debug = 1;			/* enable debugging */
    403     for ( year = 1970; year < yearend; year++ )
    404     {		/* (limited by theory unix 2038 related bug lives by, but
    405 		 * ends in yearend) */
    406 	clocktime_t  ct;
    407 	time_t	     Observed;
    408 	time_t	     Expected;
    409 	u_long       Flag;
    410 	unsigned long t;
    411 
    412 	ct.day = 1;
    413 	ct.month = 1;
    414 	ct.year = year;
    415 	ct.hour = ct.minute = ct.second = ct.usecond = 0;
    416 	ct.utcoffset = 0;
    417 	ct.utctime = 0;
    418 	ct.flags = 0;
    419 
    420 	Flag = 0;
    421  	Observed = parse_to_unixtime( &ct, &Flag );
    422 	if ( ct.year != year )
    423 	{
    424 	    fprintf( stdout,
    425 	       "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n",
    426 	       (int)year, (int)Flag, (int)ct.year );
    427 	    Error(year);
    428 	    break;
    429 	}
    430 	t = julian0(year) - julian0(1970);	/* Julian day from 1970 */
    431 	Expected = t * 24 * 60 * 60;
    432 	if ( Observed != Expected  ||  Flag )
    433 	{   /* time difference */
    434 	    fprintf( stdout,
    435 	       "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu  (%ld)\n",
    436 	       year, (int)Flag,
    437 	       (unsigned long)Observed, (unsigned long)Expected,
    438 	       ((long)Observed - (long)Expected) );
    439 	    Error(year);
    440 	    break;
    441 	}
    442 
    443 	if ( year >= YEAR_PIVOT+1900 )
    444 	{
    445 	    /* check year % 100 code we put into parse_to_unixtime() */
    446 	    ct.utctime = 0;
    447 	    ct.year = year % 100;
    448 	    Flag = 0;
    449 
    450 	    Observed = parse_to_unixtime( &ct, &Flag );
    451 
    452 	    if ( Observed != Expected  ||  Flag )
    453 	    {   /* time difference */
    454 		fprintf( stdout,
    455 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu  (%ld)\n",
    456 		   year, (int)ct.year, (int)Flag,
    457 		   (unsigned long)Observed, (unsigned long)Expected,
    458 		   ((long)Observed - (long)Expected) );
    459 		Error(year);
    460 		break;
    461 	    }
    462 
    463 	    /* check year - 1900 code we put into parse_to_unixtime() */
    464 	    ct.utctime = 0;
    465 	    ct.year = year - 1900;
    466 	    Flag = 0;
    467 
    468 	    Observed = parse_to_unixtime( &ct, &Flag );
    469 
    470 	    if ( Observed != Expected  ||  Flag )
    471 	    {   /* time difference */
    472 		fprintf( stdout,
    473 "%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu  (%ld)\n",
    474 		   year, (int)ct.year, (int)Flag,
    475 		   (unsigned long)Observed, (unsigned long)Expected,
    476 		   ((long)Observed - (long)Expected) );
    477 		Error(year);
    478 		break;
    479 	    }
    480 
    481 
    482 	}
    483     }
    484 #endif		/* } */
    485    }
    486   }
    487 
    488     puts( " libntp/caljulian.c" );
    489   {		/* test caljulian() */
    490     struct	calendar  ot;
    491     u_long ntp_time;		/* NTP time */
    492 
    493     year = year0;		/* calculate the basic year */
    494     printf( "  starting year %04d\n", (int)year0 );
    495     printf( "  ending year   %04d\n", (int)yearend );
    496 
    497 
    498     ntp_time = julian0( year0 );		/* NTP starts in 1900-01-01 */
    499 #if DAY_NTP_STARTS == 693596
    500     ntp_time -= 365;		/* BIAS required for successful test */
    501 #endif
    502     if ( DAY_NTP_STARTS != ntp_time )
    503     {
    504 	Error(year);
    505 	fprintf( stdout,
    506 		"%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n",
    507 		(int)year0,
    508 		(long)DAY_NTP_STARTS,  (long)ntp_time,
    509 		(long)DAY_NTP_STARTS - (long)ntp_time );
    510     }
    511 
    512     for ( ; year < yearend; year++ )
    513     {
    514 
    515 	/* 01-01 for the current year */
    516 	ntp_time = Days( year ) - Days( year0 );  /* days into NTP time */
    517 	ntp_time *= 24 * 60 * 60;	/* convert into seconds */
    518 	caljulian( ntp_time, &ot );	/* convert January 1 */
    519 	if ( ot.year  != year
    520 	  || ot.month != 1
    521 	  || ot.monthday != 1 )
    522 	{
    523 	    Error(year);
    524 	    fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
    525 			(unsigned long)ntp_time,
    526 			year,
    527 			(int)ot.year, (int)ot.month, (int)ot.monthday );
    528 	    break;
    529 	}
    530 
    531 	ntp_time += (31 + 28-1) * ( 24 * 60 * 60 );	/* advance to 02-28 */
    532 	caljulian( ntp_time, &ot );	/* convert Feb 28 */
    533 	if ( ot.year  != year
    534 	  || ot.month != 2
    535 	  || ot.monthday != 28 )
    536 	{
    537 	    Error(year);
    538 	    fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n",
    539 			(unsigned long)ntp_time,
    540 			year,
    541 			(int)ot.year, (int)ot.month, (int)ot.monthday );
    542 	    break;
    543 	}
    544 
    545       {
    546 	int    m;		/* expected month */
    547 	int    d;		/* expected day */
    548 
    549 	m = isleap_4(year) ?  2 : 3;
    550 	d = isleap_4(year) ? 29 : 1;
    551 
    552 	ntp_time += ( 24 * 60 * 60 );	/* advance to the next day */
    553 	caljulian( ntp_time, &ot );	/* convert this day */
    554 	if ( ot.year  != year
    555 	  || ot.month != m
    556 	  || ot.monthday != d )
    557 	{
    558 	    Error(year);
    559 	    fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n",
    560 			(unsigned long)ntp_time,
    561 			year, m, d,
    562 			(int)ot.year, (int)ot.month, (int)ot.monthday );
    563 	    break;
    564 	}
    565 
    566       }
    567     }
    568   }
    569 
    570     puts( " libntp/caltontp.c" );
    571   {		/* test caltontp() */
    572     struct	calendar  ot;
    573     u_long      ntp_time;		/* NTP time */
    574 
    575     year = year0;		/* calculate the basic year */
    576     printf( "  starting year %04d\n", (int)year0 );
    577     printf( "  ending year   %04d\n", (int)yearend );
    578 
    579 
    580     for ( ; year < yearend; year++ )
    581     {
    582 	u_long  ObservedNtp;
    583 
    584 	/* 01-01 for the current year */
    585 	ot.year = year;
    586 	ot.month = ot.monthday = 1; 	/* unused, but set anyway JIC */
    587 	ot.yearday = 1;		/* this is the magic value used by caltontp() */
    588 	ot.hour = ot.minute = ot.second = 0;
    589 
    590 	ntp_time = Days( year ) - Days( year0 );  /* days into NTP time */
    591 	ntp_time *= 24 * 60 * 60;	/* convert into seconds */
    592 	ObservedNtp = caltontp( &ot );
    593 	if ( ntp_time != ObservedNtp )
    594 	{
    595 	    Error(year);
    596 	    fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n",
    597 			(int)year,
    598 			(unsigned long)ntp_time, (unsigned long)ObservedNtp ,
    599 			(long)ntp_time - (long)ObservedNtp );
    600 
    601 	    break;
    602 	}
    603 
    604 	/* now call caljulian as a type of failsafe supercheck */
    605 	caljulian( ObservedNtp, &ot );	/* convert January 1 */
    606 	if ( ot.year  != year
    607 	  || ot.month != 1
    608 	  || ot.monthday != 1 )
    609 	{
    610 	    Error(year);
    611 	    fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
    612 			(unsigned long)ObservedNtp,
    613 			year,
    614 			(int)ot.year, (int)ot.month, (int)ot.monthday );
    615 	    break;
    616 	}
    617     }
    618   }
    619 
    620    if ( Warnings > 0 )
    621        fprintf( stdout, "%d WARNINGS\n",  Warnings );
    622    if ( Fatals > 0 )
    623        fprintf( stdout, "%d FATAL ERRORS\n",  Fatals );
    624    return Fatals ? 1 : 0;
    625 }
    626 							/* Y2KFixes ] */
    627