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