]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: log: GMT offset not updated when entering/leaving DST
authorBenoit GARNIER <chezbunch+haproxy@gmail.com>
Sun, 27 Mar 2016 09:08:03 +0000 (11:08 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 13 Mar 2016 22:48:05 +0000 (23:48 +0100)
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
src/haproxy.c
src/log.c
src/standard.c

index 9abdb06439405a2187cc53358e68af2c15bb5f1a..353d0b023b16cf00e767023c9d5e89c1af6361c8 100644 (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
index 4d38d2713fdbb238eb3c76568a1b86944fb3635f..9842f3d3c22d67484d6659821e21eb6f79255a96 100644 (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)
index 3e25bc79d612ed347ad9cc2f40c21fe059fbfd82..ab383531e7a386d86d5cd175fe8c038d158b15c8 100644 (file)
--- 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
index 40727b0dd8a07a1f1db273da40def2ca239503b9..e08795fd167339932be1c44d4c930199945756a6 100644 (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';