PR143: don't hang with invalid locale
[cacao.git] / src / vm / properties.cpp
1 /* src/vm/properties.cpp - handling commandline properties
2
3    Copyright (C) 1996-2010
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <errno.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31
32 #include <string.h>
33 #include <time.h>
34 #include <unistd.h>
35
36 #include "mm/memory.hpp"
37
38 #include "native/llni.h"
39
40 #include "vm/class.hpp"
41 #include "vm/global.h"
42 #include "vm/method.hpp"
43 #include "vm/options.h"
44 #include "vm/os.hpp"
45 #include "vm/properties.hpp"
46 #include "vm/string.hpp"
47 #include "vm/vm.hpp"
48
49 #include "vm/jit/asmpart.h"
50
51 #ifdef HAVE_LOCALE_H
52 #include <locale.h>
53 #endif
54
55 /**
56  * Constructor fills the properties list with default values.
57  */
58 Properties::Properties()
59 {
60         int             len;
61         char           *p;
62
63         char           *boot_class_path;
64
65 #if defined(ENABLE_JAVASE)
66
67 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
68         struct utsname *utsnamebuf;
69 # endif
70 #endif
71
72 #if defined(ENABLE_JRE_LAYOUT)
73         /* SUN also uses a buffer of 4096-bytes (strace is your friend). */
74
75         p = MNEW(char, 4096);
76
77         if (os::readlink("/proc/self/exe", p, 4095) == -1)
78                 os::abort_errno("readlink failed");
79
80         /* We have a path like:
81
82            /path/to/executable/bin/java
83
84            or
85            
86            /path/to/executeable/jre/bin/java
87
88            Now let's strip two levels. */
89
90         p = os::dirname(p);
91         p = os::dirname(p);
92
93 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
94
95         /* Set java.home. */
96
97         char* java_home = strdup(p);
98
99         /* Set the path to Java core native libraries. */
100
101         len = strlen(java_home) + strlen("/lib/classpath") + strlen("0");
102
103         char* boot_library_path = MNEW(char, len);
104
105         strcpy(boot_library_path, java_home);
106         strcat(boot_library_path, "/lib/classpath");
107
108 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
109
110         /* Find correct java.home.  We check if there is a JRE
111            co-located. */
112
113         /* NOTE: We use the server VM here as it should be available on
114            all architectures. */
115
116         len =
117                 strlen(p) +
118                 strlen("/jre/lib/"JAVA_ARCH"/server/libjvm.so") +
119                 strlen("0");
120
121         char* java_home = MNEW(char, len);
122
123         strcpy(java_home, p);
124         strcat(java_home, "/jre/lib/"JAVA_ARCH"/server/libjvm.so");
125
126         // Check if that libjvm.so exists.
127         if (os::access(java_home, F_OK) == 0) {
128                 // Yes, we add /jre to java.home.
129                 strcpy(java_home, p);
130                 strcat(java_home, "/jre");
131         }
132         else {
133                 // No, java.home is parent directory.
134                 strcpy(java_home, p);
135         }
136
137         /* Set the path to Java core native libraries. */
138
139         len = strlen(java_home) + strlen("/lib/"JAVA_ARCH) + strlen("0");
140
141         char* boot_library_path = MNEW(char, len);
142
143         strcpy(boot_library_path, java_home);
144         strcat(boot_library_path, "/lib/"JAVA_ARCH);
145
146 # else
147 #  error unknown classpath configuration
148 # endif
149
150         /* Free path. */
151
152         MFREE(p, char, len);
153
154 #else
155         const char* java_home = CACAO_PREFIX;
156
157 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
158
159         const char* boot_library_path = JAVA_RUNTIME_LIBRARY_LIBDIR"/classpath";
160
161 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
162
163         const char* boot_library_path = JAVA_RUNTIME_LIBRARY_LIBDIR;
164
165 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1)
166
167         // No boot_library_path required.
168
169 # else
170 #  error unknown classpath configuration
171 # endif
172 #endif
173
174         put("java.home", java_home);
175
176         /* Set the bootclasspath. */
177
178         p = os::getenv("BOOTCLASSPATH");
179
180         if (p != NULL) {
181                 boot_class_path = MNEW(char, strlen(p) + strlen("0"));
182                 strcpy(boot_class_path, p);
183         }
184         else {
185 #if defined(ENABLE_JRE_LAYOUT)
186 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
187
188                 len =
189                         strlen(java_home) + strlen("/share/cacao/vm.zip:") +
190                         strlen(java_home) + strlen("/share/classpath/glibj.zip") +
191                         strlen("0");
192
193                 boot_class_path = MNEW(char, len);
194
195                 strcpy(boot_class_path, java_home);
196                 strcat(boot_class_path, "/share/cacao/vm.zip");
197                 strcat(boot_class_path, ":");
198                 strcat(boot_class_path, java_home);
199                 strcat(boot_class_path, "/share/classpath/glibj.zip");
200
201 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
202
203                 /* This is the bootclasspath taken from HotSpot (see
204                    hotspot/src/share/vm/runtime/os.cpp
205                    (os::set_boot_path)). */
206
207                 len =
208                         strlen(java_home) + strlen("/lib/resources.jar:") +
209                         strlen(java_home) + strlen("/lib/rt.jar:") +
210                         strlen(java_home) + strlen("/lib/sunrsasign.jar:") +
211                         strlen(java_home) + strlen("/lib/jsse.jar:") +
212                         strlen(java_home) + strlen("/lib/jce.jar:") +
213                         strlen(java_home) + strlen("/lib/charsets.jar:") +
214                         strlen(java_home) + strlen("/classes") +
215                         strlen("0");
216
217                 boot_class_path = MNEW(char, len);
218
219                 strcpy(boot_class_path, java_home);
220                 strcat(boot_class_path, "/lib/resources.jar:");
221                 strcat(boot_class_path, java_home);
222                 strcat(boot_class_path, "/lib/rt.jar:");
223                 strcat(boot_class_path, java_home);
224                 strcat(boot_class_path, "/lib/sunrsasign.jar:");
225                 strcat(boot_class_path, java_home);
226                 strcat(boot_class_path, "/lib/jsse.jar:");
227                 strcat(boot_class_path, java_home);
228                 strcat(boot_class_path, "/lib/jce.jar:");
229                 strcat(boot_class_path, java_home);
230                 strcat(boot_class_path, "/lib/charsets.jar:");
231                 strcat(boot_class_path, java_home);
232                 strcat(boot_class_path, "/classes");
233
234 # else
235 #  error unknown classpath configuration
236 # endif
237 #else
238 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
239
240                 len =
241                         strlen(CACAO_VM_ZIP) +
242                         strlen(":") +
243                         strlen(JAVA_RUNTIME_LIBRARY_CLASSES) +
244                         strlen("0");
245
246                 boot_class_path = MNEW(char, len);
247
248                 strcpy(boot_class_path, CACAO_VM_ZIP);
249                 strcat(boot_class_path, ":");
250                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_CLASSES);
251
252 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
253
254                 /* This is the bootclasspath taken from HotSpot (see
255                    hotspot/src/share/vm/runtime/os.cpp
256                    (os::set_boot_path)). */
257
258                 len =
259                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/resources.jar:") +
260                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/rt.jar:") +
261                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/sunrsasign.jar:") +
262                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/jsse.jar:") +
263                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/jce.jar:") +
264                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/lib/charsets.jar:") +
265                         strlen(JAVA_RUNTIME_LIBRARY_PREFIX"/classes") +
266                         strlen("0");
267
268                 boot_class_path = MNEW(char, len);
269
270                 strcpy(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/resources.jar:");
271                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/rt.jar:");
272                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/sunrsasign.jar:");
273                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/jsse.jar:");
274                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/jce.jar:");
275                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/lib/charsets.jar:");
276                 strcat(boot_class_path, JAVA_RUNTIME_LIBRARY_PREFIX"/classes");
277
278 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1)
279
280                 len =
281                         strlen(JAVA_RUNTIME_LIBRARY_CLASSES) +
282                         strlen("0");
283
284                 boot_class_path = MNEW(char, len);
285
286                 strcpy(boot_class_path, JAVA_RUNTIME_LIBRARY_CLASSES);
287
288 # else
289 #  error unknown classpath configuration
290 # endif
291 #endif
292         }
293
294         put("sun.boot.class.path", boot_class_path);
295         put("java.boot.class.path", boot_class_path);
296
297 #if defined(ENABLE_JAVASE)
298
299         /* Set the classpath. */
300
301         p = os::getenv("CLASSPATH");
302
303         char* class_path;
304
305         if (p != NULL) {
306                 class_path = MNEW(char, strlen(p) + strlen("0"));
307                 strcpy(class_path, p);
308         }
309         else {
310                 class_path = MNEW(char, strlen(".") + strlen("0"));
311                 strcpy(class_path, ".");
312         }
313
314         put("java.class.path", class_path);
315
316         // Add java.vm properties.
317         put("java.vm.specification.version", "1.0");
318         put("java.vm.specification.vendor", "Sun Microsystems Inc.");
319         put("java.vm.specification.name", "Java Virtual Machine Specification");
320         put("java.vm.version", VERSION_FULL);
321         put("java.vm.vendor", "CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO");
322         put("java.vm.name", "CACAO");
323
324 # if defined(ENABLE_INTRP)
325         if (opt_intrp) {
326                 /* XXX We don't support java.lang.Compiler */
327 /*              put("java.compiler", "cacao.intrp"); */
328                 put("java.vm.info", "interpreted mode");
329         }
330         else
331 # endif
332         {
333                 /* XXX We don't support java.lang.Compiler */
334 /*              put("java.compiler", "cacao.jit"); */
335                 put("java.vm.info", "compiled mode");
336         }
337
338         // Get and set java.library.path.
339         const char* java_library_path = os::getenv("LD_LIBRARY_PATH");
340
341         if (java_library_path == NULL)
342                 java_library_path = "";
343
344         put("java.library.path", java_library_path);
345
346 # if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
347
348         /* Get properties from system. */
349
350         char* cwd      = os::getcwd();
351
352         char* env_user = os::getenv("USER");
353         char* env_home = os::getenv("HOME");
354         char* env_lang = os::getenv("LANG");
355
356         utsnamebuf = NEW(struct utsname);
357
358         uname(utsnamebuf);
359
360         put("java.runtime.version", VERSION_FULL);
361         put("java.runtime.name", "CACAO");
362
363         put("java.specification.version", "1.5");
364         put("java.specification.vendor", "Sun Microsystems Inc.");
365         put("java.specification.name", "Java Platform API Specification");
366
367         put("java.version", JAVA_VERSION);
368         put("java.vendor", "GNU Classpath");
369         put("java.vendor.url", "http://www.gnu.org/software/classpath/");
370
371         put("java.class.version", CLASS_VERSION);
372
373         put("gnu.classpath.boot.library.path", boot_library_path);
374
375         put("java.io.tmpdir", "/tmp");
376
377 #  if defined(ENABLE_INTRP)
378         if (opt_intrp) {
379                 put("gnu.java.compiler.name", "cacao.intrp");
380         }
381         else
382 #  endif
383         {
384                 put("gnu.java.compiler.name", "cacao.jit");
385         }
386
387         /* Set the java.ext.dirs property. */
388
389         len = strlen(java_home) + strlen("/jre/lib/ext") + strlen("0");
390
391         char* extdirs = MNEW(char, len);
392
393         sprintf(extdirs, "%s/jre/lib/ext", java_home);
394
395         put("java.ext.dirs", extdirs);
396
397         /* Set the java.ext.endorsed property. */
398
399         len = strlen(java_home) + strlen("/jre/lib/endorsed") + strlen("0");
400
401         char* endorseddirs = MNEW(char, len);
402
403         sprintf(endorseddirs, "%s/jre/lib/endorsed", java_home);
404
405         put("java.endorsed.dirs", endorseddirs);
406
407 #  if defined(DISABLE_GC)
408         /* When we disable the GC, we mmap the whole heap to a specific
409            address, so we can compare call traces. For this reason we have
410            to add the same properties on different machines, otherwise
411            more memory may be allocated (e.g. strlen("i386")
412            vs. strlen("alpha"). */
413
414         put("os.arch", "unknown");
415         put("os.name", "unknown");
416         put("os.version", "unknown");
417 #  else
418         put("os.arch", JAVA_ARCH);
419         put("os.name", utsnamebuf->sysname);
420         put("os.version", utsnamebuf->release);
421 #  endif
422
423 #  if WORDS_BIGENDIAN == 1
424         put("gnu.cpu.endian", "big");
425 #  else
426         put("gnu.cpu.endian", "little");
427 #  endif
428
429         put("file.separator", "/");
430         put("path.separator", ":");
431         put("line.separator", "\n");
432
433         put("user.name", env_user ? env_user : "null");
434         put("user.home", env_home ? env_home : "null");
435         put("user.dir", cwd ? cwd : "null");
436
437         /* get locale */
438
439         bool use_en_US = true;
440         if (env_lang != NULL) {
441 #if defined(HAVE_SETLOCALE) && defined(HAVE_LC_MESSAGES)
442                 /* get the locale stuff from the environment */
443                 char *locale;
444
445                 if ((locale = setlocale(LC_MESSAGES, ""))) {
446                         int len = strlen(locale);
447                         if (((len >= 5) && (locale[2] == '_')) || len == 2)  {
448                                 use_en_US = false;
449                                 char* lang = MNEW(char, 3);
450                                 strncpy(lang, (char*) &locale[0], 2);
451                                 lang[2] = '\0';
452                                 put("user.language", lang);
453
454                                 if (len >= 5) {
455                                         char* country = MNEW(char, 3);
456                                         strncpy(country, (char*) &locale[3], 2);
457                                         country[2] = '\0';
458
459                                         put("user.country", country);
460                                 }
461                         }
462                 }
463 #endif
464         }
465         if (use_en_US) {
466                 /* if no default locale was specified, use `en_US' */
467
468                 put("user.language", "en");
469                 put("user.country", "US");
470         }
471
472 # elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
473
474         /* Actually this property is set by OpenJDK, but we need it in
475            nativevm_preinit(). */
476
477         put("sun.boot.library.path", boot_library_path);
478
479         // Set the java.ext.dirs property.
480         len =
481                 strlen(java_home) + strlen("/lib/ext") +
482                 strlen(":") +
483                 strlen("/usr/java/packages/lib/ext") +
484                 strlen("0");
485
486         char* extdirs = MNEW(char, len);
487
488         sprintf(extdirs, "%s/lib/ext:/usr/java/packages/lib/ext", java_home);
489
490         put("java.ext.dirs", extdirs);
491
492         // Set the java.ext.endorsed property.
493         len = strlen(java_home) + strlen("/lib/endorsed") + strlen("0");
494
495         char* endorseddirs = MNEW(char, len);
496
497         sprintf(endorseddirs, "%s/lib/endorsed", java_home);
498
499         put("java.endorsed.dirs", endorseddirs);
500
501 # else
502
503 #  error unknown classpath configuration
504
505 # endif
506
507 #elif defined(ENABLE_JAVAME_CLDC1_1)
508
509     put("microedition.configuration", "CLDC-1.1");
510     put("microedition.platform", "generic");
511     put("microedition.encoding", "ISO8859_1");
512     put("microedition.profiles", "");
513
514 #else
515
516 # error unknown Java configuration
517
518 #endif
519 }
520
521
522 /**
523  * Add the given property to the given Java system properties.
524  *
525  * @param p     Java properties object.
526  * @param key   Key.
527  * @param value Value.
528  */
529 void Properties::put(java_handle_t* p, const char* key, const char* value)
530 {
531         // Get Properties.put() method to add properties.
532         classinfo* c;
533         LLNI_class_get(p, c);
534
535         methodinfo* m = class_resolveclassmethod(c,
536                                                                                          utf_put,
537                                                                                          utf_new_char("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"),
538                                                                                          NULL,
539                                                                                          true);
540
541         if (m == NULL)
542                 return;
543
544         // Add to the Java system properties.
545         java_handle_t* k = javastring_new_from_utf_string(key);
546         java_handle_t* v = javastring_new_from_utf_string(value);
547
548         (void) vm_call_method(m, p, k, v);
549 }
550
551
552 /**
553  * Put the given property into the internal property map.  If there's
554  * already an entry with the same key, replace it.
555  *
556  * @param key   Key.
557  * @param value Value.
558  */
559 void Properties::put(const char* key, const char* value)
560 {
561         // Try to find the key.
562         std::map<const char*, const char*>::iterator it = _properties.find(key);
563
564         // The key is already in the map.
565         if (it != _properties.end()) {
566 #if !defined(NDEBUG)
567                 if (opt_DebugProperties) {
568                         printf("[Properties::put: key=%s, old value=%s, new value=%s]\n",
569                                    key, it->second, value);
570                 }
571 #endif
572
573                 // Replace the value in the current entry.
574                 it->second = value;
575
576                 return;
577         }
578
579         // The key was not found, insert the pair.
580 #if !defined(NDEBUG)
581         if (opt_DebugProperties) {
582                 printf("[Properties::put: key=%s, value=%s]\n", key, value);
583         }
584 #endif
585
586         _properties.insert(std::make_pair(key, value));
587 }
588
589
590 /**
591  * Get a property entry from the internal property map.
592  *
593  * @param key Key.
594  *
595  * @return Value associated with the key or NULL when not found.
596  */
597 const char* Properties::get(const char* key)
598 {
599         // Try to find the key.
600         std::map<const char*, const char*>::iterator it = _properties.find(key);
601
602         // The key is not in the map.
603         if (it == _properties.end())
604                 return NULL;
605
606         // Return the value.
607         return it->second;
608 }
609
610
611 /**
612  * Fill the given Java system properties with all properties from the
613  * internal properties map.
614  *
615  * @param p Java Properties object.
616  */
617 #if defined(ENABLE_JAVASE)
618 void Properties::fill(java_handle_t* p)
619 {
620         // Get Properties.put() method to add properties.
621         classinfo* c;
622         LLNI_class_get(p, c);
623
624         methodinfo* m = class_resolveclassmethod(c,
625                                                                                          utf_put,
626                                                                                          utf_new_char("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"),
627                                                                                          NULL,
628                                                                                          true);
629
630         if (m == NULL)
631                 return;
632
633         // Iterator over all properties.
634         for (std::map<const char*, const char*>::iterator it = _properties.begin(); it != _properties.end(); it++) {
635                 // Put into the Java system properties.
636                 java_handle_t* key   = javastring_new_from_utf_string(it->first);
637                 java_handle_t* value = javastring_new_from_utf_string(it->second);
638
639                 (void) vm_call_method(m, p, key, value);
640         }
641 }
642 #endif
643
644
645 /**
646  * Dump all property entries.
647  */
648 #if !defined(NDEBUG)
649 void Properties::dump()
650 {
651         for (std::map<const char*, const char*>::iterator it = _properties.begin(); it != _properties.end(); it++) {
652                 log_println("[Properties::dump: key=%s, value=%s]", it->first, it->second);
653         }
654 }
655 #endif
656
657
658 /*
659  * These are local overrides for various environment variables in Emacs.
660  * Please do not remove this and leave it at the end of the file, where
661  * Emacs will automagically detect them.
662  * ---------------------------------------------------------------------
663  * Local variables:
664  * mode: c++
665  * indent-tabs-mode: t
666  * c-basic-offset: 4
667  * tab-width: 4
668  * End:
669  * vim:noexpandtab:sw=4:ts=4:
670  */