|
1 /* |
|
2 * Copyright (c) 2012, 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 #include "java.h" |
|
26 |
|
27 /* |
|
28 * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put |
|
29 * "/foo" into buf. |
|
30 */ |
|
31 jboolean |
|
32 GetApplicationHome(char *buf, jint bufsize) |
|
33 { |
|
34 const char *execname = GetExecName(); |
|
35 if (execname != NULL) { |
|
36 JLI_Snprintf(buf, bufsize, "%s", execname); |
|
37 buf[bufsize-1] = '\0'; |
|
38 } else { |
|
39 return JNI_FALSE; |
|
40 } |
|
41 |
|
42 if (JLI_StrRChr(buf, '/') == 0) { |
|
43 buf[0] = '\0'; |
|
44 return JNI_FALSE; |
|
45 } |
|
46 *(JLI_StrRChr(buf, '/')) = '\0'; /* executable file */ |
|
47 if (JLI_StrLen(buf) < 4 || JLI_StrRChr(buf, '/') == 0) { |
|
48 buf[0] = '\0'; |
|
49 return JNI_FALSE; |
|
50 } |
|
51 if (JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) |
|
52 *(JLI_StrRChr(buf, '/')) = '\0'; /* sparcv9 or amd64 */ |
|
53 if (JLI_StrLen(buf) < 4 || JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) { |
|
54 buf[0] = '\0'; |
|
55 return JNI_FALSE; |
|
56 } |
|
57 *(JLI_StrRChr(buf, '/')) = '\0'; /* bin */ |
|
58 |
|
59 return JNI_TRUE; |
|
60 } |
|
61 /* |
|
62 * Return true if the named program exists |
|
63 */ |
|
64 static int |
|
65 ProgramExists(char *name) |
|
66 { |
|
67 struct stat sb; |
|
68 if (stat(name, &sb) != 0) return 0; |
|
69 if (S_ISDIR(sb.st_mode)) return 0; |
|
70 return (sb.st_mode & S_IEXEC) != 0; |
|
71 } |
|
72 |
|
73 /* |
|
74 * Find a command in a directory, returning the path. |
|
75 */ |
|
76 static char * |
|
77 Resolve(char *indir, char *cmd) |
|
78 { |
|
79 char name[PATH_MAX + 2], *real; |
|
80 |
|
81 if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1) > PATH_MAX) return 0; |
|
82 JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd); |
|
83 if (!ProgramExists(name)) return 0; |
|
84 real = JLI_MemAlloc(PATH_MAX + 2); |
|
85 if (!realpath(name, real)) |
|
86 JLI_StrCpy(real, name); |
|
87 return real; |
|
88 } |
|
89 |
|
90 /* |
|
91 * Find a path for the executable |
|
92 */ |
|
93 char * |
|
94 FindExecName(char *program) |
|
95 { |
|
96 char cwdbuf[PATH_MAX+2]; |
|
97 char *path; |
|
98 char *tmp_path; |
|
99 char *f; |
|
100 char *result = NULL; |
|
101 |
|
102 /* absolute path? */ |
|
103 if (*program == FILE_SEPARATOR || |
|
104 (FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':'))) |
|
105 return Resolve("", program+1); |
|
106 |
|
107 /* relative path? */ |
|
108 if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) { |
|
109 char buf[PATH_MAX+2]; |
|
110 return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program); |
|
111 } |
|
112 |
|
113 /* from search path? */ |
|
114 path = getenv("PATH"); |
|
115 if (!path || !*path) path = "."; |
|
116 tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2); |
|
117 JLI_StrCpy(tmp_path, path); |
|
118 |
|
119 for (f=tmp_path; *f && result==0; ) { |
|
120 char *s = f; |
|
121 while (*f && (*f != PATH_SEPARATOR)) ++f; |
|
122 if (*f) *f++ = 0; |
|
123 if (*s == FILE_SEPARATOR) |
|
124 result = Resolve(s, program); |
|
125 else { |
|
126 /* relative path element */ |
|
127 char dir[2*PATH_MAX]; |
|
128 JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)), |
|
129 FILE_SEPARATOR, s); |
|
130 result = Resolve(dir, program); |
|
131 } |
|
132 if (result != 0) break; |
|
133 } |
|
134 |
|
135 JLI_MemFree(tmp_path); |
|
136 return result; |
|
137 } |
|
138 |
|
139 void JLI_ReportErrorMessage(const char* fmt, ...) { |
|
140 va_list vl; |
|
141 va_start(vl, fmt); |
|
142 vfprintf(stderr, fmt, vl); |
|
143 fprintf(stderr, "\n"); |
|
144 va_end(vl); |
|
145 } |
|
146 |
|
147 void JLI_ReportErrorMessageSys(const char* fmt, ...) { |
|
148 va_list vl; |
|
149 char *emsg; |
|
150 |
|
151 /* |
|
152 * TODO: its safer to use strerror_r but is not available on |
|
153 * Solaris 8. Until then.... |
|
154 */ |
|
155 emsg = strerror(errno); |
|
156 if (emsg != NULL) { |
|
157 fprintf(stderr, "%s\n", emsg); |
|
158 } |
|
159 |
|
160 va_start(vl, fmt); |
|
161 vfprintf(stderr, fmt, vl); |
|
162 fprintf(stderr, "\n"); |
|
163 va_end(vl); |
|
164 } |
|
165 |
|
166 void JLI_ReportExceptionDescription(JNIEnv * env) { |
|
167 (*env)->ExceptionDescribe(env); |
|
168 } |
|
169 |
|
170 /* |
|
171 * Since using the file system as a registry is a bit risky, perform |
|
172 * additional sanity checks on the identified directory to validate |
|
173 * it as a valid jre/sdk. |
|
174 * |
|
175 * Return 0 if the tests fail; otherwise return non-zero (true). |
|
176 * |
|
177 * Note that checking for anything more than the existence of an |
|
178 * executable object at bin/java relative to the path being checked |
|
179 * will break the regression tests. |
|
180 */ |
|
181 static int |
|
182 CheckSanity(char *path, char *dir) |
|
183 { |
|
184 char buffer[PATH_MAX]; |
|
185 |
|
186 if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX) |
|
187 return (0); /* Silently reject "impossibly" long paths */ |
|
188 |
|
189 JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir); |
|
190 return ((access(buffer, X_OK) == 0) ? 1 : 0); |
|
191 } |
|
192 |
|
193 /* |
|
194 * Determine if there is an acceptable JRE in the directory dirname. |
|
195 * Upon locating the "best" one, return a fully qualified path to |
|
196 * it. "Best" is defined as the most advanced JRE meeting the |
|
197 * constraints contained in the manifest_info. If no JRE in this |
|
198 * directory meets the constraints, return NULL. |
|
199 * |
|
200 * Note that we don't check for errors in reading the directory |
|
201 * (which would be done by checking errno). This is because it |
|
202 * doesn't matter if we get an error reading the directory, or |
|
203 * we just don't find anything interesting in the directory. We |
|
204 * just return NULL in either case. |
|
205 * |
|
206 * The historical names of j2sdk and j2re were changed to jdk and |
|
207 * jre respecively as part of the 1.5 rebranding effort. Since the |
|
208 * former names are legacy on Linux, they must be recognized for |
|
209 * all time. Fortunately, this is a minor cost. |
|
210 */ |
|
211 static char |
|
212 *ProcessDir(manifest_info *info, char *dirname) |
|
213 { |
|
214 DIR *dirp; |
|
215 struct dirent *dp; |
|
216 char *best = NULL; |
|
217 int offset; |
|
218 int best_offset = 0; |
|
219 char *ret_str = NULL; |
|
220 char buffer[PATH_MAX]; |
|
221 |
|
222 if ((dirp = opendir(dirname)) == NULL) |
|
223 return (NULL); |
|
224 |
|
225 do { |
|
226 if ((dp = readdir(dirp)) != NULL) { |
|
227 offset = 0; |
|
228 if ((JLI_StrNCmp(dp->d_name, "jre", 3) == 0) || |
|
229 (JLI_StrNCmp(dp->d_name, "jdk", 3) == 0)) |
|
230 offset = 3; |
|
231 else if (JLI_StrNCmp(dp->d_name, "j2re", 4) == 0) |
|
232 offset = 4; |
|
233 else if (JLI_StrNCmp(dp->d_name, "j2sdk", 5) == 0) |
|
234 offset = 5; |
|
235 if (offset > 0) { |
|
236 if ((JLI_AcceptableRelease(dp->d_name + offset, |
|
237 info->jre_version)) && CheckSanity(dirname, dp->d_name)) |
|
238 if ((best == NULL) || (JLI_ExactVersionId( |
|
239 dp->d_name + offset, best + best_offset) > 0)) { |
|
240 if (best != NULL) |
|
241 JLI_MemFree(best); |
|
242 best = JLI_StringDup(dp->d_name); |
|
243 best_offset = offset; |
|
244 } |
|
245 } |
|
246 } |
|
247 } while (dp != NULL); |
|
248 (void) closedir(dirp); |
|
249 if (best == NULL) |
|
250 return (NULL); |
|
251 else { |
|
252 ret_str = JLI_MemAlloc(JLI_StrLen(dirname) + JLI_StrLen(best) + 2); |
|
253 sprintf(ret_str, "%s/%s", dirname, best); |
|
254 JLI_MemFree(best); |
|
255 return (ret_str); |
|
256 } |
|
257 } |
|
258 |
|
259 /* |
|
260 * This is the global entry point. It examines the host for the optimal |
|
261 * JRE to be used by scanning a set of directories. The set of directories |
|
262 * is platform dependent and can be overridden by the environment |
|
263 * variable JAVA_VERSION_PATH. |
|
264 * |
|
265 * This routine itself simply determines the set of appropriate |
|
266 * directories before passing control onto ProcessDir(). |
|
267 */ |
|
268 char* |
|
269 LocateJRE(manifest_info* info) |
|
270 { |
|
271 char *path; |
|
272 char *home; |
|
273 char *target = NULL; |
|
274 char *dp; |
|
275 char *cp; |
|
276 |
|
277 /* |
|
278 * Start by getting JAVA_VERSION_PATH |
|
279 */ |
|
280 if (info->jre_restrict_search) { |
|
281 path = JLI_StringDup(system_dir); |
|
282 } else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) { |
|
283 path = JLI_StringDup(path); |
|
284 } else { |
|
285 if ((home = getenv("HOME")) != NULL) { |
|
286 path = (char *)JLI_MemAlloc(JLI_StrLen(home) + \ |
|
287 JLI_StrLen(system_dir) + JLI_StrLen(user_dir) + 2); |
|
288 sprintf(path, "%s%s:%s", home, user_dir, system_dir); |
|
289 } else { |
|
290 path = JLI_StringDup(system_dir); |
|
291 } |
|
292 } |
|
293 |
|
294 /* |
|
295 * Step through each directory on the path. Terminate the scan with |
|
296 * the first directory with an acceptable JRE. |
|
297 */ |
|
298 cp = dp = path; |
|
299 while (dp != NULL) { |
|
300 cp = JLI_StrChr(dp, (int)':'); |
|
301 if (cp != NULL) |
|
302 *cp = '\0'; |
|
303 if ((target = ProcessDir(info, dp)) != NULL) |
|
304 break; |
|
305 dp = cp; |
|
306 if (dp != NULL) |
|
307 dp++; |
|
308 } |
|
309 JLI_MemFree(path); |
|
310 return (target); |
|
311 } |
|
312 |
|
313 /* |
|
314 * Given a path to a jre to execute, this routine checks if this process |
|
315 * is indeed that jre. If not, it exec's that jre. |
|
316 * |
|
317 * We want to actually check the paths rather than just the version string |
|
318 * built into the executable, so that given version specification (and |
|
319 * JAVA_VERSION_PATH) will yield the exact same Java environment, regardless |
|
320 * of the version of the arbitrary launcher we start with. |
|
321 */ |
|
322 void |
|
323 ExecJRE(char *jre, char **argv) |
|
324 { |
|
325 char wanted[PATH_MAX]; |
|
326 const char* progname = GetProgramName(); |
|
327 const char* execname = NULL; |
|
328 |
|
329 /* |
|
330 * Resolve the real path to the directory containing the selected JRE. |
|
331 */ |
|
332 if (realpath(jre, wanted) == NULL) { |
|
333 JLI_ReportErrorMessage(JRE_ERROR9, jre); |
|
334 exit(1); |
|
335 } |
|
336 |
|
337 /* |
|
338 * Resolve the real path to the currently running launcher. |
|
339 */ |
|
340 SetExecname(argv); |
|
341 execname = GetExecName(); |
|
342 if (execname == NULL) { |
|
343 JLI_ReportErrorMessage(JRE_ERROR10); |
|
344 exit(1); |
|
345 } |
|
346 |
|
347 /* |
|
348 * If the path to the selected JRE directory is a match to the initial |
|
349 * portion of the path to the currently executing JRE, we have a winner! |
|
350 * If so, just return. |
|
351 */ |
|
352 if (JLI_StrNCmp(wanted, execname, JLI_StrLen(wanted)) == 0) |
|
353 return; /* I am the droid you were looking for */ |
|
354 |
|
355 |
|
356 /* |
|
357 * This should never happen (because of the selection code in SelectJRE), |
|
358 * but check for "impossibly" long path names just because buffer overruns |
|
359 * can be so deadly. |
|
360 */ |
|
361 if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) { |
|
362 JLI_ReportErrorMessage(JRE_ERROR11); |
|
363 exit(1); |
|
364 } |
|
365 |
|
366 /* |
|
367 * Construct the path and exec it. |
|
368 */ |
|
369 (void)JLI_StrCat(JLI_StrCat(wanted, "/bin/"), progname); |
|
370 argv[0] = JLI_StringDup(progname); |
|
371 if (JLI_IsTraceLauncher()) { |
|
372 int i; |
|
373 printf("ReExec Command: %s (%s)\n", wanted, argv[0]); |
|
374 printf("ReExec Args:"); |
|
375 for (i = 1; argv[i] != NULL; i++) |
|
376 printf(" %s", argv[i]); |
|
377 printf("\n"); |
|
378 } |
|
379 JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n"); |
|
380 (void)fflush(stdout); |
|
381 (void)fflush(stderr); |
|
382 execv(wanted, argv); |
|
383 JLI_ReportErrorMessageSys(JRE_ERROR12, wanted); |
|
384 exit(1); |
|
385 } |
|
386 |
|
387 /* |
|
388 * "Borrowed" from Solaris 10 where the unsetenv() function is being added |
|
389 * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As |
|
390 * such, in the fullness of time this will appear in libc on all relevant |
|
391 * Solaris/Linux platforms and maybe even the Windows platform. At that |
|
392 * time, this stub can be removed. |
|
393 * |
|
394 * This implementation removes the environment locking for multithreaded |
|
395 * applications. (We don't have access to these mutexes within libc and |
|
396 * the launcher isn't multithreaded.) Note that what remains is platform |
|
397 * independent, because it only relies on attributes that a POSIX environment |
|
398 * defines. |
|
399 * |
|
400 * Returns 0 on success, -1 on failure. |
|
401 * |
|
402 * Also removed was the setting of errno. The only value of errno set |
|
403 * was EINVAL ("Invalid Argument"). |
|
404 */ |
|
405 |
|
406 /* |
|
407 * s1(environ) is name=value |
|
408 * s2(name) is name(not the form of name=value). |
|
409 * if names match, return value of 1, else return 0 |
|
410 */ |
|
411 static int |
|
412 match_noeq(const char *s1, const char *s2) |
|
413 { |
|
414 while (*s1 == *s2++) { |
|
415 if (*s1++ == '=') |
|
416 return (1); |
|
417 } |
|
418 if (*s1 == '=' && s2[-1] == '\0') |
|
419 return (1); |
|
420 return (0); |
|
421 } |
|
422 |
|
423 /* |
|
424 * added for SUSv3 standard |
|
425 * |
|
426 * Delete entry from environ. |
|
427 * Do not free() memory! Other threads may be using it. |
|
428 * Keep it around forever. |
|
429 */ |
|
430 static int |
|
431 borrowed_unsetenv(const char *name) |
|
432 { |
|
433 long idx; /* index into environ */ |
|
434 |
|
435 if (name == NULL || *name == '\0' || |
|
436 JLI_StrChr(name, '=') != NULL) { |
|
437 return (-1); |
|
438 } |
|
439 |
|
440 for (idx = 0; environ[idx] != NULL; idx++) { |
|
441 if (match_noeq(environ[idx], name)) |
|
442 break; |
|
443 } |
|
444 if (environ[idx] == NULL) { |
|
445 /* name not found but still a success */ |
|
446 return (0); |
|
447 } |
|
448 /* squeeze up one entry */ |
|
449 do { |
|
450 environ[idx] = environ[idx+1]; |
|
451 } while (environ[++idx] != NULL); |
|
452 |
|
453 return (0); |
|
454 } |
|
455 /* --- End of "borrowed" code --- */ |
|
456 |
|
457 /* |
|
458 * Wrapper for unsetenv() function. |
|
459 */ |
|
460 int |
|
461 UnsetEnv(char *name) |
|
462 { |
|
463 return(borrowed_unsetenv(name)); |
|
464 } |
|
465 |
|
466 const char * |
|
467 jlong_format_specifier() { |
|
468 return "%lld"; |
|
469 } |
|
470 |
|
471 jboolean |
|
472 IsJavaw() |
|
473 { |
|
474 /* noop on UNIX */ |
|
475 return JNI_FALSE; |
|
476 } |
|
477 |
|
478 void |
|
479 InitLauncher(jboolean javaw) |
|
480 { |
|
481 JLI_SetTraceLauncher(); |
|
482 } |
|
483 |
|
484 /* |
|
485 * The implementation for finding classes from the bootstrap |
|
486 * class loader, refer to java.h |
|
487 */ |
|
488 static FindClassFromBootLoader_t *findBootClass = NULL; |
|
489 |
|
490 jclass |
|
491 FindBootStrapClass(JNIEnv *env, const char* classname) |
|
492 { |
|
493 if (findBootClass == NULL) { |
|
494 findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT, |
|
495 "JVM_FindClassFromBootLoader"); |
|
496 if (findBootClass == NULL) { |
|
497 JLI_ReportErrorMessage(DLL_ERROR4, |
|
498 "JVM_FindClassFromBootLoader"); |
|
499 return NULL; |
|
500 } |
|
501 } |
|
502 return findBootClass(env, classname); |
|
503 } |
|
504 |