PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pgtz.c File Reference
#include "postgres.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.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

#define MAX_TZDIR_DEPTH   10

Definition at line 367 of file pgtz.c.

Referenced by pg_tzenumerate_next().

Function Documentation

static bool init_timezone_hashtable ( void  )
static

Definition at line 184 of file pgtz.c.

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

Referenced by pg_tzset().

185 {
186  HASHCTL hash_ctl;
187 
188  MemSet(&hash_ctl, 0, sizeof(hash_ctl));
189 
190  hash_ctl.keysize = TZ_STRLEN_MAX + 1;
191  hash_ctl.entrysize = sizeof(pg_tz_cache);
192 
193  timezone_cache = hash_create("Timezones",
194  4,
195  &hash_ctl,
196  HASH_ELEM);
197  if (!timezone_cache)
198  return false;
199 
200  return true;
201 }
#define HASH_ELEM
Definition: hsearch.h:87
Size entrysize
Definition: hsearch.h:73
#define MemSet(start, val, len)
Definition: c.h:853
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:301
Size keysize
Definition: hsearch.h:72
static HTAB * timezone_cache
Definition: pgtz.c:180
int pg_open_tzfile ( const char *  name,
char *  canonname 
)

Definition at line 75 of file pgtz.c.

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

76 {
77  const char *fname;
78  char fullname[MAXPGPATH];
79  int fullnamelen;
80  int orignamelen;
81 
82  /*
83  * Loop to split the given name into directory levels; for each level,
84  * search using scan_directory_ci().
85  */
86  strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
87  orignamelen = fullnamelen = strlen(fullname);
88  fname = name;
89  for (;;)
90  {
91  const char *slashptr;
92  int fnamelen;
93 
94  slashptr = strchr(fname, '/');
95  if (slashptr)
96  fnamelen = slashptr - fname;
97  else
98  fnamelen = strlen(fname);
99  if (fullnamelen + 1 + fnamelen >= MAXPGPATH)
100  return -1; /* not gonna fit */
101  if (!scan_directory_ci(fullname, fname, fnamelen,
102  fullname + fullnamelen + 1,
103  MAXPGPATH - fullnamelen - 1))
104  return -1;
105  fullname[fullnamelen++] = '/';
106  fullnamelen += strlen(fullname + fullnamelen);
107  if (slashptr)
108  fname = slashptr + 1;
109  else
110  break;
111  }
112 
113  if (canonname)
114  strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
115 
116  return open(fullname, O_RDONLY | PG_BINARY, 0);
117 }
static const char * pg_TZDIR(void)
Definition: pgtz.c:42
#define PG_BINARY
Definition: c.h:1038
#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:126
const char * name
Definition: encode.c:521
void pg_timezone_initialize ( void  )

Definition at line 345 of file pgtz.c.

References pg_tzset(), and session_timezone.

Referenced by InitializeGUCOptions().

346 {
347  /*
348  * We may not yet know where PGSHAREDIR is (in particular this is true in
349  * an EXEC_BACKEND subprocess). So use "GMT", which pg_tzset forces to be
350  * interpreted without reference to the filesystem. This corresponds to
351  * the bootstrap default for these variables in guc.c, although in
352  * principle it could be different.
353  */
354  session_timezone = pg_tzset("GMT");
356 }
pg_tz * log_timezone
Definition: pgtz.c:30
pg_tz * pg_tzset(const char *name)
Definition: pgtz.c:218
pg_tz * session_timezone
Definition: pgtz.c:27
static const char* pg_TZDIR ( void  )
static

Definition at line 42 of file pgtz.c.

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

Referenced by pg_open_tzfile(), and pg_tzenumerate_start().

