|
1 /* |
|
2 * Copyright 1999-2004 Sun Microsystems, Inc. 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. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any 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 |
|
34 #ifdef __linux__ |
|
35 #include <string.h> |
|
36 #include <dirent.h> |
|
37 #include <sys/stat.h> |
|
38 #include <sys/types.h> |
|
39 #include <unistd.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 #ifdef __linux__ |
|
53 |
|
54 static const char *sysconfig_clock_file = "/etc/sysconfig/clock"; |
|
55 static const char *zoneinfo_dir = "/usr/share/zoneinfo"; |
|
56 static const char *defailt_zoneinfo_file = "/etc/localtime"; |
|
57 |
|
58 /* |
|
59 * Returns a point to the zone ID portion of the given zoneinfo file |
|
60 * name. |
|
61 */ |
|
62 static char * |
|
63 getZoneName(char *str) |
|
64 { |
|
65 static const char *zidir = "zoneinfo/"; |
|
66 |
|
67 char * pos = strstr((const char *)str, zidir); |
|
68 if (pos == NULL) { |
|
69 return NULL; |
|
70 } |
|
71 return pos + strlen(zidir); |
|
72 } |
|
73 |
|
74 /* |
|
75 * Returns a path name created from the given 'dir' and 'name' under |
|
76 * UNIX. This function allocates memory for the pathname calling |
|
77 * malloc(). |
|
78 */ |
|
79 static char * |
|
80 getPathName(const char *dir, const char *name) { |
|
81 char *path; |
|
82 |
|
83 path = (char *) malloc(strlen(dir) + strlen(name) + 2); |
|
84 if (path == NULL) { |
|
85 return NULL; |
|
86 } |
|
87 return strcat(strcat(strcpy(path, dir), "/"), name); |
|
88 } |
|
89 |
|
90 /* |
|
91 * Scans the specified directory and its subdirectories to find a |
|
92 * zoneinfo file which has the same content as /etc/localtime given in |
|
93 * 'buf'. Returns a zone ID if found, otherwise, NULL is returned. |
|
94 */ |
|
95 static char * |
|
96 findZoneinfoFile(char *buf, size_t size, const char *dir) |
|
97 { |
|
98 DIR *dirp = NULL; |
|
99 struct stat statbuf; |
|
100 union { |
|
101 struct dirent d; |
|
102 char b[offsetof (struct dirent, d_name) + NAME_MAX + 1]; |
|
103 } entry; |
|
104 struct dirent *dp; |
|
105 char *pathname = NULL; |
|
106 int fd = -1; |
|
107 char *dbuf = NULL; |
|
108 char *tz = NULL; |
|
109 |
|
110 dirp = opendir(dir); |
|
111 if (dirp == NULL) { |
|
112 return NULL; |
|
113 } |
|
114 |
|
115 while (readdir_r(dirp, &entry.d, &dp) == 0 && dp != NULL) { |
|
116 /* |
|
117 * Skip '.' and '..' (and possibly other .* files) |
|
118 */ |
|
119 if (dp->d_name[0] == '.') { |
|
120 continue; |
|
121 } |
|
122 |
|
123 /* |
|
124 * Skip "ROC", "posixrules", and "localtime" since Java doesn't |
|
125 * support them. |
|
126 */ |
|
127 if ((strcmp(dp->d_name, "ROC") == 0) |
|
128 || (strcmp(dp->d_name, "posixrules") == 0) |
|
129 || (strcmp(dp->d_name, "localtime") == 0)) { |
|
130 continue; |
|
131 } |
|
132 |
|
133 pathname = getPathName(dir, dp->d_name); |
|
134 if (pathname == NULL) { |
|
135 break; |
|
136 } |
|
137 if (stat(pathname, &statbuf) == -1) { |
|
138 break; |
|
139 } |
|
140 |
|
141 if (S_ISDIR(statbuf.st_mode)) { |
|
142 tz = findZoneinfoFile(buf, size, pathname); |
|
143 if (tz != NULL) { |
|
144 break; |
|
145 } |
|
146 } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) { |
|
147 dbuf = (char *) malloc(size); |
|
148 if (dbuf == NULL) { |
|
149 break; |
|
150 } |
|
151 if ((fd = open(pathname, O_RDONLY)) == -1) { |
|
152 fd = 0; |
|
153 break; |
|
154 } |
|
155 if (read(fd, dbuf, size) != (ssize_t) size) { |
|
156 break; |
|
157 } |
|
158 if (memcmp(buf, dbuf, size) == 0) { |
|
159 tz = getZoneName(pathname); |
|
160 if (tz != NULL) { |
|
161 tz = strdup(tz); |
|
162 } |
|
163 break; |
|
164 } |
|
165 free((void *) dbuf); |
|
166 dbuf = NULL; |
|
167 (void) close(fd); |
|
168 fd = 0; |
|
169 } |
|
170 free((void *) pathname); |
|
171 pathname = NULL; |
|
172 } |
|
173 |
|
174 if (dirp != NULL) { |
|
175 (void) closedir(dirp); |
|
176 } |
|
177 if (pathname != NULL) { |
|
178 free((void *) pathname); |
|
179 } |
|
180 if (fd != 0) { |
|
181 (void) close(fd); |
|
182 } |
|
183 if (dbuf != NULL) { |
|
184 free((void *) dbuf); |
|
185 } |
|
186 return tz; |
|
187 } |
|
188 |
|
189 /* |
|
190 * Performs libc implementation specific mapping and returns a zone ID |
|
191 * if found. Otherwise, NULL is returned. |
|
192 */ |
|
193 static char * |
|
194 getPlatformTimeZoneID() |
|
195 { |
|
196 struct stat statbuf; |
|
197 char *tz = NULL; |
|
198 FILE *fp; |
|
199 int fd; |
|
200 char *buf; |
|
201 size_t size; |
|
202 |
|
203 /* |
|
204 * First, try the ZONE entry in /etc/sysconfig/clock. However, the |
|
205 * ZONE entry is not set up after initial Red Hat Linux |
|
206 * installation. In case that /etc/localtime is set up without |
|
207 * using timeconfig, there might be inconsistency between |
|
208 * /etc/localtime and the ZONE entry. The inconsistency between |
|
209 * timeconfig and linuxconf is reported as a bug in the Red Hat |
|
210 * web page as of May 1, 2000. |
|
211 */ |
|
212 if ((fp = fopen(sysconfig_clock_file, "r")) != NULL) { |
|
213 char line[256]; |
|
214 |
|
215 while (fgets(line, sizeof(line), fp) != NULL) { |
|
216 char *p = line; |
|
217 char *s; |
|
218 |
|
219 SKIP_SPACE(p); |
|
220 if (*p != 'Z') { |
|
221 continue; |
|
222 } |
|
223 if (strncmp(p, "ZONE=\"", 6) == 0) { |
|
224 p += 6; |
|
225 } else { |
|
226 /* |
|
227 * In case we need to parse it token by token. |
|
228 */ |
|
229 if (strncmp(p, "ZONE", 4) != 0) { |
|
230 continue; |
|
231 } |
|
232 p += 4; |
|
233 SKIP_SPACE(p); |
|
234 if (*p++ != '=') { |
|
235 break; |
|
236 } |
|
237 SKIP_SPACE(p); |
|
238 if (*p++ != '"') { |
|
239 break; |
|
240 } |
|
241 } |
|
242 for (s = p; *s && *s != '"'; s++) |
|
243 ; |
|
244 if (*s != '"') { |
|
245 /* this ZONE entry is broken. */ |
|
246 break; |
|
247 } |
|
248 *s = '\0'; |
|
249 tz = strdup(p); |
|
250 break; |
|
251 } |
|
252 (void) fclose(fp); |
|
253 if (tz != NULL) { |
|
254 return tz; |
|
255 } |
|
256 } |
|
257 |
|
258 /* |
|
259 * Next, try /etc/localtime to find the zone ID. |
|
260 */ |
|
261 if (lstat(defailt_zoneinfo_file, &statbuf) == -1) { |
|
262 return NULL; |
|
263 } |
|
264 |
|
265 /* |
|
266 * If it's a symlink, get the link name and its zone ID part. (The |
|
267 * older versions of timeconfig created a symlink as described in |
|
268 * the Red Hat man page. It was changed in 1999 to create a copy |
|
269 * of a zoneinfo file. It's no longer possible to get the zone ID |
|
270 * from /etc/localtime.) |
|
271 */ |
|
272 if (S_ISLNK(statbuf.st_mode)) { |
|
273 char linkbuf[PATH_MAX+1]; |
|
274 int len; |
|
275 |
|
276 if ((len = readlink(defailt_zoneinfo_file, linkbuf, sizeof(linkbuf)-1)) == -1) { |
|
277 jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n", |
|
278 defailt_zoneinfo_file); |
|
279 return NULL; |
|
280 } |
|
281 linkbuf[len] = '\0'; |
|
282 tz = getZoneName(linkbuf); |
|
283 if (tz != NULL) { |
|
284 tz = strdup(tz); |
|
285 } |
|
286 return tz; |
|
287 } |
|
288 |
|
289 /* |
|
290 * If it's a regular file, we need to find out the same zoneinfo file |
|
291 * that has been copied as /etc/localtime. |
|
292 */ |
|
293 size = (size_t) statbuf.st_size; |
|
294 buf = (char *) malloc(size); |
|
295 if (buf == NULL) { |
|
296 return NULL; |
|
297 } |
|
298 if ((fd = open(defailt_zoneinfo_file, O_RDONLY)) == -1) { |
|
299 free((void *) buf); |
|
300 return NULL; |
|
301 } |
|
302 |
|
303 if (read(fd, buf, size) != (ssize_t) size) { |
|
304 (void) close(fd); |
|
305 free((void *) buf); |
|
306 return NULL; |
|
307 } |
|
308 (void) close(fd); |
|
309 |
|
310 tz = findZoneinfoFile(buf, size, zoneinfo_dir); |
|
311 free((void *) buf); |
|
312 return tz; |
|
313 } |
|
314 #else |
|
315 #ifdef __solaris__ |
|
316 #if !defined(__sparcv9) && !defined(amd64) |
|
317 |
|
318 /* |
|
319 * Those file* functions mimic the UNIX stream io functions. This is |
|
320 * because of the limitation of the number of open files on Solaris |
|
321 * (32-bit mode only) due to the System V ABI. |
|
322 */ |
|
323 |
|
324 #define BUFFER_SIZE 4096 |
|
325 |
|
326 static struct iobuffer { |
|
327 int magic; /* -1 to distinguish from the real FILE */ |
|
328 int fd; /* file descriptor */ |
|
329 char *buffer; /* pointer to buffer */ |
|
330 char *ptr; /* current read pointer */ |
|
331 char *endptr; /* end pointer */ |
|
332 }; |
|
333 |
|
334 static int |
|
335 fileclose(FILE *stream) |
|
336 { |
|
337 struct iobuffer *iop = (struct iobuffer *) stream; |
|
338 |
|
339 if (iop->magic != -1) { |
|
340 return fclose(stream); |
|
341 } |
|
342 |
|
343 if (iop == NULL) { |
|
344 return 0; |
|
345 } |
|
346 close(iop->fd); |
|
347 free((void *)iop->buffer); |
|
348 free((void *)iop); |
|
349 return 0; |
|
350 } |
|
351 |
|
352 static FILE * |
|
353 fileopen(const char *fname, const char *fmode) |
|
354 { |
|
355 FILE *fp; |
|
356 int fd; |
|
357 struct iobuffer *iop; |
|
358 |
|
359 if ((fp = fopen(fname, fmode)) != NULL) { |
|
360 return fp; |
|
361 } |
|
362 |
|
363 /* |
|
364 * It assumes read open. |
|
365 */ |
|
366 if ((fd = open(fname, O_RDONLY)) == -1) { |
|
367 return NULL; |
|
368 } |
|
369 |
|
370 /* |
|
371 * Allocate struct iobuffer and its buffer |
|
372 */ |
|
373 iop = malloc(sizeof(struct iobuffer)); |
|
374 if (iop == NULL) { |
|
375 (void) close(fd); |
|
376 errno = ENOMEM; |
|
377 return NULL; |
|
378 } |
|
379 iop->magic = -1; |
|
380 iop->fd = fd; |
|
381 iop->buffer = malloc(BUFFER_SIZE); |
|
382 if (iop->buffer == NULL) { |
|
383 (void) close(fd); |
|
384 free((void *) iop); |
|
385 errno = ENOMEM; |
|
386 return NULL; |
|
387 } |
|
388 iop->ptr = iop->buffer; |
|
389 iop->endptr = iop->buffer; |
|
390 return (FILE *)iop; |
|
391 } |
|
392 |
|
393 /* |
|
394 * This implementation assumes that n is large enough and the line |
|
395 * separator is '\n'. |
|
396 */ |
|
397 static char * |
|
398 filegets(char *s, int n, FILE *stream) |
|
399 { |
|
400 struct iobuffer *iop = (struct iobuffer *) stream; |
|
401 char *p; |
|
402 |
|
403 if (iop->magic != -1) { |
|
404 return fgets(s, n, stream); |
|
405 } |
|
406 |
|
407 p = s; |
|
408 for (;;) { |
|
409 char c; |
|
410 |
|
411 if (iop->ptr == iop->endptr) { |
|
412 ssize_t len; |
|
413 |
|
414 if ((len = read(iop->fd, (void *)iop->buffer, BUFFER_SIZE)) == -1) { |
|
415 return NULL; |
|
416 } |
|
417 if (len == 0) { |
|
418 *p = 0; |
|
419 if (s == p) { |
|
420 return NULL; |
|
421 } |
|
422 return s; |
|
423 } |
|
424 iop->ptr = iop->buffer; |
|
425 iop->endptr = iop->buffer + len; |
|
426 } |
|
427 c = *iop->ptr++; |
|
428 *p++ = c; |
|
429 if ((p - s) == (n - 1)) { |
|
430 *p = 0; |
|
431 return s; |
|
432 } |
|
433 if (c == '\n') { |
|
434 *p = 0; |
|
435 return s; |
|
436 } |
|
437 } |
|
438 /*NOTREACHED*/ |
|
439 } |
|
440 #endif /* not __sparcv9 */ |
|
441 |
|
442 static const char *sys_init_file = "/etc/default/init"; |
|
443 |
|
444 /* |
|
445 * Performs libc implementation dependent mapping. Returns a zone ID |
|
446 * if found. Otherwise, NULL is returned. Solaris libc looks up |
|
447 * "/etc/default/init" to get a default TZ value if TZ is not defined |
|
448 * as an environment variable. |
|
449 */ |
|
450 static char * |
|
451 getPlatformTimeZoneID() |
|
452 { |
|
453 char *tz = NULL; |
|
454 FILE *fp; |
|
455 |
|
456 /* |
|
457 * Try the TZ entry in /etc/default/init. |
|
458 */ |
|
459 if ((fp = fileopen(sys_init_file, "r")) != NULL) { |
|
460 char line[256]; |
|
461 char quote = '\0'; |
|
462 |
|
463 while (filegets(line, sizeof(line), fp) != NULL) { |
|
464 char *p = line; |
|
465 char *s; |
|
466 char c; |
|
467 |
|
468 /* quick check for comment lines */ |
|
469 if (*p == '#') { |
|
470 continue; |
|
471 } |
|
472 if (strncmp(p, "TZ=", 3) == 0) { |
|
473 p += 3; |
|
474 SKIP_SPACE(p); |
|
475 c = *p; |
|
476 if (c == '"' || c == '\'') { |
|
477 quote = c; |
|
478 p++; |
|
479 } |
|
480 |
|
481 /* |
|
482 * PSARC/2001/383: quoted string support |
|
483 */ |
|
484 for (s = p; (c = *s) != '\0' && c != '\n'; s++) { |
|
485 /* No '\\' is supported here. */ |
|
486 if (c == quote) { |
|
487 quote = '\0'; |
|
488 break; |
|
489 } |
|
490 if (c == ' ' && quote == '\0') { |
|
491 break; |
|
492 } |
|
493 } |
|
494 if (quote != '\0') { |
|
495 jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n"); |
|
496 } |
|
497 *s = '\0'; |
|
498 tz = strdup(p); |
|
499 break; |
|
500 } |
|
501 } |
|
502 (void) fileclose(fp); |
|
503 } |
|
504 return tz; |
|
505 } |
|
506 |
|
507 #endif |
|
508 #endif |
|
509 |
|
510 /* |
|
511 * findJavaTZ_md() maps platform time zone ID to Java time zone ID |
|
512 * using <java_home>/lib/tzmappings. If the TZ value is not found, it |
|
513 * trys some libc implementation dependent mappings. If it still |
|
514 * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm |
|
515 * form. `country', which can be null, is not used for UNIX platforms. |
|
516 */ |
|
517 /*ARGSUSED1*/ |
|
518 char * |
|
519 findJavaTZ_md(const char *java_home_dir, const char *country) |
|
520 { |
|
521 char *tz; |
|
522 char *javatz = NULL; |
|
523 char *freetz = NULL; |
|
524 |
|
525 tz = getenv("TZ"); |
|
526 |
|
527 #ifdef __linux__ |
|
528 if (tz == NULL) { |
|
529 #else |
|
530 #ifdef __solaris__ |
|
531 if (tz == NULL || *tz == '\0') { |
|
532 #endif |
|
533 #endif |
|
534 tz = getPlatformTimeZoneID(); |
|
535 freetz = tz; |
|
536 } |
|
537 |
|
538 if (tz != NULL) { |
|
539 if (*tz == ':') { |
|
540 tz++; |
|
541 } |
|
542 #ifdef __linux__ |
|
543 /* |
|
544 * Ignore "posix/" prefix. |
|
545 */ |
|
546 if (strncmp(tz, "posix/", 6) == 0) { |
|
547 tz += 6; |
|
548 } |
|
549 #endif |
|
550 javatz = strdup(tz); |
|
551 if (freetz != NULL) { |
|
552 free((void *) freetz); |
|
553 } |
|
554 } |
|
555 return javatz; |
|
556 } |
|
557 |
|
558 /** |
|
559 * Returns a GMT-offset-based time zone ID. (e.g., "GMT-08:00") |
|
560 */ |
|
561 char * |
|
562 getGMTOffsetID() |
|
563 { |
|
564 time_t offset; |
|
565 char sign, buf[16]; |
|
566 |
|
567 if (timezone == 0) { |
|
568 return strdup("GMT"); |
|
569 } |
|
570 |
|
571 /* Note that the time offset direction is opposite. */ |
|
572 if (timezone > 0) { |
|
573 offset = timezone; |
|
574 sign = '-'; |
|
575 } else { |
|
576 offset = -timezone; |
|
577 sign = '+'; |
|
578 } |
|
579 sprintf(buf, (const char *)"GMT%c%02d:%02d", |
|
580 sign, (int)(offset/3600), (int)((offset%3600)/60)); |
|
581 return strdup(buf); |
|
582 } |