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 505 StdArg 506 *JLI_GetStdArgs() 507 { 508 return NULL; 509 } 510 511 int 512 JLI_GetStdArgc() { 513 return 0; 514 } 515 516 jobjectArray 517 CreateApplicationArgs(JNIEnv *env, char **strv, int argc) 518 { 519 return NewPlatformStringArray(env, strv, argc); 520 }