1 /*
   2  * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <stdlib.h>
  27 #include <stdio.h>
  28 #include <strings.h>
  29 #include <time.h>
  30 #include <limits.h>
  31 #include <errno.h>
  32 #include <stddef.h>
  33 #include <sys/stat.h>
  34 #include <sys/types.h>
  35 #include <string.h>
  36 #include <dirent.h>
  37 #include <unistd.h>
  38 #if defined(__solaris__)
  39 #include <libscf.h>
  40 #endif
  41 
  42 #include "jvm.h"
  43 #include "TimeZone_md.h"
  44 
  45 #define SKIP_SPACE(p)   while (*p == ' ' || *p == '\t') p++;
  46 
  47 #if defined(_ALLBSD_SOURCE)
  48 #define dirent64 dirent
  49 #define readdir64_r readdir_r
  50 #endif
  51 
  52 #if !defined(__solaris__) || defined(__sparcv9) || defined(amd64)
  53 #define fileopen        fopen
  54 #define filegets        fgets
  55 #define fileclose       fclose
  56 #endif
  57 
  58 #if defined(__linux__) || defined(_ALLBSD_SOURCE)
  59 static const char *ETC_TIMEZONE_FILE = "/etc/timezone";
  60 static const char *ZONEINFO_DIR = "/usr/share/zoneinfo";
  61 static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime";
  62 #else
  63 static const char *SYS_INIT_FILE = "/etc/default/init";
  64 static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo";
  65 static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";
  66 #endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */
  67 
  68 #if defined(_AIX)
  69 static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
  70 #endif
  71 
  72 #if defined(__linux__) || defined(MACOSX) || defined(__solaris__)
  73 
  74 /*
  75  * Returns a pointer to the zone ID portion of the given zoneinfo file
  76  * name, or NULL if the given string doesn't contain "zoneinfo/".
  77  */
  78 static char *
  79 getZoneName(char *str)
  80 {
  81     static const char *zidir = "zoneinfo/";
  82 
  83     char *pos = strstr((const char *)str, zidir);
  84     if (pos == NULL) {
  85         return NULL;
  86     }
  87     return pos + strlen(zidir);
  88 }
  89 
  90 /*
  91  * Returns a path name created from the given 'dir' and 'name' under
  92  * UNIX. This function allocates memory for the pathname calling
  93  * malloc(). NULL is returned if malloc() fails.
  94  */
  95 static char *
  96 getPathName(const char *dir, const char *name) {
  97     char *path;
  98 
  99     path = (char *) malloc(strlen(dir) + strlen(name) + 2);
 100     if (path == NULL) {
 101         return NULL;
 102     }
 103     return strcat(strcat(strcpy(path, dir), "/"), name);
 104 }
 105 
 106 /*
 107  * Scans the specified directory and its subdirectories to find a
 108  * zoneinfo file which has the same content as /etc/localtime on Linux
 109  * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'.
 110  * If file is symbolic link, then the contents it points to are in buf.
 111  * Returns a zone ID if found, otherwise, NULL is returned.
 112  */
 113 static char *
 114 findZoneinfoFile(char *buf, size_t size, const char *dir)
 115 {
 116     DIR *dirp = NULL;
 117     struct stat statbuf;
 118     struct dirent64 *dp = NULL;
 119     struct dirent64 *entry = NULL;
 120     char *pathname = NULL;
 121     int fd = -1;
 122     char *dbuf = NULL;
 123     char *tz = NULL;
 124 
 125     dirp = opendir(dir);
 126     if (dirp == NULL) {
 127         return NULL;
 128     }
 129 
 130     entry = (struct dirent64 *) malloc((size_t) pathconf(dir, _PC_NAME_MAX));
 131     if (entry == NULL) {
 132         (void) closedir(dirp);
 133         return NULL;
 134     }
 135 
 136     while (readdir64_r(dirp, entry, &dp) == 0 && dp != NULL) {
 137         /*
 138          * Skip '.' and '..' (and possibly other .* files)
 139          */
 140         if (dp->d_name[0] == '.') {
 141             continue;
 142         }
 143 
 144         /*
 145          * Skip "ROC", "posixrules", and "localtime".
 146          */
 147         if ((strcmp(dp->d_name, "ROC") == 0)
 148             || (strcmp(dp->d_name, "posixrules") == 0)
 149 #if defined(__solaris__)
 150             /*
 151              * Skip the "src" and "tab" directories on Solaris.
 152              */
 153             || (strcmp(dp->d_name, "src") == 0)
 154             || (strcmp(dp->d_name, "tab") == 0)
 155 #endif
 156             || (strcmp(dp->d_name, "localtime") == 0)) {
 157             continue;
 158         }
 159 
 160         pathname = getPathName(dir, dp->d_name);
 161         if (pathname == NULL) {
 162             break;
 163         }
 164         if (stat(pathname, &statbuf) == -1) {
 165             break;
 166         }
 167 
 168         if (S_ISDIR(statbuf.st_mode)) {
 169             tz = findZoneinfoFile(buf, size, pathname);
 170             if (tz != NULL) {
 171                 break;
 172             }
 173         } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
 174             dbuf = (char *) malloc(size);
 175             if (dbuf == NULL) {
 176                 break;
 177             }
 178             if ((fd = open(pathname, O_RDONLY)) == -1) {
 179                 break;
 180             }
 181             if (read(fd, dbuf, size) != (ssize_t) size) {
 182                 break;
 183             }
 184             if (memcmp(buf, dbuf, size) == 0) {
 185                 tz = getZoneName(pathname);
 186                 if (tz != NULL) {
 187                     tz = strdup(tz);
 188                 }
 189                 break;
 190             }
 191             free((void *) dbuf);
 192             dbuf = NULL;
 193             (void) close(fd);
 194             fd = -1;
 195         }
 196         free((void *) pathname);
 197         pathname = NULL;
 198     }
 199 
 200     if (entry != NULL) {
 201         free((void *) entry);
 202     }
 203     if (dirp != NULL) {
 204         (void) closedir(dirp);
 205     }
 206     if (pathname != NULL) {
 207         free((void *) pathname);
 208     }
 209     if (fd != -1) {
 210         (void) close(fd);
 211     }
 212     if (dbuf != NULL) {
 213         free((void *) dbuf);
 214     }
 215     return tz;
 216 }
 217 
 218 #if defined(__linux__) || defined(MACOSX)
 219 
 220 /*
 221  * Performs Linux specific mapping and returns a zone ID
 222  * if found. Otherwise, NULL is returned.
 223  */
 224 static char *
 225 getPlatformTimeZoneID()
 226 {
 227     struct stat statbuf;
 228     char *tz = NULL;
 229     FILE *fp;
 230     int fd;
 231     char *buf;
 232     size_t size;
 233 
 234 #if defined(__linux__)
 235     /*
 236      * Try reading the /etc/timezone file for Debian distros. There's
 237      * no spec of the file format available. This parsing assumes that
 238      * there's one line of an Olson tzid followed by a '\n', no
 239      * leading or trailing spaces, no comments.
 240      */
 241     if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) {
 242         char line[256];
 243 
 244         if (fgets(line, sizeof(line), fp) != NULL) {
 245             char *p = strchr(line, '\n');
 246             if (p != NULL) {
 247                 *p = '\0';
 248             }
 249             if (strlen(line) > 0) {
 250                 tz = strdup(line);
 251             }
 252         }
 253         (void) fclose(fp);
 254         if (tz != NULL) {
 255             return tz;
 256         }
 257     }
 258 #endif /* defined(__linux__) */
 259 
 260     /*
 261      * Next, try /etc/localtime to find the zone ID.
 262      */
 263     if (lstat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) {
 264         return NULL;
 265     }
 266 
 267     /*
 268      * If it's a symlink, get the link name and its zone ID part. (The
 269      * older versions of timeconfig created a symlink as described in
 270      * the Red Hat man page. It was changed in 1999 to create a copy
 271      * of a zoneinfo file. It's no longer possible to get the zone ID
 272      * from /etc/localtime.)
 273      */
 274     if (S_ISLNK(statbuf.st_mode)) {
 275         char linkbuf[PATH_MAX+1];
 276         int len;
 277 
 278         if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {
 279             jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
 280                         DEFAULT_ZONEINFO_FILE);
 281             return NULL;
 282         }
 283         linkbuf[len] = '\0';
 284         tz = getZoneName(linkbuf);
 285         if (tz != NULL) {
 286             tz = strdup(tz);
 287             return tz;
 288         }
 289     }
 290 
 291     /*
 292      * If it's a regular file, we need to find out the same zoneinfo file
 293      * that has been copied as /etc/localtime.
 294      * If initial symbolic link resolution failed, we should treat target
 295      * file as a regular file.
 296      */
 297     if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) {
 298         return NULL;
 299     }
 300     if (fstat(fd, &statbuf) == -1) {
 301         (void) close(fd);
 302         return NULL;
 303     }
 304     size = (size_t) statbuf.st_size;
 305     buf = (char *) malloc(size);
 306     if (buf == NULL) {
 307         (void) close(fd);
 308         return NULL;
 309     }
 310 
 311     if (read(fd, buf, size) != (ssize_t) size) {
 312         (void) close(fd);
 313         free((void *) buf);
 314         return NULL;
 315     }
 316     (void) close(fd);
 317 
 318     tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
 319     free((void *) buf);
 320     return tz;
 321 }
 322 
 323 #elif defined(__solaris__)
 324 
 325 #if !defined(__sparcv9) && !defined(amd64)
 326 
 327 /*
 328  * Those file* functions mimic the UNIX stream io functions. This is
 329  * because of the limitation of the number of open files on Solaris
 330  * (32-bit mode only) due to the System V ABI.
 331  */
 332 
 333 #define BUFFER_SIZE     4096
 334 
 335 static struct iobuffer {
 336     int     magic;      /* -1 to distinguish from the real FILE */
 337     int     fd;         /* file descriptor */
 338     char    *buffer;    /* pointer to buffer */
 339     char    *ptr;       /* current read pointer */
 340     char    *endptr;    /* end pointer */
 341 };
 342 
 343 static int
 344 fileclose(FILE *stream)
 345 {
 346     struct iobuffer *iop = (struct iobuffer *) stream;
 347 
 348     if (iop->magic != -1) {
 349         return fclose(stream);
 350     }
 351 
 352     if (iop == NULL) {
 353         return 0;
 354     }
 355     close(iop->fd);
 356     free((void *)iop->buffer);
 357     free((void *)iop);
 358     return 0;
 359 }
 360 
 361 static FILE *
 362 fileopen(const char *fname, const char *fmode)
 363 {
 364     FILE *fp;
 365     int fd;
 366     struct iobuffer *iop;
 367 
 368     if ((fp = fopen(fname, fmode)) != NULL) {
 369         return fp;
 370     }
 371 
 372     /*
 373      * It assumes read open.
 374      */
 375     if ((fd = open(fname, O_RDONLY)) == -1) {
 376         return NULL;
 377     }
 378 
 379     /*
 380      * Allocate struct iobuffer and its buffer
 381      */
 382     iop = malloc(sizeof(struct iobuffer));
 383     if (iop == NULL) {
 384         (void) close(fd);
 385         errno = ENOMEM;
 386         return NULL;
 387     }
 388     iop->magic = -1;
 389     iop->fd = fd;
 390     iop->buffer = malloc(BUFFER_SIZE);
 391     if (iop->buffer == NULL) {
 392         (void) close(fd);
 393         free((void *) iop);
 394         errno = ENOMEM;
 395         return NULL;
 396     }
 397     iop->ptr = iop->buffer;
 398     iop->endptr = iop->buffer;
 399     return (FILE *)iop;
 400 }
 401 
 402 /*
 403  * This implementation assumes that n is large enough and the line
 404  * separator is '\n'.
 405  */
 406 static char *
 407 filegets(char *s, int n, FILE *stream)
 408 {
 409     struct iobuffer *iop = (struct iobuffer *) stream;
 410     char *p;
 411 
 412     if (iop->magic != -1) {
 413         return fgets(s, n, stream);
 414     }
 415 
 416     p = s;
 417     for (;;) {
 418         char c;
 419 
 420         if (iop->ptr == iop->endptr) {
 421             ssize_t len;
 422 
 423             if ((len = read(iop->fd, (void *)iop->buffer, BUFFER_SIZE)) == -1) {
 424                 return NULL;
 425             }
 426             if (len == 0) {
 427                 *p = 0;
 428                 if (s == p) {
 429                     return NULL;
 430                 }
 431                 return s;
 432             }
 433             iop->ptr = iop->buffer;
 434             iop->endptr = iop->buffer + len;
 435         }
 436         c = *iop->ptr++;
 437         *p++ = c;
 438         if ((p - s) == (n - 1)) {
 439             *p = 0;
 440             return s;
 441         }
 442         if (c == '\n') {
 443             *p = 0;
 444             return s;
 445         }
 446     }
 447     /*NOTREACHED*/
 448 }
 449 #endif /* !defined(__sparcv9) && !defined(amd64) */
 450 
 451 /*
 452  * Performs Solaris dependent mapping. Returns a zone ID if
 453  * found. Otherwise, NULL is returned.  Solaris libc looks up
 454  * "/etc/default/init" to get the default TZ value if TZ is not defined
 455  * as an environment variable.
 456  */
 457 static char *
 458 getPlatformTimeZoneID()
 459 {
 460     char *tz = NULL;
 461     FILE *fp;
 462 
 463     /*
 464      * Try the TZ entry in /etc/default/init.
 465      */
 466     if ((fp = fileopen(SYS_INIT_FILE, "r")) != NULL) {
 467         char line[256];
 468         char quote = '\0';
 469 
 470         while (filegets(line, sizeof(line), fp) != NULL) {
 471             char *p = line;
 472             char *s;
 473             char c;
 474 
 475             /* quick check for comment lines */
 476             if (*p == '#') {
 477                 continue;
 478             }
 479             if (strncmp(p, "TZ=", 3) == 0) {
 480                 p += 3;
 481                 SKIP_SPACE(p);
 482                 c = *p;
 483                 if (c == '"' || c == '\'') {
 484                     quote = c;
 485                     p++;
 486                 }
 487 
 488                 /*
 489                  * PSARC/2001/383: quoted string support
 490                  */
 491                 for (s = p; (c = *s) != '\0' && c != '\n'; s++) {
 492                     /* No '\\' is supported here. */
 493                     if (c == quote) {
 494                         quote = '\0';
 495                         break;
 496                     }
 497                     if (c == ' ' && quote == '\0') {
 498                         break;
 499                     }
 500                 }
 501                 if (quote != '\0') {
 502                     jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n");
 503                 }
 504                 *s = '\0';
 505                 tz = strdup(p);
 506                 break;
 507             }
 508         }
 509         (void) fileclose(fp);
 510     }
 511     return tz;
 512 }
 513 
 514 #define TIMEZONE_FMRI   "svc:/system/timezone:default"
 515 #define TIMEZONE_PG     "timezone"
 516 #define LOCALTIME_PROP  "localtime"
 517 
 518 static void
 519 cleanupScf(scf_handle_t *h,
 520            scf_snapshot_t *snap,
 521            scf_instance_t *inst,
 522            scf_propertygroup_t *pg,
 523            scf_property_t *prop,
 524            scf_value_t *val,
 525            char *buf) {
 526     if (buf != NULL) {
 527         free(buf);
 528     }
 529     if (snap != NULL) {
 530         scf_snapshot_destroy(snap);
 531     }
 532     if (val != NULL) {
 533         scf_value_destroy(val);
 534     }
 535     if (prop != NULL) {
 536         scf_property_destroy(prop);
 537     }
 538     if (pg != NULL) {
 539         scf_pg_destroy(pg);
 540     }
 541     if (inst != NULL) {
 542         scf_instance_destroy(inst);
 543     }
 544     if (h != NULL) {
 545         scf_handle_destroy(h);
 546     }
 547 }
 548 
 549 /*
 550  * Returns a zone ID of Solaris when the TZ value is "localtime".
 551  * First, it tries scf. If scf fails, it looks for the same file as
 552  * /usr/share/lib/zoneinfo/localtime under /usr/share/lib/zoneinfo/.
 553  */
 554 static char *
 555 getSolarisDefaultZoneID() {
 556     char *tz = NULL;
 557     struct stat statbuf;
 558     size_t size;
 559     char *buf;
 560     int fd;
 561     /* scf specific variables */
 562     scf_handle_t *h = NULL;
 563     scf_snapshot_t *snap = NULL;
 564     scf_instance_t *inst = NULL;
 565     scf_propertygroup_t *pg = NULL;
 566     scf_property_t *prop = NULL;
 567     scf_value_t *val = NULL;
 568 
 569     if ((h = scf_handle_create(SCF_VERSION)) != NULL
 570         && scf_handle_bind(h) == 0
 571         && (inst = scf_instance_create(h)) != NULL
 572         && (snap = scf_snapshot_create(h)) != NULL
 573         && (pg = scf_pg_create(h)) != NULL
 574         && (prop = scf_property_create(h)) != NULL
 575         && (val = scf_value_create(h)) != NULL
 576         && scf_handle_decode_fmri(h, TIMEZONE_FMRI, NULL, NULL, inst,
 577                                   NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) == 0
 578         && scf_instance_get_snapshot(inst, "running", snap) == 0
 579         && scf_instance_get_pg_composed(inst, snap, TIMEZONE_PG, pg) == 0
 580         && scf_pg_get_property(pg, LOCALTIME_PROP, prop) == 0
 581         && scf_property_get_value(prop, val) == 0) {
 582         ssize_t len;
 583 
 584         /* Gets the length of the zone ID string */
 585         len = scf_value_get_astring(val, NULL, 0);
 586         if (len != -1) {
 587             tz = malloc(++len); /* +1 for a null byte */
 588             if (tz != NULL && scf_value_get_astring(val, tz, len) != -1) {
 589                 cleanupScf(h, snap, inst, pg, prop, val, NULL);
 590                 return tz;
 591             }
 592         }
 593     }
 594     cleanupScf(h, snap, inst, pg, prop, val, tz);
 595 
 596     if (stat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) {
 597         return NULL;
 598     }
 599     size = (size_t) statbuf.st_size;
 600     buf = malloc(size);
 601     if (buf == NULL) {
 602         return NULL;
 603     }
 604     if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) {
 605         free((void *) buf);
 606         return NULL;
 607     }
 608 
 609     if (read(fd, buf, size) != (ssize_t) size) {
 610         (void) close(fd);
 611         free((void *) buf);
 612         return NULL;
 613     }
 614     (void) close(fd);
 615     tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
 616     free((void *) buf);
 617     return tz;
 618 }
 619 
 620 #endif /* defined(__solaris__) */
 621 
 622 #elif defined(_AIX)
 623 
 624 static char *
 625 getPlatformTimeZoneID()
 626 {
 627     FILE *fp;
 628     char *tz = NULL;
 629     char *tz_key = "TZ=";
 630     char line[256];
 631     size_t tz_key_len = strlen(tz_key);
 632 
 633     if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) {
 634         while (fgets(line, sizeof(line), fp) != NULL) {
 635             char *p = strchr(line, '\n');
 636             if (p != NULL) {
 637                 *p = '\0';
 638             }
 639             if (0 == strncmp(line, tz_key, tz_key_len)) {
 640                 tz = strdup(line + tz_key_len);
 641                 break;
 642             }
 643         }
 644         (void) fclose(fp);
 645     }
 646 
 647     return tz;
 648 }
 649 
 650 static char *
 651 mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
 652     FILE *tzmapf;
 653     char mapfilename[PATH_MAX + 1];
 654     char line[256];
 655     int linecount = 0;
 656     char *tz_buf = NULL;
 657     char *temp_tz = NULL;
 658     char *javatz = NULL;
 659     size_t tz_len = 0;
 660 
 661     /* On AIX, the TZ environment variable may end with a comma
 662      * followed by modifier fields. These are ignored here. */
 663     temp_tz = strchr(tz, ',');
 664     tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz;
 665     tz_buf = (char *)malloc(tz_len + 1);
 666     memcpy(tz_buf, tz, tz_len);
 667     tz_buf[tz_len] = 0;
 668 
 669     /* Open tzmappings file, with buffer overrun check */
 670     if ((strlen(java_home_dir) + 15) > PATH_MAX) {
 671         jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
 672         goto tzerr;
 673     }
 674     strcpy(mapfilename, java_home_dir);
 675     strcat(mapfilename, "/lib/tzmappings");
 676     if ((tzmapf = fopen(mapfilename, "r")) == NULL) {
 677         jio_fprintf(stderr, "can't open %s\n", mapfilename);
 678         goto tzerr;
 679     }
 680 
 681     while (fgets(line, sizeof(line), tzmapf) != NULL) {
 682         char *p = line;
 683         char *sol = line;
 684         char *java;
 685         int result;
 686 
 687         linecount++;
 688         /*
 689          * Skip comments and blank lines
 690          */
 691         if (*p == '#' || *p == '\n') {
 692             continue;
 693         }
 694 
 695         /*
 696          * Get the first field, platform zone ID
 697          */
 698         while (*p != '\0' && *p != '\t') {
 699             p++;
 700         }
 701         if (*p == '\0') {
 702             /* mapping table is broken! */
 703             jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount);
 704             break;
 705         }
 706 
 707         *p++ = '\0';
 708         if ((result = strncmp(tz_buf, sol, tz_len)) == 0) {
 709             /*
 710              * If this is the current platform zone ID,
 711              * take the Java time zone ID (2nd field).
 712              */
 713             java = p;
 714             while (*p != '\0' && *p != '\n') {
 715                 p++;
 716             }
 717 
 718             if (*p == '\0') {
 719                 /* mapping table is broken! */
 720                 jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount);
 721                 break;
 722             }
 723 
 724             *p = '\0';
 725             javatz = strdup(java);
 726             break;
 727         } else if (result < 0) {
 728             break;
 729         }
 730     }
 731     (void) fclose(tzmapf);
 732 
 733 tzerr:
 734     if (tz_buf != NULL ) {
 735         free((void *) tz_buf);
 736     }
 737 
 738     if (javatz == NULL) {
 739         return getGMTOffsetID();
 740     }
 741 
 742     return javatz;
 743 }
 744 
 745 #endif /* defined(_AIX) */
 746 
 747 /*
 748  * findJavaTZ_md() maps platform time zone ID to Java time zone ID
 749  * using <java_home>/lib/tzmappings. If the TZ value is not found, it
 750  * trys some libc implementation dependent mappings. If it still
 751  * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
 752  * form.
 753  */
 754 /*ARGSUSED1*/
 755 char *
 756 findJavaTZ_md(const char *java_home_dir)
 757 {
 758     char *tz;
 759     char *javatz = NULL;
 760     char *freetz = NULL;
 761 
 762     tz = getenv("TZ");
 763 
 764     if (tz == NULL || *tz == '\0') {
 765         tz = getPlatformTimeZoneID();
 766         freetz = tz;
 767     }
 768 
 769     if (tz != NULL) {
 770         /* Ignore preceding ':' */
 771         if (*tz == ':') {
 772             tz++;
 773         }
 774 #if defined(__linux__)
 775         /* Ignore "posix/" prefix on Linux. */
 776         if (strncmp(tz, "posix/", 6) == 0) {
 777             tz += 6;
 778         }
 779 #endif
 780 
 781 #if defined(_AIX)
 782         /* On AIX do the platform to Java mapping. */
 783         javatz = mapPlatformToJavaTimezone(java_home_dir, tz);
 784         if (freetz != NULL) {
 785             free((void *) freetz);
 786         }
 787 #else
 788 #if defined(__solaris__)
 789         /* Solaris might use localtime, so handle it here. */
 790         if (strcmp(tz, "localtime") == 0) {
 791             javatz = getSolarisDefaultZoneID();
 792             if (freetz != NULL) {
 793                 free((void *) freetz);
 794             }
 795         } else
 796 #endif
 797         if (freetz == NULL) {
 798             /* strdup if we are still working on getenv result. */
 799             javatz = strdup(tz);
 800         } else if (freetz != tz) {
 801             /* strdup and free the old buffer, if we moved the pointer. */
 802             javatz = strdup(tz);
 803             free((void *) freetz);
 804         } else {
 805             /* we are good if we already work on a freshly allocated buffer. */
 806             javatz = tz;
 807         }
 808 #endif
 809     }
 810 
 811     return javatz;
 812 }
 813 
 814 /**
 815  * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00")
 816  */
 817 
 818 #if defined(MACOSX)
 819 
 820 char *
 821 getGMTOffsetID()
 822 {
 823     time_t offset;
 824     char sign, buf[32];
 825     struct tm *local_tm;
 826     time_t clock;
 827     time_t currenttime;
 828 
 829     clock = time(NULL);
 830     tzset();
 831     local_tm = localtime(&clock);
 832     if (local_tm->tm_gmtoff >= 0) {
 833         offset = (time_t) local_tm->tm_gmtoff;
 834         sign = '+';
 835     } else {
 836         offset = (time_t) -local_tm->tm_gmtoff;
 837         sign = '-';
 838     }
 839     sprintf(buf, (const char *)"GMT%c%02d:%02d",
 840             sign, (int)(offset/3600), (int)((offset%3600)/60));
 841     return strdup(buf);
 842 }
 843 
 844 #else
 845 
 846 char *
 847 getGMTOffsetID()
 848 {
 849     time_t offset;
 850     char sign, buf[32];
 851 #if defined(__solaris__)
 852     struct tm localtm;
 853     time_t currenttime;
 854 
 855     currenttime = time(NULL);
 856     if (localtime_r(&currenttime, &localtm) == NULL) {
 857         return NULL;
 858     }
 859 
 860     offset = localtm.tm_isdst ? altzone : timezone;
 861 #else
 862     offset = timezone;
 863 #endif
 864 
 865     if (offset == 0) {
 866         return strdup("GMT");
 867     }
 868 
 869     /* Note that the time offset direction is opposite. */
 870     if (offset > 0) {
 871         sign = '-';
 872     } else {
 873         offset = -offset;
 874         sign = '+';
 875     }
 876     sprintf(buf, (const char *)"GMT%c%02d:%02d",
 877             sign, (int)(offset/3600), (int)((offset%3600)/60));
 878     return strdup(buf);
 879 }
 880 #endif /* MACOSX */