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