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).
This commit is contained in:
Benoit GARNIER 2016-03-27 11:08:03 +02:00 committed by Willy Tarreau
parent 5c557d14d5
commit b413c2a759
4 changed files with 43 additions and 10 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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';