PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pgtz.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pgtz.c
4  * Timezone Library Integration Functions
5  *
6  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  * src/timezone/pgtz.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 
15 #include <ctype.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <time.h>
19 
20 #include "datatype/timestamp.h"
21 #include "miscadmin.h"
22 #include "pgtz.h"
23 #include "storage/fd.h"
24 #include "utils/hsearch.h"
25 
26 
27 /* Current session timezone (controlled by TimeZone GUC) */
29 
30 /* Current log timezone (controlled by log_timezone GUC) */
32 
33 
34 static bool scan_directory_ci(const char *dirname,
35  const char *fname, int fnamelen,
36  char *canonname, int canonnamelen);
37 
38 
39 /*
40  * Return full pathname of timezone data directory
41  */
42 static const char *
43 pg_TZDIR(void)
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 }
63 
64 
65 /*
66  * Given a timezone name, open() the timezone data file. Return the
67  * file descriptor if successful, -1 if not.
68  *
69  * The input name is searched for case-insensitively (we assume that the
70  * timezone database does not contain case-equivalent names).
71  *
72  * If "canonname" is not NULL, then on success the canonical spelling of the
73  * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
74  */
75 int
76 pg_open_tzfile(const char *name, char *canonname)
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 }
143 
144 
145 /*
146  * Scan specified directory for a case-insensitive match to fname
147  * (of length fnamelen --- fname may not be null terminated!). If found,
148  * copy the actual filename into canonname and return true.
149  */
150 static bool
151 scan_directory_ci(const char *dirname, const char *fname, int fnamelen,
152  char *canonname, int canonnamelen)
153 {
154  bool found = false;
155  DIR *dirdesc;
156  struct dirent *direntry;
157 
158  dirdesc = AllocateDir(dirname);
159  if (!dirdesc)
160  {
161  ereport(LOG,
163  errmsg("could not open directory \"%s\": %m", dirname)));
164  return false;
165  }
166 
167  while ((direntry = ReadDir(dirdesc, dirname)) != NULL)
168  {
169  /*
170  * Ignore . and .., plus any other "hidden" files. This is a security
171  * measure to prevent access to files outside the timezone directory.
172  */
173  if (direntry->d_name[0] == '.')
174  continue;
175 
176  if (strlen(direntry->d_name) == fnamelen &&
177  pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
178  {
179  /* Found our match */
180  strlcpy(canonname, direntry->d_name, canonnamelen);
181  found = true;
182  break;
183  }
184  }
185 
186  FreeDir(dirdesc);
187 
188  return found;
189 }
190 
191 
192 /*
193  * We keep loaded timezones in a hashtable so we don't have to
194  * load and parse the TZ definition file every time one is selected.
195  * Because we want timezone names to be found case-insensitively,
196  * the hash key is the uppercased name of the zone.
197  */
198 typedef struct
199 {
200  /* tznameupper contains the all-upper-case name of the timezone */
201  char tznameupper[TZ_STRLEN_MAX + 1];
203 } pg_tz_cache;
204 
205 static HTAB *timezone_cache = NULL;
206 
207 
208 static bool
210 {
211  HASHCTL hash_ctl;
212 
213  MemSet(&hash_ctl, 0, sizeof(hash_ctl));
214 
215  hash_ctl.keysize = TZ_STRLEN_MAX + 1;
216  hash_ctl.entrysize = sizeof(pg_tz_cache);
217 
218  timezone_cache = hash_create("Timezones",
219  4,
220  &hash_ctl,
221  HASH_ELEM);
222  if (!timezone_cache)
223  return false;
224 
225  return true;
226 }
227 
228 /*
229  * Load a timezone from file or from cache.
230  * Does not verify that the timezone is acceptable!
231  *
232  * "GMT" is always interpreted as the tzparse() definition, without attempting
233  * to load a definition from the filesystem. This has a number of benefits:
234  * 1. It's guaranteed to succeed, so we don't have the failure mode wherein
235  * the bootstrap default timezone setting doesn't work (as could happen if
236  * the OS attempts to supply a leap-second-aware version of "GMT").
237  * 2. Because we aren't accessing the filesystem, we can safely initialize
238  * the "GMT" zone definition before my_exec_path is known.
239  * 3. It's quick enough that we don't waste much time when the bootstrap
240  * default timezone setting is later overridden from postgresql.conf.
241  */
242 pg_tz *
243 pg_tzset(const char *name)
244 {
245  pg_tz_cache *tzp;
246  struct state tzstate;
247  char uppername[TZ_STRLEN_MAX + 1];
248  char canonname[TZ_STRLEN_MAX + 1];
249  char *p;
250 
251  if (strlen(name) > TZ_STRLEN_MAX)
252  return NULL; /* not going to fit */
253 
254  if (!timezone_cache)
256  return NULL;
257 
258  /*
259  * Upcase the given name to perform a case-insensitive hashtable search.
260  * (We could alternatively downcase it, but we prefer upcase so that we
261  * can get consistently upcased results from tzparse() in case the name is
262  * a POSIX-style timezone spec.)
263  */
264  p = uppername;
265  while (*name)
266  *p++ = pg_toupper((unsigned char) *name++);
267  *p = '\0';
268 
269  tzp = (pg_tz_cache *) hash_search(timezone_cache,
270  uppername,
271  HASH_FIND,
272  NULL);
273  if (tzp)
274  {
275  /* Timezone found in cache, nothing more to do */
276  return &tzp->tz;
277  }
278 
279  /*
280  * "GMT" is always sent to tzparse(), as per discussion above.
281  */
282  if (strcmp(uppername, "GMT") == 0)
283  {
284  if (!tzparse(uppername, &tzstate, true))
285  {
286  /* This really, really should not happen ... */
287  elog(ERROR, "could not initialize GMT time zone");
288  }
289  /* Use uppercase name as canonical */
290  strcpy(canonname, uppername);
291  }
292  else if (tzload(uppername, canonname, &tzstate, true) != 0)
293  {
294  if (uppername[0] == ':' || !tzparse(uppername, &tzstate, false))
295  {
296  /* Unknown timezone. Fail our call instead of loading GMT! */
297  return NULL;
298  }
299  /* For POSIX timezone specs, use uppercase name as canonical */
300  strcpy(canonname, uppername);
301  }
302 
303  /* Save timezone in the cache */
304  tzp = (pg_tz_cache *) hash_search(timezone_cache,
305  uppername,
306  HASH_ENTER,
307  NULL);
308 
309  /* hash_search already copied uppername into the hash key */
310  strcpy(tzp->tz.TZname, canonname);
311  memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
312 
313  return &tzp->tz;
314 }
315 
316 /*
317  * Load a fixed-GMT-offset timezone.
318  * This is used for SQL-spec SET TIME ZONE INTERVAL 'foo' cases.
319  * It's otherwise equivalent to pg_tzset().
320  *
321  * The GMT offset is specified in seconds, positive values meaning west of
322  * Greenwich (ie, POSIX not ISO sign convention). However, we use ISO
323  * sign convention in the displayable abbreviation for the zone.
324  *
325  * Caution: this can fail (return NULL) if the specified offset is outside
326  * the range allowed by the zic library.
327  */
328 pg_tz *
329 pg_tzset_offset(long gmtoffset)
330 {
331  long absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset;
332  char offsetstr[64];
333  char tzname[128];
334 
335  snprintf(offsetstr, sizeof(offsetstr),
336  "%02ld", absoffset / SECS_PER_HOUR);
337  absoffset %= SECS_PER_HOUR;
338  if (absoffset != 0)
339  {
340  snprintf(offsetstr + strlen(offsetstr),
341  sizeof(offsetstr) - strlen(offsetstr),
342  ":%02ld", absoffset / SECS_PER_MINUTE);
343  absoffset %= SECS_PER_MINUTE;
344  if (absoffset != 0)
345  snprintf(offsetstr + strlen(offsetstr),
346  sizeof(offsetstr) - strlen(offsetstr),
347  ":%02ld", absoffset);
348  }
349  if (gmtoffset > 0)
350  snprintf(tzname, sizeof(tzname), "<-%s>+%s",
351  offsetstr, offsetstr);
352  else
353  snprintf(tzname, sizeof(tzname), "<+%s>-%s",
354  offsetstr, offsetstr);
355 
356  return pg_tzset(tzname);
357 }
358 
359 
360 /*
361  * Initialize timezone library
362  *
363  * This is called before GUC variable initialization begins. Its purpose
364  * is to ensure that log_timezone has a valid value before any logging GUC
365  * variables could become set to values that require elog.c to provide
366  * timestamps (e.g., log_line_prefix). We may as well initialize
367  * session_timestamp to something valid, too.
368  */
369 void
371 {
372  /*
373  * We may not yet know where PGSHAREDIR is (in particular this is true in
374  * an EXEC_BACKEND subprocess). So use "GMT", which pg_tzset forces to be
375  * interpreted without reference to the filesystem. This corresponds to
376  * the bootstrap default for these variables in guc.c, although in
377  * principle it could be different.
378  */
379  session_timezone = pg_tzset("GMT");
380  log_timezone = session_timezone;
381 }
382 
383 
384 /*
385  * Functions to enumerate available timezones
386  *
387  * Note that pg_tzenumerate_next() will return a pointer into the pg_tzenum
388  * structure, so the data is only valid up to the next call.
389  *
390  * All data is allocated using palloc in the current context.
391  */
392 #define MAX_TZDIR_DEPTH 10
393 
394 struct pg_tzenum
395 {
396  int baselen;
397  int depth;
400  struct pg_tz tz;
401 };
402 
403 /* typedef pg_tzenum is declared in pgtime.h */
404 
405 pg_tzenum *
407 {
408  pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
409  char *startdir = pstrdup(pg_TZDIR());
410 
411  ret->baselen = strlen(startdir) + 1;
412  ret->depth = 0;
413  ret->dirname[0] = startdir;
414  ret->dirdesc[0] = AllocateDir(startdir);
415  if (!ret->dirdesc[0])
416  ereport(ERROR,
418  errmsg("could not open directory \"%s\": %m", startdir)));
419  return ret;
420 }
421 
422 void
424 {
425  while (dir->depth >= 0)
426  {
427  FreeDir(dir->dirdesc[dir->depth]);
428  pfree(dir->dirname[dir->depth]);
429  dir->depth--;
430  }
431  pfree(dir);
432 }
433 
434 pg_tz *
436 {
437  while (dir->depth >= 0)
438  {
439  struct dirent *direntry;
440  char fullname[MAXPGPATH * 2];
441  struct stat statbuf;
442 
443  direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
444 
445  if (!direntry)
446  {
447  /* End of this directory */
448  FreeDir(dir->dirdesc[dir->depth]);
449  pfree(dir->dirname[dir->depth]);
450  dir->depth--;
451  continue;
452  }
453 
454  if (direntry->d_name[0] == '.')
455  continue;
456 
457  snprintf(fullname, sizeof(fullname), "%s/%s",
458  dir->dirname[dir->depth], direntry->d_name);
459  if (stat(fullname, &statbuf) != 0)
460  ereport(ERROR,
462  errmsg("could not stat \"%s\": %m", fullname)));
463 
464  if (S_ISDIR(statbuf.st_mode))
465  {
466  /* Step into the subdirectory */
467  if (dir->depth >= MAX_TZDIR_DEPTH - 1)
468  ereport(ERROR,
469  (errmsg_internal("timezone directory stack overflow")));
470  dir->depth++;
471  dir->dirname[dir->depth] = pstrdup(fullname);
472  dir->dirdesc[dir->depth] = AllocateDir(fullname);
473  if (!dir->dirdesc[dir->depth])
474  ereport(ERROR,
476  errmsg("could not open directory \"%s\": %m",
477  fullname)));
478 
479  /* Start over reading in the new directory */
480  continue;
481  }
482 
483  /*
484  * Load this timezone using tzload() not pg_tzset(), so we don't fill
485  * the cache. Also, don't ask for the canonical spelling: we already
486  * know it, and pg_open_tzfile's way of finding it out is pretty
487  * inefficient.
488  */
489  if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0)
490  {
491  /* Zone could not be loaded, ignore it */
492  continue;
493  }
494 
495  if (!pg_tz_acceptable(&dir->tz))
496  {
497  /* Ignore leap-second zones */
498  continue;
499  }
500 
501  /* OK, return the canonical zone name spelling. */
502  strlcpy(dir->tz.TZname, fullname + dir->baselen,
503  sizeof(dir->tz.TZname));
504 
505  /* Timezone loaded OK. */
506  return &dir->tz;
507  }
508 
509  /* Nothing more found */
510  return NULL;
511 }
pg_tz * pg_tzset_offset(long gmtoffset)
Definition: pgtz.c:329
struct pg_tz tz
Definition: pgtz.c:400
#define HASH_ELEM
Definition: hsearch.h:87
void pg_timezone_initialize(void)
Definition: pgtz.c:370
int tzload(const char *name, char *canonname, struct state *sp, bool doextend)
Definition: localtime.c:556
char * pstrdup(const char *in)
Definition: mcxt.c:1076
pg_tz * log_timezone
Definition: pgtz.c:31
Size entrysize
Definition: hsearch.h:73
#define MemSet(start, val, len)
Definition: c.h:846
return result
Definition: formatting.c:1633
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:902
#define LOG
Definition: elog.h:26
Definition: dirent.h:9
pg_tz * pg_tzenumerate_next(pg_tzenum *dir)
Definition: pgtz.c:435
DIR * dirdesc[MAX_TZDIR_DEPTH]
Definition: pgtz.c:398
static const char * pg_TZDIR(void)
Definition: pgtz.c:43
#define PG_BINARY
Definition: c.h:1027
int pg_open_tzfile(const char *name, char *canonname)
Definition: pgtz.c:76
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
Definition: pgstrcasecmp.c:69
Definition: dynahash.c:208
void pfree(void *pointer)
Definition: mcxt.c:949
#define TZ_STRLEN_MAX
Definition: pgtime.h:44
Definition: dirent.c:25
char * dirname[MAX_TZDIR_DEPTH]
Definition: pgtz.c:399
#define ERROR
Definition: elog.h:43
static bool init_timezone_hashtable(void)
Definition: pgtz.c:209
#define MAXPGPATH
#define MAX_TZDIR_DEPTH
Definition: pgtz.c:392
#define SECS_PER_MINUTE
Definition: timestamp.h:88
int errcode_for_file_access(void)
Definition: elog.c:598
pg_tz * pg_tzset(const char *name)
Definition: pgtz.c:243
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2335
#define ereport(elevel, rest)
Definition: elog.h:122
bool tzparse(const char *name, struct state *sp, bool lastditch)
Definition: localtime.c:897
#define SECS_PER_HOUR
Definition: timestamp.h:87
char my_exec_path[MAXPGPATH]
Definition: globals.c:64
void * palloc0(Size size)
Definition: mcxt.c:877
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
bool pg_tz_acceptable(pg_tz *tz)
Definition: localtime.c:1845
pg_tz tz
Definition: pgtz.c:202
struct state state
Definition: pgtz.h:63
Definition: pgtz.h:59
Size keysize
Definition: hsearch.h:72
static HTAB * timezone_cache
Definition: pgtz.c:205
int baselen
Definition: pgtz.c:396
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
int errmsg_internal(const char *fmt,...)
Definition: elog.c:827
pg_tz * session_timezone
Definition: pgtz.c:28
Definition: regguts.h:298
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2401
char TZname[TZ_STRLEN_MAX+1]
Definition: pgtz.h:62
void pg_tzenumerate_end(pg_tzenum *dir)
Definition: pgtz.c:423
const char * name
Definition: encode.c:521
int errmsg(const char *fmt,...)
Definition: elog.c:797
char d_name[MAX_PATH]
Definition: dirent.h:14
#define elog
Definition: elog.h:219
void get_share_path(const char *my_exec_path, char *ret_path)
Definition: path.c:704
pg_tzenum * pg_tzenumerate_start(void)
Definition: pgtz.c:406
int depth
Definition: pgtz.c:397
unsigned char pg_toupper(unsigned char ch)
Definition: pgstrcasecmp.c:105
int FreeDir(DIR *dir)
Definition: fd.c:2444