PostgreSQL Source Code  git master
pgtz.c File Reference
#include "postgres.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include "datatype/timestamp.h"
#include "miscadmin.h"
#include "pgtz.h"
#include "storage/fd.h"
#include "utils/hsearch.h"
Include dependency graph for pgtz.c:

Go to the source code of this file.

Data Structures

struct  pg_tz_cache
 
struct  pg_tzenum
 

Macros

#define MAX_TZDIR_DEPTH   10
 

Functions

static bool scan_directory_ci (const char *dirname, const char *fname, int fnamelen, char *canonname, int canonnamelen)
 
static const char * pg_TZDIR (void)
 
int pg_open_tzfile (const char *name, char *canonname)
 
static bool init_timezone_hashtable (void)
 
pg_tzpg_tzset (const char *name)
 
pg_tzpg_tzset_offset (long gmtoffset)
 
void pg_timezone_initialize (void)
 
pg_tzenumpg_tzenumerate_start (void)
 
void pg_tzenumerate_end (pg_tzenum *dir)
 
pg_tzpg_tzenumerate_next (pg_tzenum *dir)
 

Variables

pg_tzsession_timezone = NULL
 
pg_tzlog_timezone = NULL
 
static HTABtimezone_cache = NULL
 

Macro Definition Documentation

◆ MAX_TZDIR_DEPTH

#define MAX_TZDIR_DEPTH   10

Definition at line 385 of file pgtz.c.

Referenced by pg_tzenumerate_next().

Function Documentation

◆ init_timezone_hashtable()

static bool init_timezone_hashtable ( void  )
static

Definition at line 202 of file pgtz.c.

References HASHCTL::entrysize, hash_create(), HASH_ELEM, HASHCTL::keysize, MemSet, and TZ_STRLEN_MAX.

Referenced by pg_tzset().

203 {
204  HASHCTL hash_ctl;
205 
206  MemSet(&hash_ctl, 0, sizeof(hash_ctl));
207 
208  hash_ctl.keysize = TZ_STRLEN_MAX + 1;
209  hash_ctl.entrysize = sizeof(pg_tz_cache);
210 
211  timezone_cache = hash_create("Timezones",
212  4,
213  &hash_ctl,
214  HASH_ELEM);
215  if (!timezone_cache)
216  return false;
217 
218  return true;
219 }
#define HASH_ELEM
Definition: hsearch.h:87
Size entrysize
Definition: hsearch.h:73
#define MemSet(start, val, len)
Definition: c.h:908
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
Size keysize
Definition: hsearch.h:72
static HTAB * timezone_cache
Definition: pgtz.c:198

◆ pg_open_tzfile()

int pg_open_tzfile ( const char *  name,
char *  canonname 
)

Definition at line 76 of file pgtz.c.

References MAXPGPATH, name, PG_BINARY, pg_TZDIR(), scan_directory_ci(), strlcpy(), and TZ_STRLEN_MAX.