43 {
44 #ifndef SYSTEMTZDIR
45  /* normal case: timezone stuff is under our share dir */
46  static bool done_tzdir = false;
47  static char tzdir[MAXPGPATH];
48 
49  if (done_tzdir)
50  return tzdir;
51 
53  strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
54 
55  done_tzdir = true;
56  return tzdir;
57 #else
58  /* we're configured to use system's timezone database */
59  return SYSTEMTZDIR;
60 #endif
61 }
#define MAXPGPATH
char my_exec_path[MAXPGPATH]
Definition: globals.c:63
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
void pg_tzenumerate_end ( pg_tzenum dir)

Definition at line 398 of file pgtz.c.

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

Referenced by pg_timezone_names().

399 {
400  while (dir->depth >= 0)
401  {
402  FreeDir(dir->dirdesc[dir->depth]);
403  pfree(dir->dirname[dir->depth]);
404  dir->depth--;
405  }
406  pfree(dir);
407 }
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:373
void pfree(void *pointer)
Definition: mcxt.c:992
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:374
int depth
Definition: pgtz.c:372
int FreeDir(DIR *dir)
Definition: fd.c:2393
pg_tz* pg_tzenumerate_next ( pg_tzenum dir)

Definition at line 410 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, NULL, pfree(), pg_tz_acceptable(), pstrdup(), ReadDir(), snprintf(), pg_tz::state, pg_tzenum::tz, tzload(), and pg_tz::TZname.

Referenced by pg_timezone_names().

411 {
412  while (dir->depth >= 0)
413  {
414  struct dirent *direntry;
415  char fullname[MAXPGPATH];
416  struct stat statbuf;
417 
418  direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
419 
420  if (!direntry)
421  {
422  /* End of this directory */
423  FreeDir(dir->dirdesc[dir->depth]);
424  pfree(dir->dirname[dir->depth]);
425  dir->depth--;
426  continue;
427  }
428 
429  if (direntry->d_name[0] == '.')
430  continue;
431 
432  snprintf(fullname, MAXPGPATH, "%s/%s",
433  dir->dirname[dir->depth], direntry->d_name);
434  if (stat(fullname, &statbuf) != 0)
435  ereport(ERROR,
437  errmsg("could not stat \"%s\": %m", fullname)));
438 
439  if (S_ISDIR(statbuf.st_mode))
440  {
441  /* Step into the subdirectory */
442  if (dir->depth >= MAX_TZDIR_DEPTH - 1)
443  ereport(ERROR,
444  (errmsg_internal("timezone directory stack overflow")));
445  dir->depth++;
446  dir->dirname[dir->depth] = pstrdup(fullname);
447  dir->dirdesc[dir->depth] = AllocateDir(fullname);
448  if (!dir->dirdesc[dir->depth])
449  ereport(ERROR,
451  errmsg("could not open directory \"%s\": %m",
452  fullname)));
453 
454  /* Start over reading in the new directory */
455  continue;
456  }
457 
458  /*
459  * Load this timezone using tzload() not pg_tzset(), so we don't fill
460  * the cache
461  */
462  if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
463  true) != 0)
464  {
465  /* Zone could not be loaded, ignore it */
466  continue;
467  }
468 
469  if (!pg_tz_acceptable(&dir->tz))
470  {
471  /* Ignore leap-second zones */
472  continue;
473  }
474 
475  /* Timezone loaded OK. */
476  return &dir->tz;
477  }
478 
479  /* Nothing more found */
480  return NULL;
481 }
struct pg_tz tz
Definition: pgtz.c:375
int tzload(const char *name, char *canonname, struct state *sp, bool doextend)
Definition: localtime.c:537
char * pstrdup(const char *in)
Definition: mcxt.c:1165
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:373
void pfree(void *pointer)
Definition: mcxt.c:992
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:374
#define ERROR
Definition: elog.h:43
#define MAXPGPATH
#define MAX_TZDIR_DEPTH
Definition: pgtz.c:367
int errcode_for_file_access(void)
Definition: elog.c:598
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2284
#define ereport(elevel, rest)
Definition: elog.h:122
bool pg_tz_acceptable(pg_tz *tz)
Definition: localtime.c:1785
struct state state
Definition: pgtz.h:63
int baselen
Definition: pgtz.c:371
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
#define NULL
Definition: c.h:226
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2350
char TZname[TZ_STRLEN_MAX+1]
Definition: pgtz.h:62
int errmsg(const char *fmt,...)
Definition: elog.c:797
int depth
Definition: pgtz.c:372
int FreeDir(DIR *dir)
Definition: fd.c:2393
pg_tzenum* pg_tzenumerate_start ( void  )

