7029048: (launcher) fence the launcher against LD_LIBRARY_PATH
Reviewed-by: mchung, ohair
--- a/jdk/src/share/bin/jli_util.h Thu Apr 07 11:25:09 2011 -0400
+++ b/jdk/src/share/bin/jli_util.h Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2011, 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
@@ -35,7 +35,6 @@
void JLI_MemFree(void *ptr);
int JLI_StrCCmp(const char *s1, const char* s2);
-
#define JLI_StrLen(p1) strlen((p1))
#define JLI_StrChr(p1, p2) strchr((p1), (p2))
#define JLI_StrRChr(p1, p2) strrchr((p1), (p2))
@@ -48,6 +47,7 @@
#define JLI_StrSpn(p1, p2) strspn((p1), (p2))
#define JLI_StrCSpn(p1, p2) strcspn((p1), (p2))
#define JLI_StrPBrk(p1, p2) strpbrk((p1), (p2))
+#define JLI_StrTok(p1, p2) strtok((p1), (p2))
/* On Windows lseek() is in io.h rather than the location dictated by POSIX. */
#ifdef _WIN32
--- a/jdk/src/solaris/bin/java_md.c Thu Apr 07 11:25:09 2011 -0400
+++ b/jdk/src/solaris/bin/java_md.c Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2011, 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
@@ -46,6 +46,10 @@
#define JVM_DLL "libjvm.so"
#define JAVA_DLL "libjava.so"
+/* help jettison the LD_LIBRARY_PATH settings in the future */
+#ifndef SETENV_REQUIRED
+#define SETENV_REQUIRED
+#endif
/*
* If a processor / os combination has the ability to run binaries of
* two data models and cohabitation of jre/jdk bits with both data
@@ -106,10 +110,22 @@
* Previously the launcher modified the LD_LIBRARY_PATH appropriately for the
* desired data model path, regardless if data models matched or not. The
* launcher subsequently exec'ed the desired executable, in order to make the
- * LD_LIBRARY_PATH path available for the runtime linker. This is no longer the
- * case, the launcher dlopens the target libjvm.so. All other required
- * libraries are loaded by the runtime linker, by virtue of the $ORIGIN paths
- * baked into the shared libraries, by the build infrastructure at compile time.
+ * LD_LIBRARY_PATH path available, for the runtime linker.
+ *
+ * Now, in most cases,the launcher will dlopen the target libjvm.so. All
+ * required libraries are loaded by the runtime linker, using the
+ * $RPATH/$ORIGIN baked into the shared libraries at compile time. Therefore,
+ * in most cases, the launcher will only exec, if the data models are
+ * mismatched, and will not set any environment variables, regardless of the
+ * data models.
+ *
+ * However, if the environment contains a LD_LIBRARY_PATH, this will cause the
+ * launcher to inspect the LD_LIBRARY_PATH. The launcher will check
+ * a. if the LD_LIBRARY_PATH's first component is the the path to the desired
+ * libjvm.so
+ * b. if any other libjvm.so is found in any of the paths.
+ * If case b is true, then the launcher will set the LD_LIBRARY_PATH to the
+ * desired JRE and reexec, in order to propagate the environment.
*
* Main
* (incoming argv)
@@ -137,11 +153,11 @@
* | |
* | |
* \|/ \|/
- * YES (find the desired executable and exec child)
+ * YES Find the desired executable/library
* | |
* | |
* \|/ \|/
- * CheckJvmType Main
+ * CheckJvmType RequiresSetenv
* (removes -client, -server, etc.)
* |
* |
@@ -156,7 +172,42 @@
* processes version options,
* creates argument list for vm,
* etc.)
- *
+ * |
+ * |
+ * \|/
+ * RequiresSetenv
+ * Is LD_LIBRARY_PATH
+ * and friends set ? --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ * YES YES --> Continue
+ * |
+ * |
+ * \|/
+ * Path is desired JRE ? YES --> Have Desired Model ? NO --> Re-exec --> Main
+ * NO YES --> Continue
+ * |
+ * |
+ * \|/
+ * Paths have well known
+ * jvm paths ? --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ * YES YES --> Continue
+ * |
+ * |
+ * \|/
+ * Does libjvm.so exit
+ * in any of them ? --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ * YES YES --> Continue
+ * |
+ * |
+ * \|/
+ * Set the LD_LIBRARY_PATH
+ * |
+ * |
+ * \|/
+ * Re-exec
+ * |
+ * |
+ * \|/
+ * Main
*/
static const char * SetExecname(char **argv);
@@ -182,6 +233,130 @@
}
}
+#ifdef SETENV_REQUIRED
+static jboolean
+JvmExists(const char *path) {
+ char tmp[PATH_MAX + 1];
+ struct stat statbuf;
+ JLI_Snprintf(tmp, PATH_MAX, "%s/%s", path, JVM_DLL);
+ if (stat(tmp, &statbuf) == 0) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+/*
+ * contains a lib/$LIBARCH/{server,client}/libjvm.so ?
+ */
+static jboolean
+ContainsLibJVM(int wanted, const char *env) {
+ char clientPattern[PATH_MAX + 1];
+ char serverPattern[PATH_MAX + 1];
+ char *envpath;
+ char *path;
+ jboolean clientPatternFound;
+ jboolean serverPatternFound;
+
+ /* fastest path */
+ if (env == NULL) {
+ return JNI_FALSE;
+ }
+
+ /* the usual suspects */
+ JLI_Snprintf(clientPattern, PATH_MAX, "lib/%s/client", GetArchPath(wanted));
+ JLI_Snprintf(serverPattern, PATH_MAX, "lib/%s/server", GetArchPath(wanted));
+
+ /* to optimize for time, test if any of our usual suspects are present. */
+ clientPatternFound = JLI_StrStr(env, clientPattern) != NULL;
+ serverPatternFound = JLI_StrStr(env, serverPattern) != NULL;
+ if (clientPatternFound == JNI_FALSE && serverPatternFound == JNI_FALSE) {
+ return JNI_FALSE;
+ }
+
+ /*
+ * we have a suspicious path component, check if it contains a libjvm.so
+ */
+ envpath = JLI_StringDup(env);
+ for (path = JLI_StrTok(envpath, ":"); path != NULL; path = JLI_StrTok(NULL, ":")) {
+ if (clientPatternFound && JLI_StrStr(path, clientPattern) != NULL) {
+ if (JvmExists(path)) {
+ JLI_MemFree(envpath);
+ return JNI_TRUE;
+ }
+ }
+ if (serverPatternFound && JLI_StrStr(path, serverPattern) != NULL) {
+ if (JvmExists(path)) {
+ JLI_MemFree(envpath);
+ return JNI_TRUE;
+ }
+ }
+ }
+ JLI_MemFree(envpath);
+ return JNI_FALSE;
+}
+
+/*
+ * Test whether the environment variable needs to be set, see flowchart.
+ */
+static jboolean
+RequiresSetenv(int wanted, const char *jvmpath) {
+ char jpath[PATH_MAX + 1];
+ char *llp;
+ char *dmllp = NULL;
+ char *p; /* a utility pointer */
+
+ llp = getenv("LD_LIBRARY_PATH");
+#ifdef __solaris__
+ dmllp = (CURRENT_DATA_MODEL == 32)
+ ? getenv("LD_LIBRARY_PATH_32")
+ : getenv("LD_LIBRARY_PATH_64");
+#endif /* __solaris__ */
+ /* no environment variable is a good environment variable */
+ if (llp == NULL && dmllp == NULL) {
+ return JNI_FALSE;
+ }
+#ifdef __linux
+ /*
+ * On linux, if a binary is running as sgid or suid, glibc sets
+ * LD_LIBRARY_PATH to the empty string for security purposes. (In contrast,
+ * on Solaris the LD_LIBRARY_PATH variable for a privileged binary does not
+ * lose its settings; but the dynamic linker does apply more scrutiny to the
+ * path.) The launcher uses the value of LD_LIBRARY_PATH to prevent an exec
+ * loop, here and further downstream. Therefore, if we are running sgid or
+ * suid, this function's setting of LD_LIBRARY_PATH will be ineffective and
+ * we should case a return from the calling function. Getting the right
+ * libraries will be handled by the RPATH. In reality, this check is
+ * redundant, as the previous check for a non-null LD_LIBRARY_PATH will
+ * return back to the calling function forthwith, it is left here to safe
+ * guard against any changes, in the glibc's existing security policy.
+ */
+ if ((getgid() != getegid()) || (getuid() != geteuid())) {
+ return JNI_FALSE;
+ }
+#endif /* __linux */
+
+ /*
+ * Prevent recursions. Since LD_LIBRARY_PATH is the one which will be set by
+ * previous versions of the JRE, thus it is the only path that matters here.
+ * So we check to see if the desired JRE is set.
+ */
+ JLI_StrNCpy(jpath, jvmpath, PATH_MAX);
+ p = JLI_StrRChr(jpath, '/');
+ *p = '\0';
+ if (llp != NULL && JLI_StrNCmp(llp, jpath, JLI_StrLen(jpath)) == 0) {
+ return JNI_FALSE;
+ }
+
+ /* scrutinize all the paths further */
+ if (llp != NULL && ContainsLibJVM(wanted, llp)) {
+ return JNI_TRUE;
+ }
+ if (dmllp != NULL && ContainsLibJVM(wanted, dmllp)) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+#endif /* SETENV_REQUIRED */
+
void
CreateExecutionEnvironment(int *pargc, char ***pargv,
char jrepath[], jint so_jrepath,
@@ -195,7 +370,6 @@
* informative to issue an error message based on whether or not the
* os/processor combination has dual mode capabilities.
*/
-
jboolean jvmpathExists;
/* Compute/set the name of the executable */
@@ -207,13 +381,24 @@
char * jvmtype = NULL;
int argc = *pargc;
char **argv = *pargv;
-
int running = CURRENT_DATA_MODEL;
int wanted = running; /* What data mode is being
asked for? Current model is
fine unless another model
is asked for */
+#ifdef SETENV_REQUIRED
+ jboolean mustsetenv = JNI_FALSE;
+ char *runpath = NULL; /* existing effective LD_LIBRARY_PATH setting */
+ char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */
+ char* newpath = NULL; /* path on new LD_LIBRARY_PATH */
+ char* lastslash = NULL;
+ char** newenvp = NULL; /* current environment */
+#ifdef __solaris__
+ char* dmpath = NULL; /* data model specific LD_LIBRARY_PATH,
+ Solaris only */
+#endif /* __solaris__ */
+#endif /* SETENV_REQUIRED */
char** newargv = NULL;
int newargc = 0;
@@ -300,9 +485,18 @@
}
/*
* we seem to have everything we need, so without further ado
- * we return back.
+ * we return back, otherwise proceed to set the environment.
*/
+#ifdef SETENV_REQUIRED
+ mustsetenv = RequiresSetenv(wanted, jvmpath);
+ JLI_TraceLauncher("mustsetenv: %s\n", mustsetenv ? "TRUE" : "FALSE");
+
+ if (mustsetenv == JNI_FALSE) {
+ return;
+ }
+#else
return;
+#endif /* SETENV_REQUIRED */
} else { /* do the same speculatively or exit */
#ifdef DUAL_MODE
if (running != wanted) {
@@ -331,67 +525,240 @@
/* exec child can do error checking on the existence of the path */
jvmpathExists = GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, GetArchPath(wanted));
-
+#ifdef SETENV_REQUIRED
+ mustsetenv = RequiresSetenv(wanted, jvmpath);
+#endif /* SETENV_REQUIRED */
}
#else
JLI_ReportErrorMessage(JRE_ERROR2, wanted);
exit(1);
#endif
- }
+ }
+#ifdef SETENV_REQUIRED
+ if (mustsetenv) {
+ /*
+ * We will set the LD_LIBRARY_PATH as follows:
+ *
+ * o $JVMPATH (directory portion only)
+ * o $JRE/lib/$LIBARCHNAME
+ * o $JRE/../lib/$LIBARCHNAME
+ *
+ * followed by the user's previous effective LD_LIBRARY_PATH, if
+ * any.
+ */
+
+#ifdef __solaris__
+ /*
+ * Starting in Solaris 7, ld.so.1 supports three LD_LIBRARY_PATH
+ * variables:
+ *
+ * 1. LD_LIBRARY_PATH -- used for 32 and 64 bit searches if
+ * data-model specific variables are not set.
+ *
+ * 2. LD_LIBRARY_PATH_64 -- overrides and replaces LD_LIBRARY_PATH
+ * for 64-bit binaries.
+ *
+ * 3. LD_LIBRARY_PATH_32 -- overrides and replaces LD_LIBRARY_PATH
+ * for 32-bit binaries.
+ *
+ * The vm uses LD_LIBRARY_PATH to set the java.library.path system
+ * property. To shield the vm from the complication of multiple
+ * LD_LIBRARY_PATH variables, if the appropriate data model
+ * specific variable is set, we will act as if LD_LIBRARY_PATH had
+ * the value of the data model specific variant and the data model
+ * specific variant will be unset. Note that the variable for the
+ * *wanted* data model must be used (if it is set), not simply the
+ * current running data model.
+ */
+
+ switch (wanted) {
+ case 0:
+ if (running == 32) {
+ dmpath = getenv("LD_LIBRARY_PATH_32");
+ wanted = 32;
+ } else {
+ dmpath = getenv("LD_LIBRARY_PATH_64");
+ wanted = 64;
+ }
+ break;
- {
- char *newexec = execname;
+ case 32:
+ dmpath = getenv("LD_LIBRARY_PATH_32");
+ break;
+
+ case 64:
+ dmpath = getenv("LD_LIBRARY_PATH_64");
+ break;
+
+ default:
+ JLI_ReportErrorMessage(JRE_ERROR3, __LINE__);
+ exit(1); /* unknown value in wanted */
+ break;
+ }
+
+ /*
+ * If dmpath is NULL, the relevant data model specific variable is
+ * not set and normal LD_LIBRARY_PATH should be used.
+ */
+ if (dmpath == NULL) {
+ runpath = getenv("LD_LIBRARY_PATH");
+ } else {
+ runpath = dmpath;
+ }
+#else
+ /*
+ * If not on Solaris, assume only a single LD_LIBRARY_PATH
+ * variable.
+ */
+ runpath = getenv("LD_LIBRARY_PATH");
+#endif /* __solaris__ */
+
+ /* runpath contains current effective LD_LIBRARY_PATH setting */
+
+ jvmpath = JLI_StringDup(jvmpath);
+ new_runpath = JLI_MemAlloc(((runpath != NULL) ? JLI_StrLen(runpath) : 0) +
+ 2 * JLI_StrLen(jrepath) + 2 * JLI_StrLen(arch) +
+ JLI_StrLen(jvmpath) + 52);
+ newpath = new_runpath + JLI_StrLen("LD_LIBRARY_PATH=");
+
+
+ /*
+ * Create desired LD_LIBRARY_PATH value for target data model.
+ */
+ {
+ /* remove the name of the .so from the JVM path */
+ lastslash = JLI_StrRChr(jvmpath, '/');
+ if (lastslash)
+ *lastslash = '\0';
+
+ sprintf(new_runpath, "LD_LIBRARY_PATH="
+ "%s:"
+ "%s/lib/%s:"
+ "%s/../lib/%s",
+ jvmpath,
#ifdef DUAL_MODE
- /*
- * If the data model is being changed, the path to the
- * executable must be updated accordingly; the executable name
- * and directory the executable resides in are separate. In the
- * case of 32 => 64, the new bits are assumed to reside in, e.g.
- * "olddir/LIBARCH64NAME/execname"; in the case of 64 => 32,
- * the bits are assumed to be in "olddir/../execname". For example,
- *
- * olddir/sparcv9/execname
- * olddir/amd64/execname
- *
- * for Solaris SPARC and Linux amd64, respectively.
- */
-
- if (running != wanted) {
- char *oldexec = JLI_StrCpy(JLI_MemAlloc(JLI_StrLen(execname) + 1), execname);
- char *olddir = oldexec;
- char *oldbase = JLI_StrRChr(oldexec, '/');
+ jrepath, GetArchPath(wanted),
+ jrepath, GetArchPath(wanted)
+#else
+ jrepath, arch,
+ jrepath, arch
+#endif
+ );
- newexec = JLI_MemAlloc(JLI_StrLen(execname) + 20);
- *oldbase++ = 0;
- sprintf(newexec, "%s/%s/%s", olddir,
- ((wanted==64) ? LIBARCH64NAME : ".."), oldbase);
- argv[0] = newexec;
+ /*
+ * Check to make sure that the prefix of the current path is the
+ * desired environment variable setting, though the RequiresSetenv
+ * checks if the desired runpath exists, this logic does a more
+ * comprehensive check.
+ */
+ if (runpath != NULL &&
+ JLI_StrNCmp(newpath, runpath, JLI_StrLen(newpath)) == 0 &&
+ (runpath[JLI_StrLen(newpath)] == 0 || runpath[JLI_StrLen(newpath)] == ':') &&
+ (running == wanted) /* data model does not have to be changed */
+#ifdef __solaris__
+ && (dmpath == NULL) /* data model specific variables not set */
+#endif
+ ) {
+
+ return;
+
+ }
+ }
+
+ /*
+ * Place the desired environment setting onto the prefix of
+ * LD_LIBRARY_PATH. Note that this prevents any possible infinite
+ * loop of execv() because we test for the prefix, above.
+ */
+ if (runpath != 0) {
+ JLI_StrCat(new_runpath, ":");
+ JLI_StrCat(new_runpath, runpath);
+ }
+
+ if (putenv(new_runpath) != 0) {
+ exit(1); /* problem allocating memory; LD_LIBRARY_PATH not set
+ properly */
+ }
+
+ /*
+ * Unix systems document that they look at LD_LIBRARY_PATH only
+ * once at startup, so we have to re-exec the current executable
+ * to get the changed environment variable to have an effect.
+ */
+
+#ifdef __solaris__
+ /*
+ * If dmpath is not NULL, remove the data model specific string
+ * in the environment for the exec'ed child.
+ */
+ if (dmpath != NULL)
+ (void)UnsetEnv((wanted == 32) ? "LD_LIBRARY_PATH_32" : "LD_LIBRARY_PATH_64");
+#endif
+
+ newenvp = environ;
}
-#endif
- JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
- (void)fflush(stdout);
- (void)fflush(stderr);
- execv(newexec, argv);
- JLI_ReportErrorMessageSys(JRE_ERROR4, newexec);
+#endif /* SETENV_REQUIRED */
+ {
+ char *newexec = execname;
+#ifdef DUAL_MODE
+ /*
+ * If the data model is being changed, the path to the
+ * executable must be updated accordingly; the executable name
+ * and directory the executable resides in are separate. In the
+ * case of 32 => 64, the new bits are assumed to reside in, e.g.
+ * "olddir/LIBARCH64NAME/execname"; in the case of 64 => 32,
+ * the bits are assumed to be in "olddir/../execname". For example,
+ *
+ * olddir/sparcv9/execname
+ * olddir/amd64/execname
+ *
+ * for Solaris SPARC and Linux amd64, respectively.
+ */
+
+ if (running != wanted) {
+ char *oldexec = JLI_StrCpy(JLI_MemAlloc(JLI_StrLen(execname) + 1), execname);
+ char *olddir = oldexec;
+ char *oldbase = JLI_StrRChr(oldexec, '/');
+
+
+ newexec = JLI_MemAlloc(JLI_StrLen(execname) + 20);
+ *oldbase++ = 0;
+ sprintf(newexec, "%s/%s/%s", olddir,
+ ((wanted == 64) ? LIBARCH64NAME : ".."), oldbase);
+ argv[0] = newexec;
+ }
+#endif /* DUAL_MODE */
+ JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+#ifdef SETENV_REQUIRED
+ if (mustsetenv) {
+ execve(newexec, argv, newenvp);
+ } else {
+ execv(newexec, argv);
+ }
+#else
+ execv(newexec, argv);
+#endif /* SETENV_REQUIRED */
+ JLI_ReportErrorMessageSys(JRE_ERROR4, newexec);
#ifdef DUAL_MODE
- if (running != wanted) {
- JLI_ReportErrorMessage(JRE_ERROR5, wanted, running);
-# ifdef __solaris__
-# ifdef __sparc
- JLI_ReportErrorMessage(JRE_ERROR6);
-# else
- JLI_ReportErrorMessage(JRE_ERROR7);
-# endif
+ if (running != wanted) {
+ JLI_ReportErrorMessage(JRE_ERROR5, wanted, running);
+#ifdef __solaris__
+#ifdef __sparc
+ JLI_ReportErrorMessage(JRE_ERROR6);
+#else
+ JLI_ReportErrorMessage(JRE_ERROR7);
+#endif /* __sparc */
+ }
+#endif /* __solaris__ */
+#endif /* DUAL_MODE */
+
}
-# endif
-#endif
-
- }
- exit(1);
+ exit(1);
}
-
}
/*
--- a/jdk/test/tools/launcher/ExecutionEnvironment.java Thu Apr 07 11:25:09 2011 -0400
+++ b/jdk/test/tools/launcher/ExecutionEnvironment.java Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, 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
@@ -30,7 +30,7 @@
*/
/*
- * This test tests for various things as follows:
+ * This tests for various things as follows:
* Ensures that:
* 1. uneccessary execs do not occur
* 2. the environment is pristine, users environment variable wrt.
@@ -84,7 +84,9 @@
static int errors = 0;
static int passes = 0;
- private static void createTestJar() {
+ static final String LIBJVM = TestHelper.isWindows ? "jvm.dll" : "libjvm.so";
+
+ static void createTestJar() {
try {
List<String> codeList = new ArrayList<String>();
codeList.add("static void printValue(String name, boolean property) {\n");
@@ -127,6 +129,7 @@
testJarFile.getAbsolutePath());
if (!tr.isNotZeroOutput()) {
+ System.out.println(tr);
throw new RuntimeException("Error: No output at all. Did the test execute ?");
}
@@ -177,7 +180,6 @@
Map<String, String> env = new HashMap<String, String>();
-
if (TestHelper.isLinux) {
for (String x : LD_PATH_STRINGS) {
String pairs[] = x.split("=");
@@ -209,7 +211,7 @@
verifyJavaLibraryPathOverride(tr, true);
// try changing the model from 32 to 64 bit
- if (TestHelper.java64Cmd != null && TestHelper.is32Bit) {
+ if (TestHelper.dualModePresent() && TestHelper.is32Bit) {
// verify the override occurs
env.clear();
for (String x : LD_PATH_STRINGS) {
@@ -326,7 +328,7 @@
File symLink = null;
String libPathPrefix = TestHelper.isSDK ? "jre/lib" : "/lib";
symLink = new File(TestHelper.JAVAHOME, libPathPrefix +
- TestHelper.getJreArch() + "/libjvm.so");
+ TestHelper.getJreArch() + "/" + LIBJVM);
if (symLink.exists()) {
System.out.println("FAIL: The symlink exists " +
symLink.getAbsolutePath());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/Test7029048.java Thu Apr 07 12:06:32 2011 -0700
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 7029048
+ * @summary Checks for LD_LIBRARY_PATH on *nixes
+ * @compile -XDignore.symbol.file ExecutionEnvironment.java TestHelper.java Test7029048.java
+ * @run main Test7029048
+ */
+
+/*
+ * 7029048: test for LD_LIBRARY_PATH set to different paths pointing which may
+ * contain a libjvm.so and may not, but we test to ensure that the launcher
+ * behaves correctly in all cases.
+ */
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Test7029048 {
+
+ static int passes = 0;
+ static int errors = 0;
+
+ private static final String LIBJVM = ExecutionEnvironment.LIBJVM;
+ private static final String LD_LIBRARY_PATH =
+ ExecutionEnvironment.LD_LIBRARY_PATH;
+ private static final String LD_LIBRARY_PATH_32 =
+ ExecutionEnvironment.LD_LIBRARY_PATH_32;
+ private static final String LD_LIBRARY_PATH_64 =
+ ExecutionEnvironment.LD_LIBRARY_PATH_64;
+
+ private static final File libDir =
+ new File(System.getProperty("sun.boot.library.path"));
+ private static final File srcServerDir = new File(libDir, "server");
+ private static final File srcLibjvmSo = new File(srcServerDir, LIBJVM);
+
+ private static final File dstLibDir = new File("lib");
+ private static final File dstLibArchDir =
+ new File(dstLibDir, TestHelper.getJreArch());
+
+ private static final File dstServerDir = new File(dstLibArchDir, "server");
+ private static final File dstServerLibjvm = new File(dstServerDir, LIBJVM);
+
+ private static final File dstClientDir = new File(dstLibArchDir, "client");
+ private static final File dstClientLibjvm = new File(dstClientDir, LIBJVM);
+
+ // used primarily to test the solaris variants in dual mode
+ private static final File dstOtherArchDir;
+ private static final File dstOtherServerDir;
+ private static final File dstOtherServerLibjvm;
+
+ private static final Map<String, String> env = new HashMap<>();
+
+ static {
+ if (TestHelper.isDualMode) {
+ dstOtherArchDir = new File(dstLibDir, TestHelper.getComplementaryJreArch());
+ dstOtherServerDir = new File(dstOtherArchDir, "server");
+ dstOtherServerLibjvm = new File(dstOtherServerDir, LIBJVM);
+ } else {
+ dstOtherArchDir = null;
+ dstOtherServerDir = null;
+ dstOtherServerLibjvm = null;
+ }
+ }
+
+ static String getValue(String name, List<String> in) {
+ for (String x : in) {
+ String[] s = x.split("=");
+ if (name.equals(s[0].trim())) {
+ return s[1].trim();
+ }
+ }
+ return null;
+ }
+
+ static void run(boolean want32, String dflag, Map<String, String> env,
+ int nLLPComponents, String caseID) {
+ final boolean want64 = want32 == false;
+ env.put(ExecutionEnvironment.JLDEBUG_KEY, "true");
+ List<String> cmdsList = new ArrayList<>();
+
+ // only for a dual-mode system
+ if (want64 && TestHelper.isDualMode) {
+ cmdsList.add(TestHelper.java64Cmd);
+ } else {
+ cmdsList.add(TestHelper.javaCmd); // a 32-bit java command for all
+ }
+
+ /*
+ * empty or null strings can confuse the ProcessBuilder. A null flag
+ * indicates that the appropriate data model is enforced on the chosen
+ * launcher variant.
+ */
+
+ if (dflag != null) {
+ cmdsList.add(dflag);
+ } else {
+ cmdsList.add(want32 ? "-d32" : "-d64");
+ }
+ cmdsList.add("-server");
+ cmdsList.add("-jar");
+ cmdsList.add(ExecutionEnvironment.testJarFile.getAbsolutePath());
+ String[] cmds = new String[cmdsList.size()];
+ TestHelper.TestResult tr = TestHelper.doExec(env, cmdsList.toArray(cmds));
+ analyze(tr, nLLPComponents, caseID);
+ }
+
+ // no cross launch, ie. no change to the data model.
+ static void run(Map<String, String> env, int nLLPComponents, String caseID)
+ throws IOException {
+ boolean want32 = TestHelper.is32Bit;
+ run(want32, null, env, nLLPComponents, caseID);
+ }
+
+ static void analyze(TestHelper.TestResult tr, int nLLPComponents, String caseID) {
+ String envValue = getValue(LD_LIBRARY_PATH, tr.testOutput);
+ /*
+ * the envValue can never be null, since the test code should always
+ * print a "null" string.
+ */
+ if (envValue == null) {
+ System.out.println(tr);
+ throw new RuntimeException("NPE, likely a program crash ??");
+ }
+ String values[] = envValue.split(File.pathSeparator);
+ if (values.length == nLLPComponents) {
+ System.out.println(caseID + " :OK");
+ passes++;
+ } else {
+ System.out.println("FAIL: test7029048, " + caseID);
+ System.out.println(" expected " + nLLPComponents
+ + " but got " + values.length);
+ System.out.println(envValue);
+ System.out.println(tr);
+ errors++;
+ }
+ }
+
+ /*
+ * A crucial piece, specifies what we should expect, given the conditions.
+ * That is for a given enum type, the value indicates how many absolute
+ * environment variables that can be expected. This value is used to base
+ * the actual expected values by adding the set environment variable usually
+ * it is 1, but it could be more if the test wishes to set more paths in
+ * the future.
+ */
+ private static enum LLP_VAR {
+ LLP_SET_NON_EXISTENT_PATH(0), // env set, but the path does not exist
+ LLP_SET_EMPTY_PATH(0), // env set, with a path but no libjvm.so
+ LLP_SET_WITH_JVM(3); // env set, with a libjvm.so
+ private final int value;
+ LLP_VAR(int i) {
+ this.value = i;
+ }
+ }
+
+ /*
+ * test for 7029048
+ */
+ static void test7029048() throws IOException {
+ String desc = null;
+ for (LLP_VAR v : LLP_VAR.values()) {
+ switch (v) {
+ case LLP_SET_WITH_JVM:
+ // copy the files into the directory structures
+ TestHelper.copyFile(srcLibjvmSo, dstServerLibjvm);
+ // does not matter if it is client or a server
+ TestHelper.copyFile(srcLibjvmSo, dstClientLibjvm);
+ // does not matter if the arch do not match either
+ if (TestHelper.isDualMode) {
+ TestHelper.copyFile(srcLibjvmSo, dstOtherServerLibjvm);
+ }
+ desc = "LD_LIBRARY_PATH should be set";
+ break;
+ case LLP_SET_EMPTY_PATH:
+ if (!dstClientDir.exists()) {
+ Files.createDirectories(dstClientDir.toPath());
+ } else {
+ Files.deleteIfExists(dstClientLibjvm.toPath());
+ }
+
+ if (!dstServerDir.exists()) {
+ Files.createDirectories(dstServerDir.toPath());
+ } else {
+ Files.deleteIfExists(dstServerLibjvm.toPath());
+ }
+
+ if (TestHelper.isDualMode) {
+ if (!dstOtherServerDir.exists()) {
+ Files.createDirectories(dstOtherServerDir.toPath());
+ } else {
+ Files.deleteIfExists(dstOtherServerLibjvm.toPath());
+ }
+ }
+
+ desc = "LD_LIBRARY_PATH should not be set";
+ break;
+ case LLP_SET_NON_EXISTENT_PATH:
+ if (dstLibDir.exists()) {
+ TestHelper.recursiveDelete(dstLibDir);
+ }
+ desc = "LD_LIBRARY_PATH should not be set";
+ break;
+ default:
+ throw new RuntimeException("unknown case");
+ }
+
+ /*
+ * Case 1: set the server path
+ */
+ env.clear();
+ env.put(LD_LIBRARY_PATH, dstServerDir.getAbsolutePath());
+ run(env, v.value + 1, "Case 1: " + desc);
+
+ /*
+ * Case 2: repeat with client path
+ */
+ env.clear();
+ env.put(LD_LIBRARY_PATH, dstClientDir.getAbsolutePath());
+ run(env, v.value + 1, "Case 2: " + desc);
+
+ if (!TestHelper.isDualMode) {
+ continue; // nothing more to do for Linux
+ }
+
+ // Tests applicable only to solaris.
+
+ // initialize test variables for dual mode operations
+ final File dst32ServerDir = TestHelper.is32Bit
+ ? dstServerDir
+ : dstOtherServerDir;
+
+ final File dst64ServerDir = TestHelper.is64Bit
+ ? dstServerDir
+ : dstOtherServerDir;
+
+ /*
+ * Case 3: set the appropriate LLP_XX flag,
+ * java32 -d32, LLP_32 is relevant, LLP_64 is ignored
+ * java64 -d64, LLP_64 is relevant, LLP_32 is ignored
+ */
+ env.clear();
+ env.put(LD_LIBRARY_PATH_32, dst32ServerDir.getAbsolutePath());
+ env.put(LD_LIBRARY_PATH_64, dst64ServerDir.getAbsolutePath());
+ run(TestHelper.is32Bit, null, env, v.value + 1, "Case 3: " + desc);
+
+ /*
+ * Case 4: we are in dual mode environment, running 64-bit then
+ * we have the following scenarios:
+ * java32 -d64, LLP_64 is relevant, LLP_32 is ignored
+ * java64 -d32, LLP_32 is relevant, LLP_64 is ignored
+ */
+ if (TestHelper.dualModePresent()) {
+ run(true, "-d64", env, v.value + 1, "Case 4A: " + desc);
+ run(false,"-d32", env, v.value + 1, "Case 4B: " + desc);
+ }
+ }
+ return;
+ }
+
+ public static void main(String... args) throws Exception {
+ if (TestHelper.isWindows) {
+ System.out.println("Warning: noop on windows");
+ return;
+ }
+ // create our test jar first
+ ExecutionEnvironment.createTestJar();
+
+ // run the tests
+ test7029048();
+ if (errors > 0) {
+ throw new Exception("Test7029048: FAIL: with "
+ + errors + " errors and passes " + passes);
+ } else if (TestHelper.dualModePresent() && passes < 15) {
+ throw new Exception("Test7029048: FAIL: " +
+ "all tests did not run, expected " + 15 + " got " + passes);
+ } else if (TestHelper.isSolaris && passes < 9) {
+ throw new Exception("Test7029048: FAIL: " +
+ "all tests did not run, expected " + 9 + " got " + passes);
+ } else if (TestHelper.isLinux && passes < 6) {
+ throw new Exception("Test7029048: FAIL: " +
+ "all tests did not run, expected " + 6 + " got " + passes);
+ } else {
+ System.out.println("Test7029048: PASS " + passes);
+ }
+ }
+}
--- a/jdk/test/tools/launcher/TestHelper.java Thu Apr 07 11:25:09 2011 -0400
+++ b/jdk/test/tools/launcher/TestHelper.java Thu Apr 07 12:06:32 2011 -0700
@@ -1,6 +1,5 @@
-
/*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011 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
@@ -22,20 +21,28 @@
* questions.
*/
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.FileVisitResult;
+import java.nio.file.SimpleFileVisitor;
import javax.tools.ToolProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
+import static java.nio.file.StandardCopyOption.*;
+
/**
- * This class provides some common utilites for the launcher tests.
+ * This class provides some common utilities for the launcher tests.
*/
public enum TestHelper {
INSTANCE;
@@ -101,6 +108,13 @@
}
/*
+ * is a dual mode available in the test jdk
+ */
+ static boolean dualModePresent() {
+ return isDualMode && java64Cmd != null;
+ }
+
+ /*
* usually the jre/lib/arch-name is the same as os.arch, except for x86.
*/
static String getJreArch() {
@@ -109,6 +123,27 @@
}
/*
+ * get the complementary jre arch ie. if sparc then return sparcv9 and
+ * vice-versa.
+ */
+ static String getComplementaryJreArch() {
+ String arch = System.getProperty("os.arch");
+ if (arch != null) {
+ switch (arch) {
+ case "sparc":
+ return "sparcv9";
+ case "sparcv9":
+ return "sparc";
+ case "x86":
+ return "amd64";
+ case "amd64":
+ return "i386";
+ }
+ }
+ return null;
+ }
+
+ /*
* A convenience method to create a jar with jar file name and defs
*/
static void createJar(File jarName, String... mainDefs)
@@ -168,6 +203,44 @@
}
}
+ static void copyFile(File src, File dst) throws IOException {
+ Path parent = dst.toPath().getParent();
+ if (parent != null) {
+ Files.createDirectories(parent);
+ }
+ Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
+ }
+
+ static void recursiveDelete(File target) throws IOException {
+ if (!target.exists()) {
+ return;
+ }
+ Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+ try {
+ Files.deleteIfExists(dir);
+ } catch (IOException ex) {
+ System.out.println("Error: could not delete: " + dir.toString());
+ System.out.println(ex.getMessage());
+ return FileVisitResult.TERMINATE;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ try {
+ Files.deleteIfExists(file);
+ } catch (IOException ex) {
+ System.out.println("Error: could not delete: " + file.toString());
+ System.out.println(ex.getMessage());
+ return FileVisitResult.TERMINATE;
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
static TestResult doExec(String...cmds) {
return doExec(null, cmds);
}
@@ -187,7 +260,7 @@
}
BufferedReader rdr = null;
try {
- List<String> outputList = new ArrayList<String>();
+ List<String> outputList = new ArrayList<>();
pb.redirectErrorStream(true);
Process p = pb.start();
rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
@@ -198,7 +271,9 @@
}
p.waitFor();
p.destroy();
- return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList);
+
+ return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList,
+ env, new Throwable("current stack of the test"));
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex.getMessage());
@@ -213,11 +288,16 @@
StringBuilder status;
int exitValue;
List<String> testOutput;
+ Map<String, String> env;
+ Throwable t;
- public TestResult(String str, int rv, List<String> oList) {
+ public TestResult(String str, int rv, List<String> oList,
+ Map<String, String> env, Throwable t) {
status = new StringBuilder("Executed command: " + str + "\n");
exitValue = rv;
testOutput = oList;
+ this.env = env;
+ this.t = t;
}
void appendStatus(String x) {
@@ -262,11 +342,21 @@
@Override
public String toString() {
- status = status.append("++++Test Output Begin++++\n");
+ status.append("++++Begin Test Info++++\n");
+ status.append("++++Test Environment++++\n");
+ for (String x : env.keySet()) {
+ status.append(x).append("=").append(env.get(x)).append("\n");
+ }
+ status.append("++++Test Output++++\n");
for (String x : testOutput) {
appendStatus(x);
}
- status = status.append("++++Test Output End++++\n");
+ status.append("++++Test Stack Trace++++\n");
+ status.append(t.toString());
+ for (StackTraceElement e : t.getStackTrace()) {
+ status.append(e.toString());
+ }
+ status.append("++++End of Test Info++++\n");
return status.toString();
}