77 {
78  const char *fname;
79  char fullname[MAXPGPATH];
80  int fullnamelen;
81  int orignamelen;
82 
83  /* Initialize fullname with base name of tzdata directory */
84  strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
85  orignamelen = fullnamelen = strlen(fullname);
86 
87  if (fullnamelen + 1 + strlen(name) >= MAXPGPATH)
88  return -1; /* not gonna fit */
89 
90  /*
91  * If the caller doesn't need the canonical spelling, first just try to
92  * open the name as-is. This can be expected to succeed if the given name
93  * is already case-correct, or if the filesystem is case-insensitive; and
94  * we don't need to distinguish those situations if we aren't tasked with
95  * reporting the canonical spelling.
96  */
97  if (canonname == NULL)
98  {
99  int result;
100 
101  fullname[fullnamelen] = '/';
102  /* test above ensured this will fit: */
103  strcpy(fullname + fullnamelen + 1, name);
104  result = open(fullname, O_RDONLY | PG_BINARY, 0);
105  if (result >= 0)
106  return result;
107  /* If that didn't work, fall through to do it the hard way */
108  fullname[fullnamelen] = '\0';
109  }
110 
111  /*
112  * Loop to split the given name into directory levels; for each level,
113  * search using scan_directory_ci().
114  */
115  fname = name;
116  for (;;)
117  {
118  const char *slashptr;
119  int fnamelen;
120 
121  slashptr = strchr(fname, '/');
122  if (slashptr)
123  fnamelen = slashptr - fname;
124  else
125  fnamelen = strlen(fname);
126  if (!scan_directory_ci(fullname, fname, fnamelen,
127  fullname + fullnamelen + 1,
128  MAXPGPATH - fullnamelen - 1))
129  return -1;
130  fullname[fullnamelen++] = '/';
131  fullnamelen += strlen(fullname + fullnamelen);
132  if (slashptr)
133  fname = slashptr + 1;
134  else
135  break;
136  }
137 
138  if (canonname)
139  strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
140 
141  return open(fullname, O_RDONLY | PG_BINARY, 0);
142 }
static const char * pg_TZDIR(void)
Definition: pgtz.c:43
#define PG_BINARY
Definition: c.h:1080
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
#define MAXPGPATH
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static bool scan_directory_ci(const char *dirname, const char *fname, int fnamelen, char *canonname, int canonnamelen)
Definition: pgtz.c:151
const char * name
Definition: encode.c:521

◆ pg_timezone_initialize()

void pg_timezone_initialize ( void  )

Definition at line 363 of file pgtz.c.

References pg_tzset(), and session_timezone.

Referenced by InitializeGUCOptions().

364 {
365  /*
366  * We may not yet know where PGSHAREDIR is (in particular this is true in
367  * an EXEC_BACKEND subprocess). So use "GMT", which pg_tzset forces to be
368  * interpreted without reference to the filesystem. This corresponds to
369  * the bootstrap default for these variables in guc.c, although in
370  * principle it could be different.
371  */
372  session_timezone = pg_tzset("GMT");
374 }
pg_tz * log_timezone
Definition: pgtz.c:31
pg_tz * pg_tzset(const char *name)
Definition: pgtz.c:236
pg_tz * session_timezone
Definition: pgtz.c:28

◆ pg_TZDIR()

static const char* pg_TZDIR ( void  )
static

Definition at line 43 of file pgtz.c.

References get_share_path(), MAXPGPATH, my_exec_path, and strlcpy().

Referenced by pg_open_tzfile(), and pg_tzenumerate_start().

44 {
45 #ifndef SYSTEMTZDIR
46  /* normal case: timezone stuff is under our share dir */
47  static bool done_tzdir = false;
48  static char tzdir[MAXPGPATH];
49 
50  if (done_tzdir)
51  return tzdir;
52 
54  strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
55 
56  done_tzdir = true;
57  return tzdir;
58 #else
59  /* we're configured to use system's timezone database */
60  return SYSTEMTZDIR;
61 #endif
62 }
#define MAXPGPATH
char my_exec_path[MAXPGPATH]
Definition: globals.c:71
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:704

◆ pg_tzenumerate_end()

void pg_tzenumerate_end ( pg_tzenum dir)

Definition at line 416 of file pgtz.c.

References pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, FreeDir(), and pfree().

Referenced by pg_timezone_names().

417 {
418  while (dir->depth >= 0)
419  {
420  FreeDir(dir->dirdesc[dir->depth]);
421  pfree(dir->dirname[dir->depth]);
422  dir->depth--;
423  }
424  pfree(dir);
425 }
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:391
void pfree(void *pointer)
Definition: mcxt.c:1031
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:392
int depth
Definition: pgtz.c:390
int FreeDir(DIR *dir)
Definition: fd.c:2708

◆ pg_tzenumerate_next()

pg_tz* pg_tzenumerate_next ( pg_tzenum dir)

Definition at line 428 of file pgtz.c.

References AllocateDir(), pg_tzenum::baselen, pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, ereport, errcode_for_file_access(), errmsg(), errmsg_internal(), ERROR, FreeDir(), MAX_TZDIR_DEPTH, MAXPGPATH, pfree(), pg_tz_acceptable(), pstrdup(), ReadDir(), S_ISDIR, snprintf(), stat, pg_tz::state, strlcpy(), pg_tzenum::tz, tzload(), and pg_tz::TZname.

