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