From 4fd8d6b3e77eb00cfd7bb8d3d130b147ba0d60f3 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 4 Aug 2007 19:29:25 +0000 Subject: [PATCH] Fix crash caused by log_timezone patch if we attempt to emit any elog messages 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 | 23 +++++++++++++++++++---- src/backend/utils/misc/guc.c | 8 +++++++- src/include/pgtime.h | 4 +++- src/timezone/localtime.c | 11 ++++++++--- src/timezone/pgtz.c | 31 ++++++++++++++++++++++++++++++- 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index d51dd0bf1a..44d0275208 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -42,7 +42,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.192 2007/08/04 01:26:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.193 2007/08/04 19:29:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index fc35b14cf3..45fef6d7a3 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.409 2007/08/04 01:26:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.410 2007/08/04 19:29:25 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -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. */ diff --git a/src/include/pgtime.h b/src/include/pgtime.h index b1ff5eac44..c8db2702d1 100644 --- a/src/include/pgtime.h +++ b/src/include/pgtime.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.16 2007/08/04 01:26:54 tgl Exp $ + * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.17 2007/08/04 19:29:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index fca5ebac64..774d83a2f6 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -3,7 +3,7 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.16 2006/10/18 16:43:14 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.17 2007/08/04 19:29:25 tgl Exp $ */ /* @@ -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') diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 26ff1ea112..ad599c7093 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.52 2007/08/04 01:26:54 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.53 2007/08/04 19:29:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 *