Referenced by pg_timezone_names().

429 {
430  while (dir->depth >= 0)
431  {
432  struct dirent *direntry;
433  char fullname[MAXPGPATH * 2];
434  struct stat statbuf;
435 
436  direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
437 
438  if (!direntry)
439  {
440  /* End of this directory */
441  FreeDir(dir->dirdesc[dir->depth]);
442  pfree(dir->dirname[dir->depth]);
443  dir->depth--;
444  continue;
445  }
446 
447  if (direntry->d_name[0] == '.')
448  continue;
449 
450  snprintf(fullname, sizeof(fullname), "%s/%s",
451  dir->dirname[dir->depth], direntry->d_name);
452  if (stat(fullname, &statbuf) != 0)
453  ereport(ERROR,
455  errmsg("could not stat \"%s\": %m", fullname)));
456 
457  if (S_ISDIR(statbuf.st_mode))
458  {
459  /* Step into the subdirectory */
460  if (dir->depth >= MAX_TZDIR_DEPTH - 1)
461  ereport(ERROR,
462  (errmsg_internal("timezone directory stack overflow")));
463  dir->depth++;
464  dir->dirname[dir->depth] = pstrdup(fullname);
465  dir->dirdesc[dir->depth] = AllocateDir(fullname);
466  if (!dir->dirdesc[dir->depth])
467  ereport(ERROR,
469  errmsg("could not open directory \"%s\": %m",
470  fullname)));
471 
472  /* Start over reading in the new directory */
473  continue;
474  }
475 
476  /*
477  * Load this timezone using tzload() not pg_tzset(), so we don't fill
478  * the cache. Also, don't ask for the canonical spelling: we already
479  * know it, and pg_open_tzfile's way of finding it out is pretty
480  * inefficient.
481  */
482  if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0)
483  {
484  /* Zone could not be loaded, ignore it */
485  continue;
486  }
487 
488  if (!pg_tz_acceptable(&dir->tz))
489  {
490  /* Ignore leap-second zones */
491  continue;
492  }
493 
494  /* OK, return the canonical zone name spelling. */
495  strlcpy(dir->tz.TZname, fullname + dir->baselen,
496  sizeof(dir->tz.TZname));
497 
498  /* Timezone loaded OK. */
499  return &dir->tz;
500  }
501 
502  /* Nothing more found */
503  return NULL;
504 }
struct pg_tz tz
Definition: pgtz.c:393
int tzload(const char *name, char *canonname, struct state *sp, bool doextend)
Definition: localtime.c:555
char * pstrdup(const char *in)
Definition: mcxt.c:1161
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
Definition: dirent.h:9
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:391
void pfree(void *pointer)
Definition: mcxt.c:1031
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:392
#define ERROR
Definition: elog.h:43
#define MAXPGPATH
#define MAX_TZDIR_DEPTH
Definition: pgtz.c:385
int errcode_for_file_access(void)
Definition: elog.c:598
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2590
#define ereport(elevel, rest)
Definition: elog.h:122
#define stat(a, b)
Definition: win32_port.h:266
bool pg_tz_acceptable(pg_tz *tz)
Definition: localtime.c:1839
struct state state
Definition: pgtz.h:63
int baselen
Definition: pgtz.c:389
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2656
char TZname[TZ_STRLEN_MAX+1]
Definition: pgtz.h:62
#define S_ISDIR(m)
Definition: win32_port.h:307
int errmsg(const char *fmt,...)
Definition: elog.c:797
int depth
Definition: pgtz.c:390
int FreeDir(DIR *dir)
Definition: fd.c:2708

◆ pg_tzenumerate_start()

pg_tzenum* pg_tzenumerate_start ( void  )

Definition at line 399 of file pgtz.c.

