Fix crash caused by log_timezone patch if we attempt to emit any elog messages
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 19:29:25 +0000 (19:29 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 19:29:25 +0000 (19:29 +0000)
between the setting of log_line_prefix and the setting of log_timezone.  We
can't realistically set log_timezone any earlier than we do now, so the best
behavior seems to be to use GMT zone if any timestamps are to be logged during
early startup.  Create a dummy zone variable with a minimal definition of GMT
(in particular it will never know about leap seconds), so that we can set it
up without reference to any external files.

src/backend/utils/error/elog.c
src/backend/utils/misc/guc.c
src/include/pgtime.h
src/timezone/localtime.c
src/timezone/pgtz.c

index e4832f373ef475e5a4d7c9730966761a747b5206..3e9b19b3a5500dbafbba7f9b57048a8221e37975 100644 (file)
@@ -1497,16 +1497,25 @@ log_line_prefix(StringInfo buf)
                                {
                                        struct timeval tv;
                                        pg_time_t       stamp_time;
+                                       pg_tz      *tz;
                                        char            strfbuf[128],
                                                                msbuf[8];
 
                                        gettimeofday(&tv, NULL);
                                        stamp_time = (pg_time_t) tv.tv_sec;
 
+                                       /*
+                                        * Normally we print log timestamps in log_timezone, but
+                                        * during startup we could get here before that's set.
+                                        * If so, fall back to gmt_timezone (which guc.c ensures
+                                        * is set up before Log_line_prefix can become nonempty).
+                                        */
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                /* leave room for milliseconds... */
                                                                "%Y-%m-%d %H:%M:%S     %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
 
                                        /* 'paste' milliseconds into place... */
                                        sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
@@ -1518,22 +1527,28 @@ log_line_prefix(StringInfo buf)
                        case 't':
                                {
                                        pg_time_t       stamp_time = (pg_time_t) time(NULL);
+                                       pg_tz      *tz;
                                        char            strfbuf[128];
 
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                "%Y-%m-%d %H:%M:%S %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
                                        appendStringInfoString(buf, strfbuf);
                                }
                                break;
                        case 's':
                                {
                                        pg_time_t       stamp_time = (pg_time_t) MyStartTime;
+                                       pg_tz      *tz;
                                        char            strfbuf[128];
 
+                                       tz = log_timezone ? log_timezone : gmt_timezone;
+
                                        pg_strftime(strfbuf, sizeof(strfbuf),
                                                                "%Y-%m-%d %H:%M:%S %Z",
-                                                               pg_localtime(&stamp_time, log_timezone));
+                                                               pg_localtime(&stamp_time, tz));
                                        appendStringInfoString(buf, strfbuf);
                                }
                                break;
index ec336871824957ce06f3a75cb03df4237d623ad4..368b96d9c184bfd3364abd3e0f31de5706e21b9a 100644 (file)
@@ -2926,6 +2926,12 @@ InitializeGUCOptions(void)
        char       *env;
        long            stack_rlimit;
 
+       /*
+        * Before log_line_prefix could possibly receive a nonempty setting,
+        * make sure that timezone processing is minimally alive (see elog.c).
+        */
+       pg_timezone_pre_initialize();
+
        /*
         * Build sorted array of all GUC variables.
         */
index 8f126831264f534232916610cb3f81e7d6d197fe..7cfb65cd6638b5e4ae59b20f9f00cbd05b1c1f3c 100644 (file)
@@ -52,6 +52,7 @@ extern int pg_next_dst_boundary(const pg_time_t *timep,
 extern size_t pg_strftime(char *s, size_t max, const char *format,
                        const struct pg_tm * tm);
 
+extern void pg_timezone_pre_initialize(void);
 extern void pg_timezone_initialize(void);
 extern pg_tz *pg_tzset(const char *tzname);
 extern bool tz_acceptable(pg_tz *tz);
@@ -64,6 +65,7 @@ extern void pg_tzenumerate_end(pg_tzenum *dir);
 
 extern pg_tz *session_timezone;
 extern pg_tz *log_timezone;
+extern pg_tz *gmt_timezone;
 
 /* Maximum length of a timezone name (not including trailing null) */
 #define TZ_STRLEN_MAX 255
index 954c50d847f363b7c7c0dda285c51071a7cf3bb0..862d2980bd8ea76ff32ef09969e0217e806b748d 100644 (file)
@@ -88,7 +88,6 @@ static void timesub(const pg_time_t *timep, long offset,
                const struct state * sp, struct pg_tm * tmp);
 static pg_time_t transtime(pg_time_t janfirst, int year,
                  const struct rule * rulep, long offset);
-int                    tzparse(const char *name, struct state * sp, int lastditch);
 
 /* GMT timezone */
 static struct state gmtmem;
@@ -549,6 +548,12 @@ tzparse(const char *name, struct state * sp, int lastditch)
                if (stdlen >= sizeof sp->chars)
                        stdlen = (sizeof sp->chars) - 1;
                stdoffset = 0;
+               /*
+                * Unlike the original zic library, do NOT invoke tzload() here;
+                * we can't assume pg_open_tzfile() is sane yet, and we don't
+                * care about leap seconds anyway.
+                */
+               load_result = -1;
        }
        else
        {
@@ -561,8 +566,8 @@ tzparse(const char *name, struct state * sp, int lastditch)
                name = getoffset(name, &stdoffset);
                if (name == NULL)
                        return -1;
+               load_result = tzload(TZDEFRULES, NULL, sp);
        }
-       load_result = tzload(TZDEFRULES, NULL, sp);
        if (load_result != 0)
                sp->leapcnt = 0;                /* so, we're off a little */
        if (*name != '\0')
index 211f999a56ebd11d5bb5306e2c8e524df15020a8..f7c42494d2c04c5d4ca0443f5a81636bd3669bdb 100644 (file)
@@ -33,6 +33,10 @@ pg_tz           *session_timezone = NULL;
 /* Current log timezone (controlled by log_timezone GUC) */
 pg_tz     *log_timezone = NULL;
 
+/* Fallback GMT timezone for last-ditch error message formatting */
+pg_tz     *gmt_timezone = NULL;
+static pg_tz gmt_timezone_data;
+
 
 static char tzdir[MAXPGPATH];
 static bool done_tzdir = false;
@@ -1251,6 +1255,31 @@ select_default_timezone(void)
        return NULL;                            /* keep compiler quiet */
 }
 
+
+/*
+ * Pre-initialize timezone library
+ *
+ * This is called before GUC variable initialization begins.  Its purpose
+ * is to ensure that elog.c has a pgtz variable available to format timestamps
+ * with, in case log_line_prefix is set to a value requiring that.  We cannot
+ * set log_timezone yet.
+ */
+void
+pg_timezone_pre_initialize(void)
+{
+       /*
+        * We can't use tzload() because we may not know where PGSHAREDIR
+        * is (in particular this is true in an EXEC_BACKEND subprocess).
+        * Since this timezone variable will only be used for emergency
+        * fallback purposes, it seems OK to just use the "lastditch" case
+        * provided by tzparse().
+        */
+       if (tzparse("GMT", &gmt_timezone_data.state, TRUE) != 0)
+               elog(FATAL, "could not initialize GMT timezone");
+       strcpy(gmt_timezone_data.TZname, "GMT");
+       gmt_timezone = &gmt_timezone_data;
+}
+
 /*
  * Initialize timezone library
  *