From b413c2a759d9eb965ee90695c04e5a0119f01a81 Mon Sep 17 00:00:00 2001 From: Benoit GARNIER Date: Sun, 27 Mar 2016 11:08:03 +0200 Subject: [PATCH] BUG/MINOR: log: GMT offset not updated when entering/leaving DST GMT offset used in local time formats was computed at startup, but was not updated when DST status changed while running. For example these two RFC5424 syslog traces where emitted 5 seconds apart, just before and after DST changed: <14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 2098 - - Connect ... <14>1 2016-03-27T03:00:03+01:00 bunch-VirtualBox haproxy 2098 - - Connect ... It looked like they were emitted more than 1 hour apart, unlike with the fix: <14>1 2016-03-27T01:59:58+01:00 bunch-VirtualBox haproxy 3381 - - Connect ... <14>1 2016-03-27T03:00:03+02:00 bunch-VirtualBox haproxy 3381 - - Connect ... This patch should be backported to 1.6 and partially to 1.5 (no fix needed in log.c). --- include/common/standard.h | 9 ++++++--- src/haproxy.c | 6 +----- src/log.c | 4 +++- src/standard.c | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/include/common/standard.h b/include/common/standard.h index 9abdb0643..353d0b023 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -860,9 +860,6 @@ char *human_time(int t, short hz_div); extern const char *monthname[]; -/* numeric timezone (that is, the hour and minute offset from UTC) */ -char localtimezone[6]; - /* date2str_log: write a date in the format : * sprintf(str, "%02d/%s/%04d:%02d:%02d:%02d.%03d", * tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, @@ -873,6 +870,12 @@ char localtimezone[6]; */ char *date2str_log(char *dest, struct tm *tm, struct timeval *date, size_t size); +/* Return the GMT offset for a specific local time. + * The string returned has the same format as returned by strftime(... "%z", tm). + * Offsets are kept in an internal cache for better performances. + */ +const char *get_gmt_offset(struct tm *tm); + /* gmt2str_log: write a date in the format : * "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf * return a pointer to the last char written (\0) or diff --git a/src/haproxy.c b/src/haproxy.c index 4d38d2713..9842f3d3c 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -562,7 +562,6 @@ void init(int argc, char **argv) struct wordlist *wl; char *progname; char *change_dir = NULL; - struct tm curtime; struct proxy *px; chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize); @@ -588,15 +587,12 @@ void init(int argc, char **argv) global.rlimit_memmax_all = HAPROXY_MEMMAX; #endif + tzset(); tv_update_date(-1,-1); start_date = now; srandom(now_ms - getpid()); - /* Get the numeric timezone. */ - get_localtime(start_date.tv_sec, &curtime); - strftime(localtimezone, 6, "%z", &curtime); - init_log(); signal_init(); if (init_acl() != 0) diff --git a/src/log.c b/src/log.c index 3e25bc79d..ab383531e 100644 --- a/src/log.c +++ b/src/log.c @@ -970,6 +970,7 @@ static char *update_log_hdr_rfc5424(const time_t time) { static long tvsec; static char *dataptr = NULL; /* backup of last end of header, NULL first time */ + const char *gmt_offset; if (unlikely(time != tvsec || dataptr == NULL)) { /* this string is rebuild only once a second */ @@ -978,12 +979,13 @@ static char *update_log_hdr_rfc5424(const time_t time) tvsec = time; get_localtime(tvsec, &tm); + gmt_offset = get_gmt_offset(&tm); hdr_len = snprintf(logheader_rfc5424, global.max_syslog_len, "<<<<>1 %4d-%02d-%02dT%02d:%02d:%02d%.3s:%.2s %s ", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - localtimezone, localtimezone+3, + gmt_offset, gmt_offset+3, global.log_send_hostname ? global.log_send_hostname : hostname); /* WARNING: depending upon implementations, snprintf may return * either -1 or the number of bytes that would be needed to store diff --git a/src/standard.c b/src/standard.c index 40727b0dd..e08795fd1 100644 --- a/src/standard.c +++ b/src/standard.c @@ -2552,6 +2552,35 @@ char *date2str_log(char *dst, struct tm *tm, struct timeval *date, size_t size) return dst; } +/* Return the GMT offset for a specific local time. + * The string returned has the same format as returned by strftime(... "%z", tm). + * Offsets are kept in an internal cache for better performances. + */ +const char *get_gmt_offset(struct tm *tm) +{ + /* Cache offsets from GMT (depending on whether DST is active or not) */ + static char gmt_offsets[2][5+1] = { "", "" }; + + int old_isdst = tm->tm_isdst; + char *gmt_offset; + + /* Pretend DST not active if its status is unknown, or strftime() will return an empty string for "%z" */ + if (tm->tm_isdst < 0) { + tm->tm_isdst = 0; + } + + /* Fetch the offset and initialize it if needed */ + gmt_offset = gmt_offsets[tm->tm_isdst & 0x01]; + if (unlikely(!*gmt_offset)) { + strftime(gmt_offset, 5+1, "%z", tm); + } + + /* Restore previous DST flag */ + tm->tm_isdst = old_isdst; + + return gmt_offset; +} + /* gmt2str_log: write a date in the format : * "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf * return a pointer to the last char written (\0) or @@ -2592,9 +2621,12 @@ char *gmt2str_log(char *dst, struct tm *tm, size_t size) */ char *localdate2str_log(char *dst, struct tm *tm, size_t size) { + const char *gmt_offset; if (size < 27) /* the size is fixed: 26 chars + \0 */ return NULL; + gmt_offset = get_gmt_offset(tm); + dst = utoa_pad((unsigned int)tm->tm_mday, dst, 3); // day *dst++ = '/'; memcpy(dst, monthname[tm->tm_mon], 3); // month @@ -2608,7 +2640,7 @@ char *localdate2str_log(char *dst, struct tm *tm, size_t size) *dst++ = ':'; dst = utoa_pad((unsigned int)tm->tm_sec, dst, 3); // secondes *dst++ = ' '; - memcpy(dst, localtimezone, 5); // timezone + memcpy(dst, gmt_offset, 5); // Offset from local time to GMT dst += 5; *dst = '\0';