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