00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "postgres.h"
00014
00015 #include <ctype.h>
00016 #include <fcntl.h>
00017 #include <sys/stat.h>
00018 #include <time.h>
00019
00020 #include "miscadmin.h"
00021 #include "pgtz.h"
00022 #include "storage/fd.h"
00023 #include "utils/hsearch.h"
00024
00025
00026
00027 pg_tz *session_timezone = NULL;
00028
00029
00030 pg_tz *log_timezone = NULL;
00031
00032
00033 static bool scan_directory_ci(const char *dirname,
00034 const char *fname, int fnamelen,
00035 char *canonname, int canonnamelen);
00036
00037
00038
00039
00040
00041 static const char *
00042 pg_TZDIR(void)
00043 {
00044 #ifndef SYSTEMTZDIR
00045
00046 static bool done_tzdir = false;
00047 static char tzdir[MAXPGPATH];
00048
00049 if (done_tzdir)
00050 return tzdir;
00051
00052 get_share_path(my_exec_path, tzdir);
00053 strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
00054
00055 done_tzdir = true;
00056 return tzdir;
00057 #else
00058
00059 return SYSTEMTZDIR;
00060 #endif
00061 }
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 int
00075 pg_open_tzfile(const char *name, char *canonname)
00076 {
00077 const char *fname;
00078 char fullname[MAXPGPATH];
00079 int fullnamelen;
00080 int orignamelen;
00081
00082
00083
00084
00085
00086 strcpy(fullname, pg_TZDIR());
00087 orignamelen = fullnamelen = strlen(fullname);
00088 fname = name;
00089 for (;;)
00090 {
00091 const char *slashptr;
00092 int fnamelen;
00093
00094 slashptr = strchr(fname, '/');
00095 if (slashptr)
00096 fnamelen = slashptr - fname;
00097 else
00098 fnamelen = strlen(fname);
00099 if (fullnamelen + 1 + fnamelen >= MAXPGPATH)
00100 return -1;
00101 if (!scan_directory_ci(fullname, fname, fnamelen,
00102 fullname + fullnamelen + 1,
00103 MAXPGPATH - fullnamelen - 1))
00104 return -1;
00105 fullname[fullnamelen++] = '/';
00106 fullnamelen += strlen(fullname + fullnamelen);
00107 if (slashptr)
00108 fname = slashptr + 1;
00109 else
00110 break;
00111 }
00112
00113 if (canonname)
00114 strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
00115
00116 return open(fullname, O_RDONLY | PG_BINARY, 0);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 static bool
00126 scan_directory_ci(const char *dirname, const char *fname, int fnamelen,
00127 char *canonname, int canonnamelen)
00128 {
00129 bool found = false;
00130 DIR *dirdesc;
00131 struct dirent *direntry;
00132
00133 dirdesc = AllocateDir(dirname);
00134 if (!dirdesc)
00135 {
00136 ereport(LOG,
00137 (errcode_for_file_access(),
00138 errmsg("could not open directory \"%s\": %m", dirname)));
00139 return false;
00140 }
00141
00142 while ((direntry = ReadDir(dirdesc, dirname)) != NULL)
00143 {
00144
00145
00146
00147
00148 if (direntry->d_name[0] == '.')
00149 continue;
00150
00151 if (strlen(direntry->d_name) == fnamelen &&
00152 pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
00153 {
00154
00155 strlcpy(canonname, direntry->d_name, canonnamelen);
00156 found = true;
00157 break;
00158 }
00159 }
00160
00161 FreeDir(dirdesc);
00162
00163 return found;
00164 }
00165
00166
00167
00168
00169
00170
00171
00172
00173 typedef struct
00174 {
00175
00176 char tznameupper[TZ_STRLEN_MAX + 1];
00177 pg_tz tz;
00178 } pg_tz_cache;
00179
00180 static HTAB *timezone_cache = NULL;
00181
00182
00183 static bool
00184 init_timezone_hashtable(void)
00185 {
00186 HASHCTL hash_ctl;
00187
00188 MemSet(&hash_ctl, 0, sizeof(hash_ctl));
00189
00190 hash_ctl.keysize = TZ_STRLEN_MAX + 1;
00191 hash_ctl.entrysize = sizeof(pg_tz_cache);
00192
00193 timezone_cache = hash_create("Timezones",
00194 4,
00195 &hash_ctl,
00196 HASH_ELEM);
00197 if (!timezone_cache)
00198 return false;
00199
00200 return true;
00201 }
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217 pg_tz *
00218 pg_tzset(const char *name)
00219 {
00220 pg_tz_cache *tzp;
00221 struct state tzstate;
00222 char uppername[TZ_STRLEN_MAX + 1];
00223 char canonname[TZ_STRLEN_MAX + 1];
00224 char *p;
00225
00226 if (strlen(name) > TZ_STRLEN_MAX)
00227 return NULL;
00228
00229 if (!timezone_cache)
00230 if (!init_timezone_hashtable())
00231 return NULL;
00232
00233
00234
00235
00236
00237
00238
00239 p = uppername;
00240 while (*name)
00241 *p++ = pg_toupper((unsigned char) *name++);
00242 *p = '\0';
00243
00244 tzp = (pg_tz_cache *) hash_search(timezone_cache,
00245 uppername,
00246 HASH_FIND,
00247 NULL);
00248 if (tzp)
00249 {
00250
00251 return &tzp->tz;
00252 }
00253
00254
00255
00256
00257 if (strcmp(uppername, "GMT") == 0)
00258 {
00259 if (tzparse(uppername, &tzstate, TRUE) != 0)
00260 {
00261
00262 elog(ERROR, "could not initialize GMT time zone");
00263 }
00264
00265 strcpy(canonname, uppername);
00266 }
00267 else if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
00268 {
00269 if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
00270 {
00271
00272 return NULL;
00273 }
00274
00275 strcpy(canonname, uppername);
00276 }
00277
00278
00279 tzp = (pg_tz_cache *) hash_search(timezone_cache,
00280 uppername,
00281 HASH_ENTER,
00282 NULL);
00283
00284
00285 strcpy(tzp->tz.TZname, canonname);
00286 memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
00287
00288 return &tzp->tz;
00289 }
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301 void
00302 pg_timezone_initialize(void)
00303 {
00304
00305
00306
00307
00308
00309
00310
00311 session_timezone = pg_tzset("GMT");
00312 log_timezone = session_timezone;
00313 }
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324 #define MAX_TZDIR_DEPTH 10
00325
00326 struct pg_tzenum
00327 {
00328 int baselen;
00329 int depth;
00330 DIR *dirdesc[MAX_TZDIR_DEPTH];
00331 char *dirname[MAX_TZDIR_DEPTH];
00332 struct pg_tz tz;
00333 };
00334
00335
00336
00337 pg_tzenum *
00338 pg_tzenumerate_start(void)
00339 {
00340 pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
00341 char *startdir = pstrdup(pg_TZDIR());
00342
00343 ret->baselen = strlen(startdir) + 1;
00344 ret->depth = 0;
00345 ret->dirname[0] = startdir;
00346 ret->dirdesc[0] = AllocateDir(startdir);
00347 if (!ret->dirdesc[0])
00348 ereport(ERROR,
00349 (errcode_for_file_access(),
00350 errmsg("could not open directory \"%s\": %m", startdir)));
00351 return ret;
00352 }
00353
00354 void
00355 pg_tzenumerate_end(pg_tzenum *dir)
00356 {
00357 while (dir->depth >= 0)
00358 {
00359 FreeDir(dir->dirdesc[dir->depth]);
00360 pfree(dir->dirname[dir->depth]);
00361 dir->depth--;
00362 }
00363 pfree(dir);
00364 }
00365
00366 pg_tz *
00367 pg_tzenumerate_next(pg_tzenum *dir)
00368 {
00369 while (dir->depth >= 0)
00370 {
00371 struct dirent *direntry;
00372 char fullname[MAXPGPATH];
00373 struct stat statbuf;
00374
00375 direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
00376
00377 if (!direntry)
00378 {
00379
00380 FreeDir(dir->dirdesc[dir->depth]);
00381 pfree(dir->dirname[dir->depth]);
00382 dir->depth--;
00383 continue;
00384 }
00385
00386 if (direntry->d_name[0] == '.')
00387 continue;
00388
00389 snprintf(fullname, MAXPGPATH, "%s/%s",
00390 dir->dirname[dir->depth], direntry->d_name);
00391 if (stat(fullname, &statbuf) != 0)
00392 ereport(ERROR,
00393 (errcode_for_file_access(),
00394 errmsg("could not stat \"%s\": %m", fullname)));
00395
00396 if (S_ISDIR(statbuf.st_mode))
00397 {
00398
00399 if (dir->depth >= MAX_TZDIR_DEPTH - 1)
00400 ereport(ERROR,
00401 (errmsg_internal("timezone directory stack overflow")));
00402 dir->depth++;
00403 dir->dirname[dir->depth] = pstrdup(fullname);
00404 dir->dirdesc[dir->depth] = AllocateDir(fullname);
00405 if (!dir->dirdesc[dir->depth])
00406 ereport(ERROR,
00407 (errcode_for_file_access(),
00408 errmsg("could not open directory \"%s\": %m",
00409 fullname)));
00410
00411
00412 continue;
00413 }
00414
00415
00416
00417
00418
00419 if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state,
00420 TRUE) != 0)
00421 {
00422
00423 continue;
00424 }
00425
00426 if (!pg_tz_acceptable(&dir->tz))
00427 {
00428
00429 continue;
00430 }
00431
00432
00433 return &dir->tz;
00434 }
00435
00436
00437 return NULL;
00438 }