check_y2k.c revision 285612
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
107char const *progname = "check_y2k";
108
109long
110Days ( 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
126static int  year0 = 1900;	/* sarting year for NTP time */
127static 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.) */
135static time_t Time;
136static struct tm LocalTime;
137
138#define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \
139	Warnings++; else Fatals++
140
141int
142main( 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