Definition at line 381 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().

382 {
383  pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
384  char *startdir = pstrdup(pg_TZDIR());
385 
386  ret->baselen = strlen(startdir) + 1;
387  ret->depth = 0;
388  ret->dirname[0] = startdir;
389  ret->dirdesc[0] = AllocateDir(startdir);
390  if (!ret->dirdesc[0])
391  ereport(ERROR,
393  errmsg("could not open directory \"%s\": %m", startdir)));
394  return ret;
395 }
char * pstrdup(const char *in)
Definition: mcxt.c:1165
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:373
static const char * pg_TZDIR(void)
Definition: pgtz.c:42
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:374
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:598
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2284
#define ereport(elevel, rest)
Definition: elog.h:122
void * palloc0(Size size)
Definition: mcxt.c:920
int baselen
Definition: pgtz.c:371
int errmsg(const char *fmt,...)
Definition: elog.c:797
int depth
Definition: pgtz.c:372
pg_tz* pg_tzset ( const char *  name)

Definition at line 218 of file pgtz.c.

References elog, ERROR, HASH_ENTER, HASH_FIND, hash_search(), init_timezone_hashtable(), NULL, 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().

219 {
220  pg_tz_cache *tzp;
221  struct state tzstate;
222  char uppername[TZ_STRLEN_MAX + 1];
223  char canonname[TZ_STRLEN_MAX + 1];
224  char *p;
225 
226  if (strlen(name) > TZ_STRLEN_MAX)
227  return NULL; /* not going to fit */
228 
229  if (!timezone_cache)
231  return NULL;
232 
233  /*
234  * Upcase the given name to perform a case-insensitive hashtable search.
235  * (We could alternatively downcase it, but we prefer upcase so that we
236  * can get consistently upcased results from tzparse() in case the name is
237  * a POSIX-style timezone spec.)
238  */
239  p = uppername;
240  while (*name)
241  *p++ = pg_toupper((unsigned char) *name++);
242  *p = '\0';
243 
245  uppername,
246  HASH_FIND,
247  NULL);
248  if (tzp)
249  {
250  /* Timezone found in cache, nothing more to do */
251  return &tzp->tz;
252  }
253 
254  /*
255  * "GMT" is always sent to tzparse(), as per discussion above.
256  */
257  if (strcmp(uppername, "GMT") == 0)
258  {
259  if (!tzparse(uppername, &tzstate, true))
260  {
261  /* This really, really should not happen ... */
262  elog(ERROR, "could not initialize GMT time zone");
263  }
264  /* Use uppercase name as canonical */
265  strcpy(canonname, uppername);
266  }
267  else if (tzload(uppername, canonname, &tzstate, true) != 0)
268  {
269  if (uppername[0] == ':' || !tzparse(uppername, &tzstate, false))
270  {
271  /* Unknown timezone. Fail our call instead of loading GMT! */
272  return NULL;
273  }
274  /* For POSIX timezone specs, use uppercase name as canonical */
275  strcpy(canonname, uppername);
276  }
277 
278  /* Save timezone in the cache */
280  uppername,
281  HASH_ENTER,
282  NULL);
283 
284  /* hash_search already copied uppername into the hash key */
285  strcpy(tzp->tz.TZname, canonname);
286  memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
287 
288  return &tzp->tz;
289 }
int tzload(const char *name, char *canonname, struct state *sp, bool doextend)
Definition: localtime.c:537
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:885
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
#define ERROR
Definition: elog.h:43
static bool init_timezone_hashtable(void)
Definition: pgtz.c:184
bool tzparse(const char *name, struct state *sp, bool lastditch)
Definition: localtime.c:878
pg_tz tz
Definition: pgtz.c:177
struct state state
Definition: pgtz.h:63
static HTAB * timezone_cache
Definition: pgtz.c:180
#define NULL
Definition: c.h:226
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_tz* pg_tzset_offset ( long  gmtoffset)