References AllocateDir(), pg_tzenum::baselen, pg_tzenum::depth, pg_tzenum::dirdesc, pg_tzenum::dirname, ereport, errcode_for_file_access(), errmsg(), ERROR, palloc0(), pg_TZDIR(), and pstrdup().

Referenced by pg_timezone_names().

400 {
401  pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
402  char *startdir = pstrdup(pg_TZDIR());
403 
404  ret->baselen = strlen(startdir) + 1;
405  ret->depth = 0;
406  ret->dirname[0] = startdir;
407  ret->dirdesc[0] = AllocateDir(startdir);
408  if (!ret->dirdesc[0])
409  ereport(ERROR,
411  errmsg("could not open directory \"%s\": %m", startdir)));
412  return ret;
413 }
char * pstrdup(const char *in)
Definition: mcxt.c:1161
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:391
static const char * pg_TZDIR(void)
Definition: pgtz.c:43
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:392
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:598
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2590
#define ereport(elevel, rest)
Definition: elog.h:122
void * palloc0(Size size)
Definition: mcxt.c:955
int baselen
Definition: pgtz.c:389
int errmsg(const char *fmt,...)
Definition: elog.c:797
int depth
Definition: pgtz.c:390

◆ pg_tzset()

pg_tz* pg_tzset ( const char *  name)

Definition at line 236 of file pgtz.c.

References elog, ERROR, HASH_ENTER, HASH_FIND, hash_search(), init_timezone_hashtable(), pg_toupper(), pg_tz::state, pg_tz_cache::tz, TZ_STRLEN_MAX, tzload(), pg_tz::TZname, and tzparse().

Referenced by check_log_timezone(), check_timezone(), DecodeDateTime(), DecodeTimeOnly(), FetchDynamicTimeZone(), parse_sane_timezone(), pg_timezone_initialize(), pg_tzset_offset(), timestamp_zone(), timestamptz_zone(), and timetz_zone().

237 {
238  pg_tz_cache *tzp;
239  struct state tzstate;
240  char uppername[TZ_STRLEN_MAX + 1];
241  char canonname[TZ_STRLEN_MAX + 1];
242  char *p;
243 
244  if (strlen(name) > TZ_STRLEN_MAX)
245  return NULL; /* not going to fit */
246 
247  if (!timezone_cache)
249  return NULL;
250 
251  /*
252  * Upcase the given name to perform a case-insensitive hashtable search.
253  * (We could alternatively downcase it, but we prefer upcase so that we
254  * can get consistently upcased results from tzparse() in case the name is
255  * a POSIX-style timezone spec.)
256  */
257  p = uppername;
258  while (*name)
259  *p++ = pg_toupper((unsigned char) *name++);
260  *p = '\0';
261 
263  uppername,
264  HASH_FIND,
265  NULL);
266  if (tzp)
267  {
268  /* Timezone found in cache, nothing more to do */
269  return &tzp->tz;
270  }
271 
272  /*
273  * "GMT" is always sent to tzparse(), as per discussion above.
274  */
275  if (strcmp(uppername, "GMT") == 0)
276  {
277  if (!tzparse(uppername, &tzstate, true))
278  {
279  /* This really, really should not happen ... */
280  elog(ERROR, "could not initialize GMT time zone");
281  }
282  /* Use uppercase name as canonical */
283  strcpy(canonname, uppername);
284  }
285  else if (tzload(uppername, canonname, &tzstate, true) != 0)
286  {
287  if (uppername[0] == ':' || !tzparse(uppername, &tzstate, false))
288  {
289  /* Unknown timezone. Fail our call instead of loading GMT! */
290  return NULL;
291  }
292  /* For POSIX timezone specs, use uppercase name as canonical */
293  strcpy(canonname, uppername);
294  }
295 
296  /* Save timezone in the cache */
298  uppername,
299  HASH_ENTER,
300  NULL);
301 
302  /* hash_search already copied uppername into the hash key */
303  strcpy(tzp->tz.TZname, canonname);
304  memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
305 
306  return &tzp->tz;
307 }
int tzload(const char *name, char *canonname, struct state *sp, bool doextend)
Definition: localtime.c:555
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
#define ERROR
Definition: elog.h:43
static bool init_timezone_hashtable(void)
Definition: pgtz.c:202
bool tzparse(const char *name, struct state *sp, bool lastditch)
Definition: localtime.c:896
pg_tz tz
Definition: pgtz.c:195
struct state state
Definition: pgtz.h:63
static HTAB * timezone_cache
Definition: pgtz.c:198
Definition: regguts.h:298
char TZname[TZ_STRLEN_MAX+1]
Definition: pgtz.h:62
const char * name
Definition: encode.c:521
#define elog
Definition: elog.h:219
unsigned char pg_toupper(unsigned char ch)
Definition: pgstrcasecmp.c:105

