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.
This commit is contained in:
Tom Lane 2007-08-04 19:29:25 +00:00
parent 0b9d3d4dcd
commit 4fd8d6b3e7
5 changed files with 67 additions and 10 deletions

View File

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

View File

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* 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.
*/

View File

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

View File

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

View File

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