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