java_md_common.c revision 5270
0N/A/*
1879N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
1472N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1472N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
0N/A */
1879N/A#include "java.h"
1879N/A
1879N/A/*
1879N/A * If app is "/foo/bin/javac", or "/foo/bin/sparcv9/javac" then put
1879N/A * "/foo" into buf.
1879N/A */
1879N/Ajboolean
0N/AGetApplicationHome(char *buf, jint bufsize)
0N/A{
0N/A const char *execname = GetExecName();
0N/A if (execname != NULL) {
0N/A JLI_Snprintf(buf, bufsize, "%s", execname);
0N/A buf[bufsize-1] = '\0';
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A if (JLI_StrRChr(buf, '/') == 0) {
3863N/A buf[0] = '\0';
0N/A return JNI_FALSE;
0N/A }
0N/A *(JLI_StrRChr(buf, '/')) = '\0'; /* executable file */
0N/A if (JLI_StrLen(buf) < 4 || JLI_StrRChr(buf, '/') == 0) {
0N/A buf[0] = '\0';
0N/A return JNI_FALSE;
0N/A }
0N/A if (JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0)
0N/A *(JLI_StrRChr(buf, '/')) = '\0'; /* sparcv9 or amd64 */
0N/A if (JLI_StrLen(buf) < 4 || JLI_StrCmp("/bin", buf + JLI_StrLen(buf) - 4) != 0) {
0N/A buf[0] = '\0';
0N/A return JNI_FALSE;
0N/A }
0N/A *(JLI_StrRChr(buf, '/')) = '\0'; /* bin */
0N/A
0N/A return JNI_TRUE;
0N/A}
0N/A/*
0N/A * Return true if the named program exists
0N/A */
1089N/Astatic int
1089N/AProgramExists(char *name)
1089N/A{
0N/A struct stat sb;
0N/A if (stat(name, &sb) != 0) return 0;
0N/A if (S_ISDIR(sb.st_mode)) return 0;
0N/A return (sb.st_mode & S_IEXEC) != 0;
0N/A}
0N/A
0N/A/*
0N/A * Find a command in a directory, returning the path.
0N/A */
0N/Astatic char *
0N/AResolve(char *indir, char *cmd)
0N/A{
0N/A char name[PATH_MAX + 2], *real;
0N/A
0N/A if ((JLI_StrLen(indir) + JLI_StrLen(cmd) + 1) > PATH_MAX) return 0;
0N/A JLI_Snprintf(name, sizeof(name), "%s%c%s", indir, FILE_SEPARATOR, cmd);
0N/A if (!ProgramExists(name)) return 0;
0N/A real = JLI_MemAlloc(PATH_MAX + 2);
0N/A if (!realpath(name, real))
0N/A JLI_StrCpy(real, name);
0N/A return real;
0N/A}
0N/A
0N/A/*
0N/A * Find a path for the executable
0N/A */
0N/Achar *
0N/AFindExecName(char *program)
0N/A{
0N/A char cwdbuf[PATH_MAX+2];
1089N/A char *path;
1089N/A char *tmp_path;
0N/A char *f;
0N/A char *result = NULL;
0N/A
0N/A /* absolute path? */
0N/A if (*program == FILE_SEPARATOR ||
0N/A (FILE_SEPARATOR=='\\' && JLI_StrRChr(program, ':')))
0N/A return Resolve("", program+1);
0N/A
0N/A /* relative path? */
0N/A if (JLI_StrRChr(program, FILE_SEPARATOR) != 0) {
0N/A char buf[PATH_MAX+2];
0N/A return Resolve(getcwd(cwdbuf, sizeof(cwdbuf)), program);
3127N/A }
0N/A
0N/A /* from search path? */
0N/A path = getenv("PATH");
0N/A if (!path || !*path) path = ".";
0N/A tmp_path = JLI_MemAlloc(JLI_StrLen(path) + 2);
0N/A JLI_StrCpy(tmp_path, path);
0N/A
0N/A for (f=tmp_path; *f && result==0; ) {
0N/A char *s = f;
0N/A while (*f && (*f != PATH_SEPARATOR)) ++f;
0N/A if (*f) *f++ = 0;
0N/A if (*s == FILE_SEPARATOR)
0N/A result = Resolve(s, program);
0N/A else {
0N/A /* relative path element */
0N/A char dir[2*PATH_MAX];
0N/A JLI_Snprintf(dir, sizeof(dir), "%s%c%s", getcwd(cwdbuf, sizeof(cwdbuf)),
0N/A FILE_SEPARATOR, s);
0N/A result = Resolve(dir, program);
0N/A }
0N/A if (result != 0) break;
0N/A }
0N/A
0N/A JLI_MemFree(tmp_path);
0N/A return result;
0N/A}
0N/A
0N/Avoid JLI_ReportErrorMessage(const char* fmt, ...) {
0N/A va_list vl;
1623N/A va_start(vl, fmt);
1623N/A vfprintf(stderr, fmt, vl);
1623N/A fprintf(stderr, "\n");
0N/A va_end(vl);
0N/A}
0N/A
0N/Avoid JLI_ReportErrorMessageSys(const char* fmt, ...) {
0N/A va_list vl;
0N/A char *emsg;
0N/A
0N/A /*
0N/A * TODO: its safer to use strerror_r but is not available on
0N/A * Solaris 8. Until then....
0N/A */
0N/A emsg = strerror(errno);
1623N/A if (emsg != NULL) {
0N/A fprintf(stderr, "%s\n", emsg);
0N/A }
0N/A
0N/A va_start(vl, fmt);
0N/A vfprintf(stderr, fmt, vl);
0N/A fprintf(stderr, "\n");
0N/A va_end(vl);
0N/A}
0N/A
1623N/Avoid JLI_ReportExceptionDescription(JNIEnv * env) {
1623N/A (*env)->ExceptionDescribe(env);
0N/A}
2453N/A
0N/A/*
0N/A * Since using the file system as a registry is a bit risky, perform
0N/A * additional sanity checks on the identified directory to validate
0N/A * it as a valid jre/sdk.
0N/A *
0N/A * Return 0 if the tests fail; otherwise return non-zero (true).
0N/A *
0N/A * Note that checking for anything more than the existence of an
0N/A * executable object at bin/java relative to the path being checked
0N/A * will break the regression tests.
0N/A */
0N/Astatic int
1623N/ACheckSanity(char *path, char *dir)
1623N/A{
1623N/A char buffer[PATH_MAX];
2453N/A
0N/A if (JLI_StrLen(path) + JLI_StrLen(dir) + 11 > PATH_MAX)
0N/A return (0); /* Silently reject "impossibly" long paths */
1623N/A
1623N/A JLI_Snprintf(buffer, sizeof(buffer), "%s/%s/bin/java", path, dir);
1623N/A return ((access(buffer, X_OK) == 0) ? 1 : 0);
1623N/A}
0N/A
2453N/A/*
2453N/A * Determine if there is an acceptable JRE in the directory dirname.
0N/A * Upon locating the "best" one, return a fully qualified path to
0N/A * it. "Best" is defined as the most advanced JRE meeting the
0N/A * constraints contained in the manifest_info. If no JRE in this
0N/A * directory meets the constraints, return NULL.
0N/A *
0N/A * Note that we don't check for errors in reading the directory
0N/A * (which would be done by checking errno). This is because it
0N/A * doesn't matter if we get an error reading the directory, or
0N/A * we just don't find anything interesting in the directory. We
0N/A * just return NULL in either case.
0N/A *
0N/A * The historical names of j2sdk and j2re were changed to jdk and
0N/A * jre respecively as part of the 1.5 rebranding effort. Since the
0N/A * former names are legacy on Linux, they must be recognized for
0N/A * all time. Fortunately, this is a minor cost.
0N/A */
0N/Astatic char
0N/A*ProcessDir(manifest_info *info, char *dirname)
0N/A{
0N/A DIR *dirp;
0N/A struct dirent *dp;
0N/A char *best = NULL;
0N/A int offset;
0N/A int best_offset = 0;
0N/A char *ret_str = NULL;
0N/A char buffer[PATH_MAX];
0N/A
0N/A if ((dirp = opendir(dirname)) == NULL)
0N/A return (NULL);
0N/A
0N/A do {
0N/A if ((dp = readdir(dirp)) != NULL) {
0N/A offset = 0;
0N/A if ((JLI_StrNCmp(dp->d_name, "jre", 3) == 0) ||
0N/A (JLI_StrNCmp(dp->d_name, "jdk", 3) == 0))
0N/A offset = 3;
0N/A else if (JLI_StrNCmp(dp->d_name, "j2re", 4) == 0)
0N/A offset = 4;
0N/A else if (JLI_StrNCmp(dp->d_name, "j2sdk", 5) == 0)
0N/A offset = 5;
0N/A if (offset > 0) {
0N/A if ((JLI_AcceptableRelease(dp->d_name + offset,
0N/A info->jre_version)) && CheckSanity(dirname, dp->d_name))
0N/A if ((best == NULL) || (JLI_ExactVersionId(
0N/A dp->d_name + offset, best + best_offset) > 0)) {
0N/A if (best != NULL)
0N/A JLI_MemFree(best);
0N/A best = JLI_StringDup(dp->d_name);
0N/A best_offset = offset;
0N/A }
0N/A }
0N/A }
0N/A } while (dp != NULL);
0N/A (void) closedir(dirp);
0N/A if (best == NULL)
0N/A return (NULL);
0N/A else {
0N/A ret_str = JLI_MemAlloc(JLI_StrLen(dirname) + JLI_StrLen(best) + 2);
0N/A sprintf(ret_str, "%s/%s", dirname, best);
0N/A JLI_MemFree(best);
0N/A return (ret_str);
0N/A }
0N/A}
1089N/A
1089N/A/*
1089N/A * This is the global entry point. It examines the host for the optimal
1089N/A * JRE to be used by scanning a set of directories. The set of directories
1089N/A * is platform dependent and can be overridden by the environment
1089N/A * variable JAVA_VERSION_PATH.
1089N/A *
1089N/A * This routine itself simply determines the set of appropriate
1089N/A * directories before passing control onto ProcessDir().
1089N/A */
1089N/Achar*
1089N/ALocateJRE(manifest_info* info)
1089N/A{
1089N/A char *path;
1089N/A char *home;
1089N/A char *target = NULL;
1089N/A char *dp;
1089N/A char *cp;
1879N/A
1879N/A /*
* 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);
}
StdArg
*JLI_GetStdArgs()
{
return NULL;
}
int
JLI_GetStdArgc() {
return 0;
}
jobjectArray
CreateApplicationArgs(JNIEnv *env, char **strv, int argc)
{
return NewPlatformStringArray(env, strv, argc);
}