Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/java/util/TimeZone_md.c
32287 views
/*1* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <stdlib.h>26#include <stdio.h>27#include <strings.h>28#include <time.h>29#include <limits.h>30#include <errno.h>31#include <stddef.h>32#include <sys/stat.h>33#include <sys/types.h>34#include <string.h>35#include <dirent.h>36#include <unistd.h>37#if defined(__solaris__)38#include <libscf.h>39#endif4041#include "jvm.h"42#include "TimeZone_md.h"4344static char *isFileIdentical(char* buf, size_t size, char *pathname);4546#define SKIP_SPACE(p) while (*p == ' ' || *p == '\t') p++;4748#if defined(_ALLBSD_SOURCE)49#define dirent64 dirent50#define readdir64_r readdir_r51#endif5253#if !defined(__solaris__) || defined(__sparcv9) || defined(amd64)54#define fileopen fopen55#define filegets fgets56#define fileclose fclose57#endif5859#if defined(__linux__) || defined(_ALLBSD_SOURCE)60static const char *ETC_TIMEZONE_FILE = "/etc/timezone";61static const char *ZONEINFO_DIR = "/usr/share/zoneinfo";62static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime";63#else64static const char *SYS_INIT_FILE = "/etc/default/init";65static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo";66static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";67#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */6869static const char popularZones[][4] = {"UTC", "GMT"};7071#if defined(_AIX)72static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";73#endif7475#if defined(__linux__) || defined(MACOSX) || defined(__solaris__)7677/*78* Returns a pointer to the zone ID portion of the given zoneinfo file79* name, or NULL if the given string doesn't contain "zoneinfo/".80*/81static char *82getZoneName(char *str)83{84static const char *zidir = "zoneinfo/";8586char *pos = strstr((const char *)str, zidir);87if (pos == NULL) {88return NULL;89}90return pos + strlen(zidir);91}9293/*94* Returns a path name created from the given 'dir' and 'name' under95* UNIX. This function allocates memory for the pathname calling96* malloc(). NULL is returned if malloc() fails.97*/98static char *99getPathName(const char *dir, const char *name) {100char *path;101102path = (char *) malloc(strlen(dir) + strlen(name) + 2);103if (path == NULL) {104return NULL;105}106return strcat(strcat(strcpy(path, dir), "/"), name);107}108109/*110* Scans the specified directory and its subdirectories to find a111* zoneinfo file which has the same content as /etc/localtime on Linux112* or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'.113* If file is symbolic link, then the contents it points to are in buf.114* Returns a zone ID if found, otherwise, NULL is returned.115*/116static char *117findZoneinfoFile(char *buf, size_t size, const char *dir)118{119DIR *dirp = NULL;120struct dirent64 *dp = NULL;121struct dirent64 *entry = NULL;122char *pathname = NULL;123char *tz = NULL;124long name_max = 0;125126if (strcmp(dir, ZONEINFO_DIR) == 0) {127/* fast path for 1st iteration */128unsigned int i;129for (i = 0; i < sizeof (popularZones) / sizeof (popularZones[0]); i++) {130pathname = getPathName(dir, popularZones[i]);131if (pathname == NULL) {132continue;133}134tz = isFileIdentical(buf, size, pathname);135free((void *) pathname);136pathname = NULL;137if (tz != NULL) {138return tz;139}140}141}142143dirp = opendir(dir);144if (dirp == NULL) {145return NULL;146}147148name_max = pathconf(dir, _PC_NAME_MAX);149// If pathconf did not work, fall back to a mimimum buffer size.150if (name_max < 1024) {151name_max = 1024;152}153154entry = (struct dirent64 *)malloc(offsetof(struct dirent64, d_name) + name_max + 1);155if (entry == NULL) {156(void) closedir(dirp);157return NULL;158}159160while (readdir64_r(dirp, entry, &dp) == 0 && dp != NULL) {161/*162* Skip '.' and '..' (and possibly other .* files)163*/164if (dp->d_name[0] == '.') {165continue;166}167168/*169* Skip "ROC", "posixrules", and "localtime".170*/171if ((strcmp(dp->d_name, "ROC") == 0)172|| (strcmp(dp->d_name, "posixrules") == 0)173#if defined(__solaris__)174/*175* Skip the "src" and "tab" directories on Solaris.176*/177|| (strcmp(dp->d_name, "src") == 0)178|| (strcmp(dp->d_name, "tab") == 0)179#endif180|| (strcmp(dp->d_name, "localtime") == 0)) {181continue;182}183184pathname = getPathName(dir, dp->d_name);185if (pathname == NULL) {186break;187}188189tz = isFileIdentical(buf, size, pathname);190191free((void *) pathname);192pathname = NULL;193if (tz != NULL) {194break;195}196}197198if (entry != NULL) {199free((void *) entry);200}201if (dirp != NULL) {202(void) closedir(dirp);203}204return tz;205}206207/*208* Checks if the file pointed to by pathname matches209* the data contents in buf.210* Returns a representation of the timezone file name211* if file match is found, otherwise NULL.212*/213static char *214isFileIdentical(char *buf, size_t size, char *pathname)215{216char *possibleMatch = NULL;217struct stat statbuf;218char *dbuf = NULL;219int fd = -1;220int res;221222if (stat(pathname, &statbuf) == -1) {223return NULL;224}225226if (S_ISDIR(statbuf.st_mode)) {227possibleMatch = findZoneinfoFile(buf, size, pathname);228} else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {229dbuf = (char *) malloc(size);230if (dbuf == NULL) {231return NULL;232}233if ((fd = open(pathname, O_RDONLY)) == -1) {234goto freedata;235}236if (read(fd, dbuf, size) != (ssize_t) size) {237goto freedata;238}239if (memcmp(buf, dbuf, size) == 0) {240possibleMatch = getZoneName(pathname);241if (possibleMatch != NULL) {242possibleMatch = strdup(possibleMatch);243}244}245freedata:246free((void *) dbuf);247dbuf = NULL;248(void) close(fd);249}250return possibleMatch;251}252253#if defined(__linux__) || defined(MACOSX)254255/*256* Performs Linux specific mapping and returns a zone ID257* if found. Otherwise, NULL is returned.258*/259static char *260getPlatformTimeZoneID()261{262struct stat statbuf;263char *tz = NULL;264FILE *fp;265int fd;266char *buf;267size_t size;268269#if defined(__linux__)270/*271* Try reading the /etc/timezone file for Debian distros. There's272* no spec of the file format available. This parsing assumes that273* there's one line of an Olson tzid followed by a '\n', no274* leading or trailing spaces, no comments.275*/276if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) {277char line[256];278279if (fgets(line, sizeof(line), fp) != NULL) {280char *p = strchr(line, '\n');281if (p != NULL) {282*p = '\0';283}284if (strlen(line) > 0) {285tz = strdup(line);286}287}288(void) fclose(fp);289if (tz != NULL) {290return tz;291}292}293#endif /* defined(__linux__) */294295/*296* Next, try /etc/localtime to find the zone ID.297*/298if (lstat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) {299return NULL;300}301302/*303* If it's a symlink, get the link name and its zone ID part. (The304* older versions of timeconfig created a symlink as described in305* the Red Hat man page. It was changed in 1999 to create a copy306* of a zoneinfo file. It's no longer possible to get the zone ID307* from /etc/localtime.)308*/309if (S_ISLNK(statbuf.st_mode)) {310char linkbuf[PATH_MAX+1];311int len;312313if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {314jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",315DEFAULT_ZONEINFO_FILE);316return NULL;317}318linkbuf[len] = '\0';319tz = getZoneName(linkbuf);320if (tz != NULL) {321tz = strdup(tz);322return tz;323}324}325326/*327* If it's a regular file, we need to find out the same zoneinfo file328* that has been copied as /etc/localtime.329* If initial symbolic link resolution failed, we should treat target330* file as a regular file.331*/332if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) {333return NULL;334}335if (fstat(fd, &statbuf) == -1) {336(void) close(fd);337return NULL;338}339size = (size_t) statbuf.st_size;340buf = (char *) malloc(size);341if (buf == NULL) {342(void) close(fd);343return NULL;344}345346if (read(fd, buf, size) != (ssize_t) size) {347(void) close(fd);348free((void *) buf);349return NULL;350}351(void) close(fd);352353tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);354free((void *) buf);355return tz;356}357358#elif defined(__solaris__)359360#if !defined(__sparcv9) && !defined(amd64)361362/*363* Those file* functions mimic the UNIX stream io functions. This is364* because of the limitation of the number of open files on Solaris365* (32-bit mode only) due to the System V ABI.366*/367368#define BUFFER_SIZE 4096369370static struct iobuffer {371int magic; /* -1 to distinguish from the real FILE */372int fd; /* file descriptor */373char *buffer; /* pointer to buffer */374char *ptr; /* current read pointer */375char *endptr; /* end pointer */376};377378static int379fileclose(FILE *stream)380{381struct iobuffer *iop = (struct iobuffer *) stream;382383if (iop->magic != -1) {384return fclose(stream);385}386387if (iop == NULL) {388return 0;389}390close(iop->fd);391free((void *)iop->buffer);392free((void *)iop);393return 0;394}395396static FILE *397fileopen(const char *fname, const char *fmode)398{399FILE *fp;400int fd;401struct iobuffer *iop;402403if ((fp = fopen(fname, fmode)) != NULL) {404return fp;405}406407/*408* It assumes read open.409*/410if ((fd = open(fname, O_RDONLY)) == -1) {411return NULL;412}413414/*415* Allocate struct iobuffer and its buffer416*/417iop = malloc(sizeof(struct iobuffer));418if (iop == NULL) {419(void) close(fd);420errno = ENOMEM;421return NULL;422}423iop->magic = -1;424iop->fd = fd;425iop->buffer = malloc(BUFFER_SIZE);426if (iop->buffer == NULL) {427(void) close(fd);428free((void *) iop);429errno = ENOMEM;430return NULL;431}432iop->ptr = iop->buffer;433iop->endptr = iop->buffer;434return (FILE *)iop;435}436437/*438* This implementation assumes that n is large enough and the line439* separator is '\n'.440*/441static char *442filegets(char *s, int n, FILE *stream)443{444struct iobuffer *iop = (struct iobuffer *) stream;445char *p;446447if (iop->magic != -1) {448return fgets(s, n, stream);449}450451p = s;452for (;;) {453char c;454455if (iop->ptr == iop->endptr) {456ssize_t len;457458if ((len = read(iop->fd, (void *)iop->buffer, BUFFER_SIZE)) == -1) {459return NULL;460}461if (len == 0) {462*p = 0;463if (s == p) {464return NULL;465}466return s;467}468iop->ptr = iop->buffer;469iop->endptr = iop->buffer + len;470}471c = *iop->ptr++;472*p++ = c;473if ((p - s) == (n - 1)) {474*p = 0;475return s;476}477if (c == '\n') {478*p = 0;479return s;480}481}482/*NOTREACHED*/483}484#endif /* !defined(__sparcv9) && !defined(amd64) */485486/*487* Performs Solaris dependent mapping. Returns a zone ID if488* found. Otherwise, NULL is returned. Solaris libc looks up489* "/etc/default/init" to get the default TZ value if TZ is not defined490* as an environment variable.491*/492static char *493getPlatformTimeZoneID()494{495char *tz = NULL;496FILE *fp;497498/*499* Try the TZ entry in /etc/default/init.500*/501if ((fp = fileopen(SYS_INIT_FILE, "r")) != NULL) {502char line[256];503char quote = '\0';504505while (filegets(line, sizeof(line), fp) != NULL) {506char *p = line;507char *s;508char c;509510/* quick check for comment lines */511if (*p == '#') {512continue;513}514if (strncmp(p, "TZ=", 3) == 0) {515p += 3;516SKIP_SPACE(p);517c = *p;518if (c == '"' || c == '\'') {519quote = c;520p++;521}522523/*524* PSARC/2001/383: quoted string support525*/526for (s = p; (c = *s) != '\0' && c != '\n'; s++) {527/* No '\\' is supported here. */528if (c == quote) {529quote = '\0';530break;531}532if (c == ' ' && quote == '\0') {533break;534}535}536if (quote != '\0') {537jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n");538}539*s = '\0';540tz = strdup(p);541break;542}543}544(void) fileclose(fp);545}546return tz;547}548549#define TIMEZONE_FMRI "svc:/system/timezone:default"550#define TIMEZONE_PG "timezone"551#define LOCALTIME_PROP "localtime"552553static void554cleanupScf(scf_handle_t *h,555scf_snapshot_t *snap,556scf_instance_t *inst,557scf_propertygroup_t *pg,558scf_property_t *prop,559scf_value_t *val,560char *buf) {561if (buf != NULL) {562free(buf);563}564if (snap != NULL) {565scf_snapshot_destroy(snap);566}567if (val != NULL) {568scf_value_destroy(val);569}570if (prop != NULL) {571scf_property_destroy(prop);572}573if (pg != NULL) {574scf_pg_destroy(pg);575}576if (inst != NULL) {577scf_instance_destroy(inst);578}579if (h != NULL) {580scf_handle_destroy(h);581}582}583584/*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 as587* /usr/share/lib/zoneinfo/localtime under /usr/share/lib/zoneinfo/.588*/589static char *590getSolarisDefaultZoneID() {591char *tz = NULL;592struct stat statbuf;593size_t size;594char *buf;595int fd;596/* scf specific variables */597scf_handle_t *h = NULL;598scf_snapshot_t *snap = NULL;599scf_instance_t *inst = NULL;600scf_propertygroup_t *pg = NULL;601scf_property_t *prop = NULL;602scf_value_t *val = NULL;603604if ((h = scf_handle_create(SCF_VERSION)) != NULL605&& scf_handle_bind(h) == 0606&& (inst = scf_instance_create(h)) != NULL607&& (snap = scf_snapshot_create(h)) != NULL608&& (pg = scf_pg_create(h)) != NULL609&& (prop = scf_property_create(h)) != NULL610&& (val = scf_value_create(h)) != NULL611&& scf_handle_decode_fmri(h, TIMEZONE_FMRI, NULL, NULL, inst,612NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) == 0613&& scf_instance_get_snapshot(inst, "running", snap) == 0614&& scf_instance_get_pg_composed(inst, snap, TIMEZONE_PG, pg) == 0615&& scf_pg_get_property(pg, LOCALTIME_PROP, prop) == 0616&& scf_property_get_value(prop, val) == 0) {617ssize_t len;618619/* Gets the length of the zone ID string */620len = scf_value_get_astring(val, NULL, 0);621if (len != -1) {622tz = malloc(++len); /* +1 for a null byte */623if (tz != NULL && scf_value_get_astring(val, tz, len) != -1) {624cleanupScf(h, snap, inst, pg, prop, val, NULL);625return tz;626}627}628}629cleanupScf(h, snap, inst, pg, prop, val, tz);630631if (stat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) {632return NULL;633}634size = (size_t) statbuf.st_size;635buf = malloc(size);636if (buf == NULL) {637return NULL;638}639if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) {640free((void *) buf);641return NULL;642}643644if (read(fd, buf, size) != (ssize_t) size) {645(void) close(fd);646free((void *) buf);647return NULL;648}649(void) close(fd);650tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);651free((void *) buf);652return tz;653}654655#endif /* defined(__solaris__) */656657#elif defined(_AIX)658659static char *660getPlatformTimeZoneID()661{662FILE *fp;663char *tz = NULL;664char *tz_key = "TZ=";665char line[256];666size_t tz_key_len = strlen(tz_key);667668if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) {669while (fgets(line, sizeof(line), fp) != NULL) {670char *p = strchr(line, '\n');671if (p != NULL) {672*p = '\0';673}674if (0 == strncmp(line, tz_key, tz_key_len)) {675tz = strdup(line + tz_key_len);676break;677}678}679(void) fclose(fp);680}681682return tz;683}684685static char *686mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {687FILE *tzmapf;688char mapfilename[PATH_MAX + 1];689char line[256];690int linecount = 0;691char *tz_buf = NULL;692char *temp_tz = NULL;693char *javatz = NULL;694size_t tz_len = 0;695696/* On AIX, the TZ environment variable may end with a comma697* followed by modifier fields. These are ignored here. */698temp_tz = strchr(tz, ',');699tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz;700tz_buf = (char *)malloc(tz_len + 1);701memcpy(tz_buf, tz, tz_len);702tz_buf[tz_len] = 0;703704/* Open tzmappings file, with buffer overrun check */705if ((strlen(java_home_dir) + 15) > PATH_MAX) {706jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);707goto tzerr;708}709strcpy(mapfilename, java_home_dir);710strcat(mapfilename, "/lib/tzmappings");711if ((tzmapf = fopen(mapfilename, "r")) == NULL) {712jio_fprintf(stderr, "can't open %s\n", mapfilename);713goto tzerr;714}715716while (fgets(line, sizeof(line), tzmapf) != NULL) {717char *p = line;718char *sol = line;719char *java;720int result;721722linecount++;723/*724* Skip comments and blank lines725*/726if (*p == '#' || *p == '\n') {727continue;728}729730/*731* Get the first field, platform zone ID732*/733while (*p != '\0' && *p != '\t') {734p++;735}736if (*p == '\0') {737/* mapping table is broken! */738jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount);739break;740}741742*p++ = '\0';743if ((result = strncmp(tz_buf, sol, tz_len)) == 0) {744/*745* If this is the current platform zone ID,746* take the Java time zone ID (2nd field).747*/748java = p;749while (*p != '\0' && *p != '\n') {750p++;751}752753if (*p == '\0') {754/* mapping table is broken! */755jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount);756break;757}758759*p = '\0';760javatz = strdup(java);761break;762} else if (result < 0) {763break;764}765}766(void) fclose(tzmapf);767768tzerr:769if (tz_buf != NULL ) {770free((void *) tz_buf);771}772773if (javatz == NULL) {774return getGMTOffsetID();775}776777return javatz;778}779780#endif /* defined(_AIX) */781782/*783* findJavaTZ_md() maps platform time zone ID to Java time zone ID784* using <java_home>/lib/tzmappings. If the TZ value is not found, it785* trys some libc implementation dependent mappings. If it still786* can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm787* form.788*/789/*ARGSUSED1*/790char *791findJavaTZ_md(const char *java_home_dir)792{793char *tz;794char *javatz = NULL;795char *freetz = NULL;796797tz = getenv("TZ");798799if (tz == NULL || *tz == '\0') {800tz = getPlatformTimeZoneID();801freetz = tz;802}803804if (tz != NULL) {805/* Ignore preceding ':' */806if (*tz == ':') {807tz++;808}809#if defined(__linux__)810/* Ignore "posix/" prefix on Linux. */811if (strncmp(tz, "posix/", 6) == 0) {812tz += 6;813}814#endif815816#if defined(_AIX)817/* On AIX do the platform to Java mapping. */818javatz = mapPlatformToJavaTimezone(java_home_dir, tz);819if (freetz != NULL) {820free((void *) freetz);821}822#else823#if defined(__solaris__)824/* Solaris might use localtime, so handle it here. */825if (strcmp(tz, "localtime") == 0) {826javatz = getSolarisDefaultZoneID();827if (freetz != NULL) {828free((void *) freetz);829}830} else831#endif832if (freetz == NULL) {833/* strdup if we are still working on getenv result. */834javatz = strdup(tz);835} else if (freetz != tz) {836/* strdup and free the old buffer, if we moved the pointer. */837javatz = strdup(tz);838free((void *) freetz);839} else {840/* we are good if we already work on a freshly allocated buffer. */841javatz = tz;842}843#endif844}845846return javatz;847}848849/**850* Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00")851*/852853#if defined(MACOSX)854855char *856getGMTOffsetID()857{858time_t offset;859char sign, buf[32];860struct tm *local_tm;861time_t clock;862time_t currenttime;863864clock = time(NULL);865tzset();866local_tm = localtime(&clock);867if (local_tm->tm_gmtoff >= 0) {868offset = (time_t) local_tm->tm_gmtoff;869sign = '+';870} else {871offset = (time_t) -local_tm->tm_gmtoff;872sign = '-';873}874sprintf(buf, (const char *)"GMT%c%02d:%02d",875sign, (int)(offset/3600), (int)((offset%3600)/60));876return strdup(buf);877}878879#else880881char *882getGMTOffsetID()883{884time_t offset;885char sign, buf[32];886#if defined(__solaris__)887struct tm localtm;888time_t currenttime;889890currenttime = time(NULL);891if (localtime_r(¤ttime, &localtm) == NULL) {892return NULL;893}894895offset = localtm.tm_isdst ? altzone : timezone;896#else897offset = timezone;898#endif899900if (offset == 0) {901return strdup("GMT");902}903904/* Note that the time offset direction is opposite. */905if (offset > 0) {906sign = '-';907} else {908offset = -offset;909sign = '+';910}911sprintf(buf, (const char *)"GMT%c%02d:%02d",912sign, (int)(offset/3600), (int)((offset%3600)/60));913return strdup(buf);914}915#endif /* MACOSX */916917918