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
Days(int Year)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
main(void)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