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(¤ttime, &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 */