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