diff -r 378aa3362868 -r 320a714614e9 jdk/src/solaris/bin/java_md_common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/solaris/bin/java_md_common.c Tue Mar 06 20:34:38 2012 +0000 @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include "java.h" + +/* + * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put + * "/foo" into buf. + */ +jboolean +GetApplicationHome(char *buf, jint bufsize) +{ + const char *execname = GetExecName(); + if (execname != NULL) { + JLI_Snprintf(buf, bufsize, "%s", execname); + buf[bufsize-1] = '\0'; + } else { + return JNI_FALSE; + } + + if (JLI_StrRChr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(JLI_StrRChr(buf, '/')) = '\0'; /* executable file */ + if (JLI_StrLen(buf) < 4 || JLI_StrRChr(buf, '/') == 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + if (JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) + *(JLI_StrRChr(buf, '/')) = '\0'; /* sparcv9 or amd64 */ + if (JLI_StrLen(buf) < 4 || JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) { + buf[0] = '\0'; + return JNI_FALSE; + } + *(JLI_StrRChr(buf, '/')) = '\0'; /* bin */ + + return JNI_TRUE; +} +/* + * Return true if the named program exists + */ +static int +ProgramExists(char *name) +{ + struct stat sb; + if (stat(name, &sb) != 0) return 0; + if (S_ISDIR(sb.st_mode)) return 0; + return (sb.st_mode & S_IEXEC) != 0; +} + +/* + * Find a command in a directory, returning the path. + */ +static char * +Resolve(char *indir, char *cmd) +{ + char name[PATH_MAX + 2], *real; + + if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1) > PATH_MAX) return 0; + JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd); + if (!ProgramExists(name)) return 0; + real = JLI_MemAlloc(PATH_MAX + 2); + if (!realpath(name, real)) + JLI_StrCpy(real, name); + return real; +} + +/* + * Find a path for the executable + */ +char * +FindExecName(char *program) +{ + char cwdbuf[PATH_MAX+2]; + char *path; + char *tmp_path; + char *f; + char *result = NULL; + + /* absolute path? */ + if (*program == FILE_SEPARATOR || + (FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':'))) + return Resolve("", program+1); + + /* relative path? */ + if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) { + char buf[PATH_MAX+2]; + return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program); + } + + /* from search path? */ + path = getenv("PATH"); + if (!path || !*path) path = "."; + tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2); + JLI_StrCpy(tmp_path, path); + + for (f=tmp_path; *f && result==0; ) { + char *s = f; + while (*f && (*f != PATH_SEPARATOR)) ++f; + if (*f) *f++ = 0; + if (*s == FILE_SEPARATOR) + result = Resolve(s, program); + else { + /* relative path element */ + char dir[2*PATH_MAX]; + JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)), + FILE_SEPARATOR, s); + result = Resolve(dir, program); + } + if (result != 0) break; + } + + JLI_MemFree(tmp_path); + return result; +} + +void JLI_ReportErrorMessage(const char* fmt, ...) { + va_list vl; + va_start(vl, fmt); + vfprintf(stderr, fmt, vl); + fprintf(stderr, "\n"); + va_end(vl); +} + +void JLI_ReportErrorMessageSys(const char* fmt, ...) { + va_list vl; + char *emsg; + + /* + * TODO: its safer to use strerror_r but is not available on + * Solaris 8. Until then.... + */ + emsg = strerror(errno); + if (emsg != NULL) { + fprintf(stderr, "%s\n", emsg); + } + + va_start(vl, fmt); + vfprintf(stderr, fmt, vl); + fprintf(stderr, "\n"); + va_end(vl); +} + +void JLI_ReportExceptionDescription(JNIEnv * env) { + (*env)->ExceptionDescribe(env); +} + +/* + * Since using the file system as a registry is a bit risky, perform + * additional sanity checks on the identified directory to validate + * it as a valid jre/sdk. + * + * Return 0 if the tests fail; otherwise return non-zero (true). + * + * Note that checking for anything more than the existence of an + * executable object at bin/java relative to the path being checked + * will break the regression tests. + */ +static int +CheckSanity(char *path, char *dir) +{ + char buffer[PATH_MAX]; + + if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX) + return (0); /* Silently reject "impossibly" long paths */ + + JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir); + return ((access(buffer, X_OK) == 0) ? 1 : 0); +} + +/* + * Determine if there is an acceptable JRE in the directory dirname. + * Upon locating the "best" one, return a fully qualified path to + * it. "Best" is defined as the most advanced JRE meeting the + * constraints contained in the manifest_info. If no JRE in this + * directory meets the constraints, return NULL. + * + * Note that we don't check for errors in reading the directory + * (which would be done by checking errno). This is because it + * doesn't matter if we get an error reading the directory, or + * we just don't find anything interesting in the directory. We + * just return NULL in either case. + * + * The historical names of j2sdk and j2re were changed to jdk and + * jre respecively as part of the 1.5 rebranding effort. Since the + * former names are legacy on Linux, they must be recognized for + * all time. Fortunately, this is a minor cost. + */ +static char +*ProcessDir(manifest_info *info, char *dirname) +{ + DIR *dirp; + struct dirent *dp; + char *best = NULL; + int offset; + int best_offset = 0; + char *ret_str = NULL; + char buffer[PATH_MAX]; + + if ((dirp = opendir(dirname)) == NULL) + return (NULL); + + do { + if ((dp = readdir(dirp)) != NULL) { + offset = 0; + if ((JLI_StrNCmp(dp->d_name, "jre", 3) == 0) || + (JLI_StrNCmp(dp->d_name, "jdk", 3) == 0)) + offset = 3; + else if (JLI_StrNCmp(dp->d_name, "j2re", 4) == 0) + offset = 4; + else if (JLI_StrNCmp(dp->d_name, "j2sdk", 5) == 0) + offset = 5; + if (offset > 0) { + if ((JLI_AcceptableRelease(dp->d_name + offset, + info->jre_version)) && CheckSanity(dirname, dp->d_name)) + if ((best == NULL) || (JLI_ExactVersionId( + dp->d_name + offset, best + best_offset) > 0)) { + if (best != NULL) + JLI_MemFree(best); + best = JLI_StringDup(dp->d_name); + best_offset = offset; + } + } + } + } while (dp != NULL); + (void) closedir(dirp); + if (best == NULL) + return (NULL); + else { + ret_str = JLI_MemAlloc(JLI_StrLen(dirname) + JLI_StrLen(best) + 2); + sprintf(ret_str, "%s/%s", dirname, best); + JLI_MemFree(best); + return (ret_str); + } +} + +/* + * This is the global entry point. It examines the host for the optimal + * JRE to be used by scanning a set of directories. The set of directories + * is platform dependent and can be overridden by the environment + * variable JAVA_VERSION_PATH. + * + * This routine itself simply determines the set of appropriate + * directories before passing control onto ProcessDir(). + */ +char* +LocateJRE(manifest_info* info) +{ + char *path; + char *home; + char *target = NULL; + char *dp; + char *cp; + + /* + * Start by getting JAVA_VERSION_PATH + */ + if (info->jre_restrict_search) { + path = JLI_StringDup(system_dir); + } else if ((path = getenv("JAVA_VERSION_PATH")) != NULL) { + path = JLI_StringDup(path); + } else { + if ((home = getenv("HOME")) != NULL) { + path = (char *)JLI_MemAlloc(JLI_StrLen(home) + \ + JLI_StrLen(system_dir) + JLI_StrLen(user_dir) + 2); + sprintf(path, "%s%s:%s", home, user_dir, system_dir); + } else { + path = JLI_StringDup(system_dir); + } + } + + /* + * Step through each directory on the path. Terminate the scan with + * the first directory with an acceptable JRE. + */ + cp = dp = path; + while (dp != NULL) { + cp = JLI_StrChr(dp, (int)':'); + if (cp != NULL) + *cp = '\0'; + if ((target = ProcessDir(info, dp)) != NULL) + break; + dp = cp; + if (dp != NULL) + dp++; + } + JLI_MemFree(path); + return (target); +} + +/* + * Given a path to a jre to execute, this routine checks if this process + * is indeed that jre. If not, it exec's that jre. + * + * We want to actually check the paths rather than just the version string + * built into the executable, so that given version specification (and + * JAVA_VERSION_PATH) will yield the exact same Java environment, regardless + * of the version of the arbitrary launcher we start with. + */ +void +ExecJRE(char *jre, char **argv) +{ + char wanted[PATH_MAX]; + const char* progname = GetProgramName(); + const char* execname = NULL; + + /* + * Resolve the real path to the directory containing the selected JRE. + */ + if (realpath(jre, wanted) == NULL) { + JLI_ReportErrorMessage(JRE_ERROR9, jre); + exit(1); + } + + /* + * Resolve the real path to the currently running launcher. + */ + SetExecname(argv); + execname = GetExecName(); + if (execname == NULL) { + JLI_ReportErrorMessage(JRE_ERROR10); + exit(1); + } + + /* + * If the path to the selected JRE directory is a match to the initial + * portion of the path to the currently executing JRE, we have a winner! + * If so, just return. + */ + if (JLI_StrNCmp(wanted, execname, JLI_StrLen(wanted)) == 0) + return; /* I am the droid you were looking for */ + + + /* + * This should never happen (because of the selection code in SelectJRE), + * but check for "impossibly" long path names just because buffer overruns + * can be so deadly. + */ + if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) { + JLI_ReportErrorMessage(JRE_ERROR11); + exit(1); + } + + /* + * Construct the path and exec it. + */ + (void)JLI_StrCat(JLI_StrCat(wanted, "/bin/"), progname); + argv[0] = JLI_StringDup(progname); + if (JLI_IsTraceLauncher()) { + int i; + printf("ReExec Command: %s (%s)\n", wanted, argv[0]); + printf("ReExec Args:"); + for (i = 1; argv[i] != NULL; i++) + printf(" %s", argv[i]); + printf("\n"); + } + JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n"); + (void)fflush(stdout); + (void)fflush(stderr); + execv(wanted, argv); + JLI_ReportErrorMessageSys(JRE_ERROR12, wanted); + exit(1); +} + +/* + * "Borrowed" from Solaris 10 where the unsetenv() function is being added + * to libc thanks to SUSv3 (Standard Unix Specification, version 3). As + * such, in the fullness of time this will appear in libc on all relevant + * Solaris/Linux platforms and maybe even the Windows platform. At that + * time, this stub can be removed. + * + * This implementation removes the environment locking for multithreaded + * applications. (We don't have access to these mutexes within libc and + * the launcher isn't multithreaded.) Note that what remains is platform + * independent, because it only relies on attributes that a POSIX environment + * defines. + * + * Returns 0 on success, -1 on failure. + * + * Also removed was the setting of errno. The only value of errno set + * was EINVAL ("Invalid Argument"). + */ + +/* + * s1(environ) is name=value + * s2(name) is name(not the form of name=value). + * if names match, return value of 1, else return 0 + */ +static int +match_noeq(const char *s1, const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '=') + return (1); + } + if (*s1 == '=' && s2[-1] == '\0') + return (1); + return (0); +} + +/* + * added for SUSv3 standard + * + * Delete entry from environ. + * Do not free() memory! Other threads may be using it. + * Keep it around forever. + */ +static int +borrowed_unsetenv(const char *name) +{ + long idx; /* index into environ */ + + if (name == NULL || *name == '\0' || + JLI_StrChr(name, '=') != NULL) { + return (-1); + } + + for (idx = 0; environ[idx] != NULL; idx++) { + if (match_noeq(environ[idx], name)) + break; + } + if (environ[idx] == NULL) { + /* name not found but still a success */ + return (0); + } + /* squeeze up one entry */ + do { + environ[idx] = environ[idx+1]; + } while (environ[++idx] != NULL); + + return (0); +} +/* --- End of "borrowed" code --- */ + +/* + * Wrapper for unsetenv() function. + */ +int +UnsetEnv(char *name) +{ + return(borrowed_unsetenv(name)); +} + +const char * +jlong_format_specifier() { + return "%lld"; +} + +jboolean +IsJavaw() +{ + /* noop on UNIX */ + return JNI_FALSE; +} + +void +InitLauncher(jboolean javaw) +{ + JLI_SetTraceLauncher(); +} + +/* + * The implementation for finding classes from the bootstrap + * class loader, refer to java.h + */ +static FindClassFromBootLoader_t *findBootClass = NULL; + +jclass +FindBootStrapClass(JNIEnv *env, const char* classname) +{ + if (findBootClass == NULL) { + findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT, + "JVM_FindClassFromBootLoader"); + if (findBootClass == NULL) { + JLI_ReportErrorMessage(DLL_ERROR4, + "JVM_FindClassFromBootLoader"); + return NULL; + } + } + return findBootClass(env, classname); +} +