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