---Overview--- This patch is intended to fix a minor bug in qmail-1.03 (and also netqmail-1.05) that causes qmail to use incorrect timestamp information on systems using a TAI system clock. Since the TAI timescale does not permit leap seconds, it is offset from the more common UTC timescale by an integer number of seconds. From the period 1999-01-01 to 2005-06-30 this offset was 32 seconds. Converting between TAI and UTC is easy if you have an accurate table of all previous leap seconds, usually placed in "/etc/leapsecs.dat". qmail-1.03 and netqmail-1.05 do not check for "/etc/leapsecs.dat", and thus do not handle leap seconds correctly on systems that use TAI without this patch. ---What This Patch Does--- Converting qmail's internals to use the libtai library everywhere would involve rewriting all of its time-handling code. Instead, I chose to simply scoop out the datetime() function and replace it with a few lines of libtai's most useful features. This provides support for systems that use TAI without seriously changing the way qmail understands time. The file "datetime.c" now contains libtai's ability to convert from seconds since the UNIX epoch to a caltime struct, using that value to check "/etc/leapsecs.dat" for the correct TAI - UTC offset. This value is then given to qmail to digest into its old datetime struct, the precursor to libtai's caltime. --Installation--- Apply this patch to netqmail-1.05 with the following instructions: 1. Untar the netqmail-1.05 package. 2. Run the included "collate.sh" script. 3. Place this patch in the newly created netqmail-1.05 directory. 4. Type "patch < netqmail-1.05-TAI-leapsecs.patch". 5. If you receive no errors, continue with the netqmail build process according to the documention available online at . ---Notes--- Related information you might find useful: - What is TAI? - "TAI is a uniform time scale" - Why twenty-two seconds and not thirty-two? - DJB's acknowledgement of the bug - libtai library homepage - clockspeed software homepage ---Version History--- 2004-11-06, v.1.1 - replaced datetime() logic with caltime_utc() 2004-11-02, v.1.0 - initial release, datetime() still uses "tod" --Toby Betts diff netqmail-1.05/FILES netqmail-1.05.new/FILES --- netqmail-1.05/FILES Sat Nov 6 16:10:36 2004 +++ netqmail-1.05.new/FILES Sat Nov 6 16:09:47 2004 @@ -432,3 +432,16 @@ tcp-environ.5 constmap.h constmap.c +caldate.h +caldate_fmjd.c +caltime.h +caltime_utc.c +leapsecs.h +leapsecs_init.c +leapsecs_read.c +leapsecs_sub.c +tai.h +tai_unpack.c +tryulong64.c +uint64.h1 +uint64.h2 diff netqmail-1.05/Makefile netqmail-1.05.new/Makefile --- netqmail-1.05/Makefile Sat Nov 6 16:10:36 2004 +++ netqmail-1.05.new/Makefile Sat Nov 6 16:04:42 2004 @@ -215,6 +215,14 @@ compile byte_zero.c byte.h ./compile byte_zero.c +caldate_fmjd.o: \ +compile caldate_fmjd.c caldate.h + ./compile caldate_fmjd.c + +caltime_utc.o: \ +compile caltime_utc.c caltime.h + ./compile caltime_utc.c + case.a: \ makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \ case_starts.o @@ -375,11 +383,14 @@ chmod 755 datemail datetime.a: \ -makelib datetime.o datetime_un.o - ./makelib datetime.a datetime.o datetime_un.o +makelib datetime.o datetime_un.o caltime_utc.o caldate_fmjd.o leapsecs_sub.o \ +leapsecs_init.o leapsecs_read.o tai_unpack.o + ./makelib datetime.a datetime.o datetime_un.o caltime_utc.o \ + caldate_fmjd.o leapsecs_sub.o leapsecs_init.o leapsecs_read.o \ + tai_unpack.o datetime.o: \ -compile datetime.c datetime.h +compile datetime.c datetime.h caltime.h tai.h uint64.h ./compile datetime.c datetime_un.o: \ @@ -810,6 +821,18 @@ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ binm3 binm3+df +leapsecs_init.o: \ +compile leapsecs_init.c leapsecs.h + ./compile leapsecs_init.c + +leapsecs_read.o: \ +compile leapsecs_read.c leapsecs.h + ./compile leapsecs_read.c + +leapsecs_sub.o: \ +compile leapsecs_sub.c leapsecs.h + ./compile leapsecs_sub.c + load: \ make-load warn-auto.sh systype ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load @@ -2064,6 +2087,10 @@ find-systype trycpp.c ./find-systype > systype +tai_unpack.o: \ +compile tai_unpack.c tai.h uint64.h + ./compile tai_unpack.c + tcp-env: \ load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ @@ -2127,6 +2154,13 @@ ./tryulong32 ) >/dev/null 2>&1 \ && cat uint32.h2 || cat uint32.h1 ) > uint32.h rm -f tryulong32.o tryulong32 + +uint64.h: \ +tryulong64.c compile load uint64.h1 uint64.h2 + ( ( ./compile tryulong64.c && ./load tryulong64 && \ + ./tryulong64 ) >/dev/null 2>&1 \ + && cat uint64.h2 || cat uint64.h1 ) > uint64.h + rm -f tryulong64.o tryulong64 wait.a: \ makelib wait_pid.o wait_nohang.o diff netqmail-1.05/TARGETS netqmail-1.05.new/TARGETS --- netqmail-1.05/TARGETS Mon Jun 15 06:52:55 1998 +++ netqmail-1.05.new/TARGETS Sat Nov 6 16:06:10 2004 @@ -385,3 +385,9 @@ man setup check +caldate_fmjd.o +caltime_utc.o +leapsecs_init.o +leapsecs_read.o +leapsecs_sub.o +tai_unpack.o diff netqmail-1.05/caldate.h netqmail-1.05.new/caldate.h --- netqmail-1.05/caldate.h Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/caldate.h Sat Nov 6 13:31:20 2004 @@ -0,0 +1,19 @@ +#ifndef CALDATE_H +#define CALDATE_H + +struct caldate { + long year; + int month; + int day; +} ; + +extern unsigned int caldate_fmt(); +extern unsigned int caldate_scan(); + +extern void caldate_frommjd(); +extern long caldate_mjd(); +extern void caldate_normalize(); + +extern void caldate_easter(); + +#endif diff netqmail-1.05/caldate_fmjd.c netqmail-1.05.new/caldate_fmjd.c --- netqmail-1.05/caldate_fmjd.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/caldate_fmjd.c Sat Nov 6 13:31:20 2004 @@ -0,0 +1,48 @@ +#include "caldate.h" + +void caldate_frommjd(cd,day,pwday,pyday) +struct caldate *cd; +long day; +int *pwday; +int *pyday; +{ + long year; + long month; + int yday; + + year = day / 146097L; + day %= 146097L; + day += 678881L; + while (day >= 146097L) { day -= 146097L; ++year; } + + /* year * 146097 + day - 678881 is MJD; 0 <= day < 146097 */ + /* 2000-03-01, MJD 51604, is year 5, day 0 */ + + if (pwday) *pwday = (day + 3) % 7; + + year *= 4; + if (day == 146096L) { year += 3; day = 36524L; } + else { year += day / 36524L; day %= 36524L; } + year *= 25; + year += day / 1461; + day %= 1461; + year *= 4; + + yday = (day < 306); + if (day == 1460) { year += 3; day = 365; } + else { year += day / 365; day %= 365; } + yday += day; + + day *= 10; + month = (day + 5) / 306; + day = (day + 5) % 306; + day /= 10; + if (month >= 10) { yday -= 306; ++year; month -= 10; } + else { yday += 59; month += 2; } + + cd->year = year; + cd->month = month + 1; + cd->day = day + 1; + + if (pyday) *pyday = yday; +} diff netqmail-1.05/caltime.h netqmail-1.05.new/caltime.h --- netqmail-1.05/caltime.h Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/caltime.h Sat Nov 6 13:31:20 2004 @@ -0,0 +1,20 @@ +#ifndef CALTIME_H +#define CALTIME_H + +#include "caldate.h" + +struct caltime { + struct caldate date; + int hour; + int minute; + int second; + long offset; +} ; + +extern void caltime_tai(); +extern void caltime_utc(); + +extern unsigned int caltime_fmt(); +extern unsigned int caltime_scan(); + +#endif diff netqmail-1.05/caltime_utc.c netqmail-1.05.new/caltime_utc.c --- netqmail-1.05/caltime_utc.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/caltime_utc.c Sat Nov 6 13:31:21 2004 @@ -0,0 +1,35 @@ +#include "tai.h" +#include "leapsecs.h" +#include "caldate.h" +#include "caltime.h" + +/* XXX: breaks tai encapsulation */ + +void caltime_utc(ct,t,pwday,pyday) +struct caltime *ct; +struct tai *t; +int *pwday; +int *pyday; +{ + struct tai t2 = *t; + uint64 u; + int leap; + long s; + + /* XXX: check for overfow? */ + + leap = leapsecs_sub(&t2); + u = t2.x; + + u += 58486; + s = u % 86400ULL; + + ct->second = (s % 60) + leap; s /= 60; + ct->minute = s % 60; s /= 60; + ct->hour = s; + + u /= 86400ULL; + caldate_frommjd(&ct->date,/*XXX*/(long) (u - 53375995543064ULL),pwday,pyday); + + ct->offset = 0; +} diff netqmail-1.05/datetime.c netqmail-1.05.new/datetime.c --- netqmail-1.05/datetime.c Mon Jun 15 06:52:55 1998 +++ netqmail-1.05.new/datetime.c Sat Nov 6 15:57:43 2004 @@ -1,55 +1,27 @@ /* 19950925 */ +#include "caltime.h" #include "datetime.h" +#include "tai.h" void datetime_tai(dt,t) struct datetime *dt; datetime_sec t; { - int day; - int tod; - int year; int yday; int wday; - int mon; + + struct tai t2; + struct caltime ct; + + tai_unix(&t2,t); + caltime_utc(&ct,&t2,&wday,&yday); - tod = t % 86400; - day = t / 86400; - if (tod < 0) { tod += 86400; --day; } - - dt->hour = tod / 3600; - tod %= 3600; - dt->min = tod / 60; - dt->sec = tod % 60; - - wday = (day + 4) % 7; if (wday < 0) wday += 7; + dt->hour = ct.hour; + dt->min = ct.minute; + dt->sec = ct.second; dt->wday = wday; - - day -= 11017; - /* day 0 is march 1, 2000 */ - year = 5 + day / 146097; - day = day % 146097; if (day < 0) { day += 146097; --year; } - /* from now on, day is nonnegative */ - year *= 4; - if (day == 146096) { year += 3; day = 36524; } - else { year += day / 36524; day %= 36524; } - year *= 25; - year += day / 1461; - day %= 1461; - year *= 4; - yday = (day < 306); - if (day == 1460) { year += 3; day = 365; } - else { year += day / 365; day %= 365; } - yday += day; - - day *= 10; - mon = (day + 5) / 306; - day = day + 5 - 306 * mon; - day /= 10; - if (mon >= 10) { yday -= 306; ++year; mon -= 10; } - else { yday += 59; mon += 2; } - dt->yday = yday; - dt->year = year - 1900; - dt->mon = mon; - dt->mday = day + 1; + dt->year = ct.date.year - 1900; + dt->mon = ct.date.month - 1; + dt->mday = ct.date.day; } diff netqmail-1.05/leapsecs.h netqmail-1.05.new/leapsecs.h --- netqmail-1.05/leapsecs.h Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/leapsecs.h Sat Nov 6 13:31:25 2004 @@ -0,0 +1,10 @@ +#ifndef LEAPSECS_H +#define LEAPSECS_H + +extern int leapsecs_init(); +extern int leapsecs_read(); + +extern void leapsecs_add(); +extern int leapsecs_sub(); + +#endif diff netqmail-1.05/leapsecs_init.c netqmail-1.05.new/leapsecs_init.c --- netqmail-1.05/leapsecs_init.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/leapsecs_init.c Sat Nov 6 13:31:25 2004 @@ -0,0 +1,11 @@ +#include "leapsecs.h" + +static int flaginit = 0; + +int leapsecs_init() +{ + if (flaginit) return 0; + if (leapsecs_read() == -1) return -1; + flaginit = 1; + return 0; +} diff netqmail-1.05/leapsecs_read.c netqmail-1.05.new/leapsecs_read.c --- netqmail-1.05/leapsecs_read.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/leapsecs_read.c Sat Nov 6 13:31:25 2004 @@ -0,0 +1,50 @@ +#include +#include +#include +#include +extern int errno; +#include "tai.h" +#include "leapsecs.h" + +struct tai *leapsecs = 0; +int leapsecs_num = 0; + +int leapsecs_read() +{ + int fd; + struct stat st; + struct tai *t; + int n; + int i; + struct tai u; + + fd = open("/etc/leapsecs.dat",O_RDONLY | O_NDELAY); + if (fd == -1) { + if (errno != ENOENT) return -1; + if (leapsecs) free(leapsecs); + leapsecs = 0; + leapsecs_num = 0; + return 0; + } + + if (fstat(fd,&st) == -1) { close(fd); return -1; } + + t = (struct tai *) malloc(st.st_size); + if (!t) { close(fd); return -1; } + + n = read(fd,(char *) t,st.st_size); + close(fd); + if (n != st.st_size) { free(t); return -1; } + + n /= sizeof(struct tai); + + for (i = 0;i < n;++i) { + tai_unpack((char *) &t[i],&u); + t[i] = u; + } + + if (leapsecs) free(leapsecs); + + leapsecs = t; + leapsecs_num = n; +} diff netqmail-1.05/leapsecs_sub.c netqmail-1.05.new/leapsecs_sub.c --- netqmail-1.05/leapsecs_sub.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/leapsecs_sub.c Sat Nov 6 13:31:25 2004 @@ -0,0 +1,29 @@ +#include "leapsecs.h" +#include "tai.h" + +/* XXX: breaks tai encapsulation */ + +extern struct tai *leapsecs; +extern int leapsecs_num; + +int leapsecs_sub(t) +struct tai *t; +{ + int i; + uint64 u; + int s; + + if (leapsecs_init() == -1) return 0; + + u = t->x; + s = 0; + + for (i = 0;i < leapsecs_num;++i) { + if (u < leapsecs[i].x) break; + ++s; + if (u == leapsecs[i].x) { t->x = u - s; return 1; } + } + + t->x = u - s; + return 0; +} diff netqmail-1.05/tai.h netqmail-1.05.new/tai.h --- netqmail-1.05/tai.h Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/tai.h Sat Aug 24 18:32:17 2002 @@ -0,0 +1,28 @@ +/* Public domain. */ + +#ifndef TAI_H +#define TAI_H + +#include "uint64.h" + +struct tai { + uint64 x; +} ; + +#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u))) + +extern void tai_now(struct tai *); + +#define tai_approx(t) ((double) ((t)->x)) + +extern void tai_add(struct tai *,const struct tai *,const struct tai *); +extern void tai_sub(struct tai *,const struct tai *,const struct tai *); +#define tai_less(t,u) ((t)->x < (u)->x) + +#define TAI_PACK 8 +extern void tai_pack(char *,const struct tai *); +extern void tai_unpack(const char *,struct tai *); + +extern void tai_uint(struct tai *,unsigned int); + +#endif diff netqmail-1.05/tai_unpack.c netqmail-1.05.new/tai_unpack.c --- netqmail-1.05/tai_unpack.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/tai_unpack.c Sat Aug 24 18:32:17 2002 @@ -0,0 +1,18 @@ +/* Public domain. */ + +#include "tai.h" + +void tai_unpack(const char *s,struct tai *t) +{ + uint64 x; + + x = (unsigned char) s[0]; + x <<= 8; x += (unsigned char) s[1]; + x <<= 8; x += (unsigned char) s[2]; + x <<= 8; x += (unsigned char) s[3]; + x <<= 8; x += (unsigned char) s[4]; + x <<= 8; x += (unsigned char) s[5]; + x <<= 8; x += (unsigned char) s[6]; + x <<= 8; x += (unsigned char) s[7]; + t->x = x; +} diff netqmail-1.05/tryulong64.c netqmail-1.05.new/tryulong64.c --- netqmail-1.05/tryulong64.c Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/tryulong64.c Sat Aug 24 18:32:17 2002 @@ -0,0 +1,13 @@ +/* Public domain. */ + +int main() +{ + unsigned long u; + u = 1; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + if (!u) _exit(1); + _exit(0); +} diff netqmail-1.05/uint64.h netqmail-1.05.new/uint64.h --- netqmail-1.05/uint64.h Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/uint64.h Sat Nov 6 16:10:00 2004 @@ -0,0 +1,15 @@ +/* Public domain. */ + +#ifndef UINT64_H +#define UINT64_H + +/* sysdep: -ulong64 */ + +typedef unsigned long long uint64; + +extern void uint64_pack(char *,uint64); +extern void uint64_pack_big(char *,uint64); +extern void uint64_unpack(const char *,uint64 *); +extern void uint64_unpack_big(const char *,uint64 *); + +#endif diff netqmail-1.05/uint64.h1 netqmail-1.05.new/uint64.h1 --- netqmail-1.05/uint64.h1 Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/uint64.h1 Sat Aug 24 18:32:17 2002 @@ -0,0 +1,15 @@ +/* Public domain. */ + +#ifndef UINT64_H +#define UINT64_H + +/* sysdep: -ulong64 */ + +typedef unsigned long long uint64; + +extern void uint64_pack(char *,uint64); +extern void uint64_pack_big(char *,uint64); +extern void uint64_unpack(const char *,uint64 *); +extern void uint64_unpack_big(const char *,uint64 *); + +#endif diff netqmail-1.05/uint64.h2 netqmail-1.05.new/uint64.h2 --- netqmail-1.05/uint64.h2 Wed Dec 31 19:00:00 1969 +++ netqmail-1.05.new/uint64.h2 Sat Aug 24 18:32:17 2002 @@ -0,0 +1,15 @@ +/* Public domain. */ + +#ifndef UINT64_H +#define UINT64_H + +/* sysdep: +ulong64 */ + +typedef unsigned long uint64; + +extern void uint64_pack(char *,uint64); +extern void uint64_pack_big(char *,uint64); +extern void uint64_unpack(const char *,uint64 *); +extern void uint64_unpack_big(const char *,uint64 *); + +#endif