summaryrefslogtreecommitdiff
path: root/src/libpcp/src/tz.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp/src/tz.c')
-rw-r--r--src/libpcp/src/tz.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/libpcp/src/tz.c b/src/libpcp/src/tz.c
new file mode 100644
index 0000000..1157ad6
--- /dev/null
+++ b/src/libpcp/src/tz.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 1995-2003,2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * Thread-safe notes
+ *
+ * These routines manipulate the environment and call lots of routines
+ * like localtime(), asctime(), gmtime(), getenv(), putenv() ... all of
+ * which are not thread-safe.
+ *
+ * We use the big lock to prevent concurrent execution.
+ *
+ * Need to call PM_INIT_LOCKS() in all the exposed routines because we
+ * may be called before a context has been created, and missed the
+ * lock initialization in pmNewContext().
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+
+static char *envtz; /* buffer in env */
+static int envtzlen;
+
+static char *savetz; /* real $TZ from env */
+static char **savetzp;
+
+static int nzone; /* table of zones */
+static int curzone = -1;
+static char **zone;
+
+#if !defined(HAVE_UNDERBAR_ENVIRON)
+#define _environ environ
+#endif
+
+extern char **_environ;
+
+static void
+_pushTZ(void)
+{
+ char **p;
+
+ savetzp = NULL;
+ for (p = _environ; *p != NULL; p++) {
+ if (strncmp(*p, "TZ=", 3) == 0) {
+ savetz = *p;
+ *p = envtz;
+ savetzp = p;
+ break;
+ }
+ }
+ if (*p == NULL)
+ putenv(envtz);
+ tzset();
+}
+
+static void
+_popTZ(void)
+{
+ if (savetzp != NULL)
+ *savetzp = savetz;
+ else
+ putenv("TZ=");
+ tzset();
+}
+
+/*
+ * Construct TZ=... subject to the constraint that the length of the
+ * timezone part is not more than PM_TZ_MAXLEN bytes
+ * Assumes TZ= is in the start of tzbuffer and this is not touched.
+ * And finally set TZ in the environment.
+ */
+static void
+__pmSquashTZ(char *tzbuffer)
+{
+ time_t now = time(NULL);
+ struct tm *t;
+ char *tzn;
+#ifndef IS_MINGW
+ time_t offset;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ tzset();
+ t = localtime(&now);
+
+#ifdef HAVE_ALTZONE
+ offset = (t->tm_isdst > 0) ? altzone : timezone;
+#elif defined HAVE_STRFTIME_z
+ {
+ char tzoffset[6]; /* +1200\0 */
+
+ strftime (tzoffset, sizeof (tzoffset), "%z", t);
+ offset = -strtol (tzoffset, NULL, 10);
+ offset = ((offset/100) * 3600) + ((offset%100) * 60);
+ }
+#else
+ {
+ struct tm *gmt = gmtime(&now);
+ offset = (gmt->tm_hour - t->tm_hour) * 3600 +
+ (gmt->tm_min - t->tm_min) * 60;
+ }
+#endif
+
+ tzn = tzname[(t->tm_isdst > 0)];
+
+ if (offset != 0) {
+ int hours = offset / 3600;
+ int mins = abs ((offset % 3600) / 60);
+ int len = (int) strlen(tzn);
+
+ if (mins == 0) {
+ /* -3 for +HH in worst case */
+ if (len > PM_TZ_MAXLEN-3) len = PM_TZ_MAXLEN-3;
+ snprintf(tzbuffer+3, PM_TZ_MAXLEN, "%*.*s%+d", len, len, tzn, hours);
+ }
+ else {
+ /* -6 for +HH:MM in worst case */
+ if (len > PM_TZ_MAXLEN-6) len = PM_TZ_MAXLEN-6;
+ snprintf(tzbuffer+3, PM_TZ_MAXLEN, "%*.*s%+d:%02d", len, len, tzn, hours, mins);
+ }
+ }
+ else {
+ strncpy(tzbuffer+3, tzn, PM_TZ_MAXLEN);
+ tzbuffer[PM_TZ_MAXLEN+4-1] = '\0';
+ }
+ putenv(tzbuffer);
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return;
+
+#else /* IS_MINGW */
+ /*
+ * Use the native Win32 API to extract the timezone. This is
+ * a Windows timezone, we want the POSIX style but there's no
+ * API, really. What we've found works, is the same approach
+ * the MSYS dll takes - we set TZ their way (below) and then
+ * use tzset, then extract. Note that the %Z and %z strftime
+ * parameters do not contain abbreviated names/offsets (they
+ * both contain Windows timezone, and both are the same with
+ * no TZ). Note also that putting the Windows name into the
+ * environment as TZ does not do anything good (see the tzset
+ * MSDN docs).
+ */
+#define is_upper(c) ((unsigned)(c) - 'A' <= 26)
+
+ TIME_ZONE_INFORMATION tz;
+ static const char wildabbr[] = "GMT";
+ char tzbuf[256], tzoff[64];
+ char *cp, *dst, *off;
+ wchar_t *src;
+ div_t d;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ GetTimeZoneInformation(&tz);
+ dst = cp = tzbuf;
+ off = tzoff;
+ for (src = tz.StandardName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if (cp == dst) {
+ /* In Asian Windows, tz.StandardName may not contain
+ the timezone name. */
+ strcpy(cp, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.StandardBias, 60);
+ sprintf(cp, "%d", d.quot);
+ sprintf(off, "%d", d.quot);
+ if (d.rem) {
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ sprintf(off=strchr(off, 0), ":%d", abs(d.rem));
+ }
+ if (tz.StandardDate.wMonth) {
+ cp = strchr(cp, 0);
+ dst = cp;
+ for (src = tz.DaylightName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if (cp == dst) {
+ /* In Asian Windows, tz.StandardName may not contain
+ the daylight name. */
+ strcpy(tzbuf, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.DaylightBias, 60);
+ sprintf(cp, "%d", d.quot);
+ if (d.rem)
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.DaylightDate.wMonth,
+ tz.DaylightDate.wDay,
+ tz.DaylightDate.wDayOfWeek,
+ tz.DaylightDate.wHour);
+ if (tz.DaylightDate.wMinute || tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wMinute);
+ if (tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wSecond);
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.StandardDate.wMonth,
+ tz.StandardDate.wDay,
+ tz.StandardDate.wDayOfWeek,
+ tz.StandardDate.wHour);
+ if (tz.StandardDate.wMinute || tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wMinute);
+ if (tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wSecond);
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_TIMECONTROL)
+ fprintf(stderr, "Win32 TZ=%s\n", tzbuf);
+#endif
+
+ snprintf(tzbuffer+3, PM_TZ_MAXLEN, "%s", tzbuf);
+ putenv(tzbuffer);
+
+ tzset();
+ t = localtime(&now);
+ tzn = tzname[(t->tm_isdst > 0)];
+
+ snprintf(tzbuffer+3, PM_TZ_MAXLEN, "%s%s", tzn, tzoff);
+ putenv(tzbuffer);
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return;
+#endif
+}
+
+/*
+ * __pmTimezone: work out local timezone
+ */
+char *
+__pmTimezone(void)
+{
+ static char *tzbuffer = NULL;
+ char *tz;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ tz = getenv("TZ");
+
+ if (tzbuffer == NULL) {
+ /*
+ * size is PM_TZ_MAXLEN + length of "TZ=" + null byte terminator
+ */
+ tzbuffer = (char *)malloc(PM_TZ_MAXLEN+4);
+ if (tzbuffer == NULL) {
+ /* not much we can do here ... */
+ PM_UNLOCK(__pmLock_libpcp);
+ return NULL;
+ }
+ strcpy(tzbuffer, "TZ=");
+ }
+
+ if (tz == NULL || tz[0] == ':') {
+ /* NO TZ in the environment - invent one. If TZ starts with a colon,
+ * it's an Olson-style TZ and it does not supported on all IRIXes, so
+ * squash it into a simple one (pv#788431). */
+ __pmSquashTZ(tzbuffer);
+ tz = &tzbuffer[3];
+ } else if (strlen(tz) > PM_TZ_MAXLEN) {
+ /* TZ is too long to fit into the internal PCP timezone structs
+ * let's try to sqash it a bit */
+ char *tb;
+
+ if ((tb = strdup(tz)) == NULL) {
+ /* sorry state of affairs, go squash w/out copying buffer */
+ __pmSquashTZ(tzbuffer);
+ tz = &tzbuffer[3];
+ }
+ else {
+ char *ptz = tz;
+ char *zeros;
+ char *end = tb;
+
+ while ((zeros = strstr(ptz, ":00")) != NULL) {
+ strncpy(end, ptz, zeros-ptz);
+ end += zeros-ptz;
+ *end = '\0';
+ ptz = zeros+3;
+ }
+
+ if (strlen(tb) > PM_TZ_MAXLEN) {
+ /* Still too long - let's pretend it's Olson */
+ __pmSquashTZ(tzbuffer);
+ tz = &tzbuffer[3];
+ } else {
+ strcpy(tzbuffer+3, tb);
+ putenv(tzbuffer);
+ tz = tzbuffer+3;
+ }
+
+ free(tb);
+ }
+ }
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return tz;
+}
+
+/*
+ * buffer should be at least PM_TZ_MAXLEN bytes long
+ */
+char *
+__pmTimezone_r(char *buf, int buflen)
+{
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ strcpy(buf, __pmTimezone());
+ PM_UNLOCK(__pmLock_libpcp);
+ return buf;
+}
+
+int
+pmUseZone(const int tz_handle)
+{
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ if (tz_handle < 0 || tz_handle >= nzone) {
+ PM_UNLOCK(__pmLock_libpcp);
+ return -1;
+ }
+
+ curzone = tz_handle;
+ strcpy(&envtz[3], zone[curzone]);
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT)
+ fprintf(stderr, "pmUseZone(%d) tz=%s\n", curzone, zone[curzone]);
+#endif
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return 0;
+}
+
+int
+pmNewZone(const char *tz)
+{
+ int len;
+ int hack = 0;
+ int sts;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ len = (int)strlen(tz);
+ if (len == 3) {
+ /*
+ * things like TZ=GMT may be broken in libc, particularly
+ * in _ltzset() of time_comm.c, where changes to TZ are
+ * sometimes not properly reflected.
+ * TZ=GMT+0 avoids the problem.
+ */
+ len += 2;
+ hack = 1;
+ }
+
+ if (len+4 > envtzlen) {
+ /* expand buffer for env */
+ if (envtz != NULL)
+ free(envtz);
+ envtzlen = len+4;
+ envtz = (char *)malloc(envtzlen);
+ strcpy(envtz, "TZ=");
+ }
+ strcpy(&envtz[3], tz);
+ if (hack)
+ /* see above */
+ strcpy(&envtz[6], "+0");
+
+ curzone = nzone++;
+ zone = (char **)realloc(zone, nzone * sizeof(char *));
+ if (zone == NULL) {
+ __pmNoMem("pmNewZone", nzone * sizeof(char *), PM_FATAL_ERR);
+ }
+ zone[curzone] = strdup(&envtz[3]);
+
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_CONTEXT)
+ fprintf(stderr, "pmNewZone(%s) -> %d\n", zone[curzone], curzone);
+#endif
+ sts = curzone;
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return sts;
+}
+
+int
+pmNewContextZone(void)
+{
+ __pmContext *ctxp;
+ int sts;
+
+ if ((ctxp = __pmHandleToPtr(pmWhichContext())) == NULL)
+ return PM_ERR_NOCONTEXT;
+
+ if (ctxp->c_type == PM_CONTEXT_ARCHIVE) {
+ sts = pmNewZone(ctxp->c_archctl->ac_log->l_label.ill_tz);
+ PM_UNLOCK(ctxp->c_lock);
+ }
+ else if (ctxp->c_type == PM_CONTEXT_LOCAL) {
+ char tzbuf[PM_TZ_MAXLEN];
+ /* from env, not PMCD */
+ PM_UNLOCK(ctxp->c_lock);
+ __pmTimezone_r(tzbuf, sizeof(tzbuf));
+ sts = pmNewZone(tzbuf);
+ }
+ else {
+ /* assume PM_CONTEXT_HOST */
+ char *name = "pmcd.timezone";
+ pmID pmid;
+ pmResult *rp;
+
+ PM_UNLOCK(ctxp->c_lock);
+ if ((sts = pmLookupName(1, &name, &pmid)) < 0)
+ return sts;
+ if ((sts = pmFetch(1, &pmid, &rp)) >= 0) {
+ if (rp->vset[0]->numval == 1 &&
+ (rp->vset[0]->valfmt == PM_VAL_DPTR || rp->vset[0]->valfmt == PM_VAL_SPTR))
+ sts = pmNewZone((char *)rp->vset[0]->vlist[0].value.pval->vbuf);
+ else
+ sts = PM_ERR_VALUE;
+ pmFreeResult(rp);
+ }
+ }
+
+ return sts;
+}
+
+char *
+pmCtime(const time_t *clock, char *buf)
+{
+#if !defined(IS_SOLARIS) && !defined(IS_MINGW)
+ struct tm tbuf;
+#endif
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ if (curzone >= 0) {
+ _pushTZ();
+#if defined(IS_SOLARIS) || defined(IS_MINGW)
+ strcpy(buf, asctime(localtime(clock)));
+#else
+ asctime_r(localtime_r(clock, &tbuf), buf);
+#endif
+ _popTZ();
+ }
+ else {
+#if defined(IS_SOLARIS) || defined(IS_MINGW)
+ strcpy(buf, asctime(localtime(clock)));
+#else
+ asctime_r(localtime_r(clock, &tbuf), buf);
+#endif
+ }
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return buf;
+}
+
+struct tm *
+pmLocaltime(const time_t *clock, struct tm *result)
+{
+#if defined(IS_SOLARIS) || defined(IS_MINGW)
+ struct tm *tmp;
+#endif
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ if (curzone >= 0) {
+ _pushTZ();
+#if defined(IS_SOLARIS) || defined(IS_MINGW)
+ tmp = localtime(clock);
+ memcpy(result, tmp, sizeof(*result));
+#else
+ localtime_r(clock, result);
+#endif
+ _popTZ();
+ }
+ else {
+#if defined(IS_SOLARIS) || defined(IS_MINGW)
+ tmp = localtime(clock);
+ memcpy(result, tmp, sizeof(*result));
+#else
+ localtime_r(clock, result);
+#endif
+ }
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return result;
+}
+
+time_t
+__pmMktime(struct tm *timeptr)
+{
+ time_t ans;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+
+ if (curzone >= 0) {
+ _pushTZ();
+ ans = mktime(timeptr);
+ _popTZ();
+ }
+ else
+ ans = mktime(timeptr);
+
+ PM_UNLOCK(__pmLock_libpcp);
+ return ans;
+}
+
+int
+pmWhichZone(char **tz)
+{
+ int sts;
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (curzone >= 0)
+ *tz = zone[curzone];
+ sts = curzone;
+ PM_UNLOCK(__pmLock_libpcp);
+ return sts;
+}