Definition at line 304 of file pgtz.c.

References pg_tzset(), SECSPERHOUR, SECSPERMIN, and snprintf().

Referenced by check_timezone().

305 {
306  long absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset;
307  char offsetstr[64];
308  char tzname[128];
309 
310  snprintf(offsetstr, sizeof(offsetstr),
311  "%02ld", absoffset / SECSPERHOUR);
312  absoffset %= SECSPERHOUR;
313  if (absoffset != 0)
314  {
315  snprintf(offsetstr + strlen(offsetstr),
316  sizeof(offsetstr) - strlen(offsetstr),
317  ":%02ld", absoffset / SECSPERMIN);
318  absoffset %= SECSPERMIN;
319  if (absoffset != 0)
320  snprintf(offsetstr + strlen(offsetstr),
321  sizeof(offsetstr) - strlen(offsetstr),
322  ":%02ld", absoffset);
323  }
324  if (gmtoffset > 0)
325  snprintf(tzname, sizeof(tzname), "<-%s>+%s",
326  offsetstr, offsetstr);
327  else
328  snprintf(tzname, sizeof(tzname), "<+%s>-%s",
329  offsetstr, offsetstr);
330 
331  return pg_tzset(tzname);
332 }
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
pg_tz * pg_tzset(const char *name)
Definition: pgtz.c:218
#define SECSPERMIN
Definition: tzfile.h:103
#define SECSPERHOUR
Definition: tzfile.h:109
static bool scan_directory_ci ( const char *  dirname,
const char *  fname,
int  fnamelen,
char *  canonname,
int  canonnamelen 
)
static

Definition at line 126 of file pgtz.c.

References AllocateDir(), dirent::d_name, ereport, errcode_for_file_access(), errmsg(), FreeDir(), LOG, NULL, pg_strncasecmp(), ReadDir(), and strlcpy().

Referenced by pg_open_tzfile().

128 {
129  bool found = false;
130  DIR *dirdesc;
131  struct dirent *direntry;
132 
133  dirdesc = AllocateDir(dirname);
134  if (!dirdesc)
135  {
136  ereport(LOG,
138  errmsg("could not open directory \"%s\": %m", dirname)));
139  return false;
140  }
141 
142  while ((direntry = ReadDir(dirdesc, dirname)) != NULL)
143  {
144  /*
145  * Ignore . and .., plus any other "hidden" files. This is a security
146  * measure to prevent access to files outside the timezone directory.
147  */
148  if (direntry->d_name[0] == '.')
149  continue;
150 
151  if (strlen(direntry->d_name) == fnamelen &&
152  pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
153  {
154  /* Found our match */
155  strlcpy(canonname, direntry->d_name, canonnamelen);
156  found = true;
157  break;
158  }
159  }
160 
161  FreeDir(dirdesc);
162 
163  return found;
164 }
#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
int errcode_for_file_access(void)
Definition: elog.c:598
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2284
#define ereport(elevel, rest)
Definition: elog.h:122
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define NULL
Definition: c.h:226
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2350
int errmsg(const char *fmt,...)
Definition: elog.c:797
char d_name[MAX_PATH]
Definition: dirent.h:14
int FreeDir(DIR *dir)
Definition: fd.c:2393

Variable Documentation

HTAB* timezone_cache = NULL
static

Definition at line 180 of file pgtz.c.