◆ pg_tzset_offset()

pg_tz* pg_tzset_offset ( long  gmtoffset)

Definition at line 322 of file pgtz.c.

References pg_tzset(), SECS_PER_HOUR, SECS_PER_MINUTE, and snprintf().

Referenced by check_timezone().

323 {
324  long absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset;
325  char offsetstr[64];
326  char tzname[128];
327 
328  snprintf(offsetstr, sizeof(offsetstr),
329  "%02ld", absoffset / SECS_PER_HOUR);
330  absoffset %= SECS_PER_HOUR;
331  if (absoffset != 0)
332  {
333  snprintf(offsetstr + strlen(offsetstr),
334  sizeof(offsetstr) - strlen(offsetstr),
335  ":%02ld", absoffset / SECS_PER_MINUTE);
336  absoffset %= SECS_PER_MINUTE;
337  if (absoffset != 0)
338  snprintf(offsetstr + strlen(offsetstr),
339  sizeof(offsetstr) - strlen(offsetstr),
340  ":%02ld", absoffset);
341  }
342  if (gmtoffset > 0)
343  snprintf(tzname, sizeof(tzname), "<-%s>+%s",
344  offsetstr, offsetstr);
345  else
346  snprintf(tzname, sizeof(tzname), "<+%s>-%s",
347  offsetstr, offsetstr);
348 
349  return pg_tzset(tzname);
350 }
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
#define SECS_PER_MINUTE
Definition: timestamp.h:88
pg_tz * pg_tzset(const char *name)
Definition: pgtz.c:236
#define SECS_PER_HOUR
Definition: timestamp.h:87

◆ scan_directory_ci()

static bool scan_directory_ci ( const char *  dirname,
const char *  fname,
int  fnamelen,
char *  canonname,
int  canonnamelen 
)
static

Definition at line 151 of file pgtz.c.

References AllocateDir(), dirent::d_name, FreeDir(), LOG, pg_strncasecmp(), ReadDirExtended(), and strlcpy().

Referenced by pg_open_tzfile().

153 {
154  bool found = false;
155  DIR *dirdesc;
156  struct dirent *direntry;
157 
158  dirdesc = AllocateDir(dirname);
159 
160  while ((direntry = ReadDirExtended(dirdesc, dirname, LOG)) != NULL)
161  {
162  /*
163  * Ignore . and .., plus any other "hidden" files. This is a security
164  * measure to prevent access to files outside the timezone directory.
165  */
166  if (direntry->d_name[0] == '.')
167  continue;
168 
169  if (strlen(direntry->d_name) == fnamelen &&
170  pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
171  {
172  /* Found our match */
173  strlcpy(canonname, direntry->d_name, canonnamelen);
174  found = true;
175  break;
176  }
177  }
178 
179  FreeDir(dirdesc);
180 
181  return found;
182 }
struct dirent * ReadDirExtended(DIR *dir, const char *dirname, int elevel)
Definition: fd.c:2671
#define LOG
Definition: elog.h:26
Definition: dirent.h:9
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
Definition: pgstrcasecmp.c:69
Definition: dirent.c:25
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2590
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
char d_name[MAX_PATH]
Definition: dirent.h:14
int FreeDir(DIR *dir)
Definition: fd.c:2708

Variable Documentation

◆ log_timezone

◆ session_timezone

◆ timezone_cache

HTAB* timezone_cache = NULL
static

Definition at line 198 of file pgtz.c.