--- a/jdk/src/share/classes/java/lang/System.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/java/lang/System.java Wed Jun 17 13:12:42 2009 -0700
@@ -1174,6 +1174,12 @@
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
+ public int getStackTraceDepth(Throwable t) {
+ return t.getStackTraceDepth();
+ }
+ public StackTraceElement getStackTraceElement(Throwable t, int i) {
+ return t.getStackTraceElement(i);
+ }
});
}
--- a/jdk/src/share/classes/java/lang/Throwable.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Throwable.java Wed Jun 17 13:12:42 2009 -0700
@@ -645,17 +645,21 @@
/**
* Returns the number of elements in the stack trace (or 0 if the stack
* trace is unavailable).
+ *
+ * package-protection for use by SharedSecrets.
*/
- private native int getStackTraceDepth();
+ native int getStackTraceDepth();
/**
* Returns the specified element of the stack trace.
*
+ * package-protection for use by SharedSecrets.
+ *
* @param index index of the element to return.
* @throws IndexOutOfBoundsException if <tt>index < 0 ||
* index >= getStackTraceDepth() </tt>
*/
- private native StackTraceElement getStackTraceElement(int index);
+ native StackTraceElement getStackTraceElement(int index);
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
--- a/jdk/src/share/classes/java/util/logging/LogRecord.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogRecord.java Wed Jun 17 13:12:42 2009 -0700
@@ -29,6 +29,9 @@
import java.util.concurrent.atomic.AtomicLong;
import java.io.*;
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+
/**
* LogRecord objects are used to pass logging requests between
* the logging framework and individual log Handlers.
@@ -522,29 +525,31 @@
// Private method to infer the caller's class and method names
private void inferCaller() {
needToInferCaller = false;
- // Get the stack trace.
- StackTraceElement stack[] = (new Throwable()).getStackTrace();
- // First, search back to a method in the Logger class.
- int ix = 0;
- while (ix < stack.length) {
- StackTraceElement frame = stack[ix];
+ JavaLangAccess access = SharedSecrets.getJavaLangAccess();
+ Throwable throwable = new Throwable();
+ int depth = access.getStackTraceDepth(throwable);
+
+ String logClassName = "java.util.logging.Logger";
+ boolean lookingForLogger = true;
+ for (int ix = 0; ix < depth; ix++) {
+ // Calling getStackTraceElement directly prevents the VM
+ // from paying the cost of building the entire stack frame.
+ StackTraceElement frame =
+ access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
- if (cname.equals("java.util.logging.Logger")) {
- break;
+ if (lookingForLogger) {
+ // Skip all frames until we have found the first logger frame.
+ if (cname.equals(logClassName)) {
+ lookingForLogger = false;
+ }
+ } else {
+ if (!cname.equals(logClassName)) {
+ // We've found the relevant frame.
+ setSourceClassName(cname);
+ setSourceMethodName(frame.getMethodName());
+ return;
+ }
}
- ix++;
- }
- // Now search for the first frame before the "Logger" class.
- while (ix < stack.length) {
- StackTraceElement frame = stack[ix];
- String cname = frame.getClassName();
- if (!cname.equals("java.util.logging.Logger")) {
- // We've found the relevant frame.
- setSourceClassName(cname);
- setSourceMethodName(frame.getMethodName());
- return;
- }
- ix++;
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
--- a/jdk/src/share/classes/sun/misc/JavaLangAccess.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/sun/misc/JavaLangAccess.java Wed Jun 17 13:12:42 2009 -0700
@@ -73,4 +73,14 @@
* the slot is not valid to register.
*/
void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
+
+ /**
+ * Returns the number of stack frames represented by the given throwable.
+ */
+ int getStackTraceDepth(Throwable t);
+
+ /**
+ * Returns the ith StackTraceElement for the given throwable.
+ */
+ StackTraceElement getStackTraceElement(Throwable t, int i);
}
--- a/jdk/src/share/classes/sun/security/x509/CertificateVersion.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/sun/security/x509/CertificateVersion.java Wed Jun 17 13:12:42 2009 -0700
@@ -28,7 +28,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Date;
import java.util.Enumeration;
import sun.security.util.*;
--- a/jdk/src/share/classes/sun/security/x509/InvalidityDateExtension.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/share/classes/sun/security/x509/InvalidityDateExtension.java Wed Jun 17 13:12:42 2009 -0700
@@ -140,7 +140,11 @@
*/
public Object get(String name) throws IOException {
if (name.equalsIgnoreCase(DATE)) {
- return date;
+ if (date == null) {
+ return null;
+ } else {
+ return (new Date(date.getTime())); // clone
+ }
} else {
throw new IOException
("Name not supported by InvalidityDateExtension");
--- a/jdk/src/solaris/native/java/lang/UNIXProcess_md.c Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/src/solaris/native/java/lang/UNIXProcess_md.c Wed Jun 17 13:12:42 2009 -0700
@@ -49,6 +49,18 @@
#include <fcntl.h>
#include <limits.h>
+#ifndef USE_CLONE
+#ifdef __linux__
+#define USE_CLONE 1
+#else
+#define USE_CLONE 0
+#endif
+#endif
+
+#if USE_CLONE
+#include <sched.h>
+#endif
+
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
@@ -376,70 +388,61 @@
}
#endif /* DEBUG_PROCESS */
-/* Version of execvpe when child's PATH differs from parent's */
-static int
-execvp_usingParentPath(const char *file, const char *const argv[])
+/**
+ * Exec FILE as a traditional Bourne shell script (i.e. one without #!).
+ * If we could do it over again, we would probably not support such an ancient
+ * misfeature, but compatibility wins over sanity. The original support for
+ * this was imported accidentally from execvp().
+ */
+static void
+execve_as_traditional_shell_script(const char *file,
+ const char *argv[],
+ const char *const envp[])
{
- char expanded_file[PATH_MAX];
- int filelen = strlen(file);
- int sticky_errno = 0;
- const char * const * dirs;
- /* Search parent's PATH */
- for (dirs = parentPathv; *dirs; dirs++) {
- const char * dir = *dirs;
- int dirlen = strlen(dir);
- if (filelen + dirlen + 1 >= PATH_MAX) {
- /* Resist the urge to remove this limit;
- * calling malloc after fork is unsafe. */
- errno = ENAMETOOLONG;
- continue;
- }
- strcpy(expanded_file, dir);
- strcpy(expanded_file + dirlen, file);
- execvp(expanded_file, (char **) argv);
- /* There are 3 responses to various classes of errno:
- * return immediately, continue (especially for ENOENT),
- * or continue with "sticky" errno.
- *
- * From exec(3):
- *
- * If permission is denied for a file (the attempted
- * execve returned EACCES), these functions will continue
- * searching the rest of the search path. If no other
- * file is found, however, they will return with the
- * global variable errno set to EACCES.
- */
- switch (errno) {
- case EACCES:
- sticky_errno = errno;
- /* FALLTHRU */
- case ENOENT:
- case ENOTDIR:
-#ifdef ELOOP
- case ELOOP:
-#endif
-#ifdef ESTALE
- case ESTALE:
-#endif
-#ifdef ENODEV
- case ENODEV:
-#endif
-#ifdef ETIMEDOUT
- case ETIMEDOUT:
-#endif
- break; /* Try other directories in PATH */
- default:
- return -1;
- }
- }
- if (sticky_errno != 0)
- errno = sticky_errno;
- return -1;
+ /* Use the extra word of space provided for us in argv by caller. */
+ const char *argv0 = argv[0];
+ const char *const *end = argv;
+ while (*end != NULL)
+ ++end;
+ memmove(argv+2, argv+1, (end-argv) * sizeof (*end));
+ argv[0] = "/bin/sh";
+ argv[1] = file;
+ execve(argv[0], (char **) argv, (char **) envp);
+ /* Can't even exec /bin/sh? Big trouble, but let's soldier on... */
+ memmove(argv+1, argv+2, (end-argv) * sizeof (*end));
+ argv[0] = argv0;
}
-/* execvpe should have been included in the Unix standards. */
-static int
-execvpe(const char *file, const char *const argv[], const char *const envp[])
+/**
+ * Like execve(2), except that in case of ENOEXEC, FILE is assumed to
+ * be a shell script and the system default shell is invoked to run it.
+ */
+static void
+execve_with_shell_fallback(const char *file,
+ const char *argv[],
+ const char *const envp[])
+{
+#if USE_CLONE
+ execve(file, (char **) argv, (char **) envp);
+ if (errno == ENOEXEC)
+ execve_as_traditional_shell_script(file, argv, envp);
+#else
+ /* Our address space is unshared, so can mutate environ. */
+ extern char **environ;
+ environ = (char **) envp;
+ execvp(file, (char **) argv);
+#endif
+}
+
+/**
+ * execvpe should have been included in the Unix standards.
+ * execvpe is identical to execvp, except that the child environment is
+ * specified via the 3rd argument instead of being inherited from environ.
+ */
+static void
+execvpe(const char *file,
+ const char *argv[],
+ const char *const envp[])
{
/* This is one of the rare times it's more portable to declare an
* external symbol explicitly, rather than via a system header.
@@ -454,28 +457,73 @@
*/
extern char **environ;
- if (envp != NULL)
- environ = (char **) envp;
+ if (envp == NULL || (char **) envp == environ) {
+ execvp(file, (char **) argv);
+ return;
+ }
- if (/* Parent and child environment the same? Use child PATH. */
- (envp == NULL)
+ if (*file == '\0') {
+ errno = ENOENT;
+ return;
+ }
- /* http://www.opengroup.org/onlinepubs/009695399/functions/exec.html
- * "If the file argument contains a slash character, it is used as
- * the pathname for this file. Otherwise, the path prefix for this
- * file is obtained by a search of the directories passed in the
- * PATH environment variable" */
- || (strchr(file, '/') != NULL)
-
- /* Parent and child PATH the same? Use child PATH. */
- || (strcmp(parentPath, effectivePath()) == 0)
-
- /* We want ENOENT, not EACCES, for zero-length program names. */
- || (*file == '\0'))
-
- return execvp(file, (char **) argv);
- else
- return execvp_usingParentPath(file, argv);
+ if (strchr(file, '/') != NULL) {
+ execve_with_shell_fallback(file, argv, envp);
+ } else {
+ /* We must search PATH (parent's, not child's) */
+ char expanded_file[PATH_MAX];
+ int filelen = strlen(file);
+ int sticky_errno = 0;
+ const char * const * dirs;
+ for (dirs = parentPathv; *dirs; dirs++) {
+ const char * dir = *dirs;
+ int dirlen = strlen(dir);
+ if (filelen + dirlen + 1 >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ continue;
+ }
+ memcpy(expanded_file, dir, dirlen);
+ memcpy(expanded_file + dirlen, file, filelen);
+ expanded_file[dirlen + filelen] = '\0';
+ execve_with_shell_fallback(expanded_file, argv, envp);
+ /* There are 3 responses to various classes of errno:
+ * return immediately, continue (especially for ENOENT),
+ * or continue with "sticky" errno.
+ *
+ * From exec(3):
+ *
+ * If permission is denied for a file (the attempted
+ * execve returned EACCES), these functions will continue
+ * searching the rest of the search path. If no other
+ * file is found, however, they will return with the
+ * global variable errno set to EACCES.
+ */
+ switch (errno) {
+ case EACCES:
+ sticky_errno = errno;
+ /* FALLTHRU */
+ case ENOENT:
+ case ENOTDIR:
+#ifdef ELOOP
+ case ELOOP:
+#endif
+#ifdef ESTALE
+ case ESTALE:
+#endif
+#ifdef ENODEV
+ case ENODEV:
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+#endif
+ break; /* Try other directories in PATH */
+ default:
+ return;
+ }
+ }
+ if (sticky_errno != 0)
+ errno = sticky_errno;
+ }
}
static void
@@ -516,10 +564,95 @@
}
}
-#ifndef __solaris__
-#undef fork1
-#define fork1() fork()
-#endif
+typedef struct _ChildStuff
+{
+ int in[2];
+ int out[2];
+ int err[2];
+ int fail[2];
+ int fds[3];
+ const char **argv;
+ const char **envv;
+ const char *pdir;
+ jboolean redirectErrorStream;
+} ChildStuff;
+
+static void
+copyPipe(int from[2], int to[2])
+{
+ to[0] = from[0];
+ to[1] = from[1];
+}
+
+/**
+ * Child process after a successful fork() or clone().
+ * This function must not return, and must be prepared for either all
+ * of its address space to be shared with its parent, or to be a copy.
+ * It must not modify global variables such as "environ".
+ */
+static int
+childProcess(void *arg)
+{
+ const ChildStuff* p = (const ChildStuff*) arg;
+
+ /* Close the parent sides of the pipes.
+ Closing pipe fds here is redundant, since closeDescriptors()
+ would do it anyways, but a little paranoia is a good thing. */
+ closeSafely(p->in[1]);
+ closeSafely(p->out[0]);
+ closeSafely(p->err[0]);
+ closeSafely(p->fail[0]);
+
+ /* Give the child sides of the pipes the right fileno's. */
+ /* Note: it is possible for in[0] == 0 */
+ moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], STDIN_FILENO);
+ moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1], STDOUT_FILENO);
+
+ if (p->redirectErrorStream) {
+ closeSafely(p->err[1]);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else {
+ moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], STDERR_FILENO);
+ }
+
+ moveDescriptor(p->fail[1], FAIL_FILENO);
+
+ /* close everything */
+ if (closeDescriptors() == 0) { /* failed, close the old way */
+ int max_fd = (int)sysconf(_SC_OPEN_MAX);
+ int i;
+ for (i = FAIL_FILENO + 1; i < max_fd; i++)
+ close(i);
+ }
+
+ /* change to the new working directory */
+ if (p->pdir != NULL && chdir(p->pdir) < 0)
+ goto WhyCantJohnnyExec;
+
+ if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1)
+ goto WhyCantJohnnyExec;
+
+ execvpe(p->argv[0], p->argv, p->envv);
+
+ WhyCantJohnnyExec:
+ /* We used to go to an awful lot of trouble to predict whether the
+ * child would fail, but there is no reliable way to predict the
+ * success of an operation without *trying* it, and there's no way
+ * to try a chdir or exec in the parent. Instead, all we need is a
+ * way to communicate any failure back to the parent. Easy; we just
+ * send the errno back to the parent over a pipe in case of failure.
+ * The tricky thing is, how do we communicate the *success* of exec?
+ * We use FD_CLOEXEC together with the fact that a read() on a pipe
+ * yields EOF when the write ends (we have two of them!) are closed.
+ */
+ {
+ int errnum = errno;
+ write(FAIL_FILENO, &errnum, sizeof(errnum));
+ }
+ close(FAIL_FILENO);
+ _exit(-1);
+ return 0; /* Suppress warning "no return value from function" */
+}
JNIEXPORT jint JNICALL
Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
@@ -533,34 +666,43 @@
{
int errnum;
int resultPid = -1;
+#if USE_CLONE
+ void *clone_stack = NULL;
+#endif
int in[2], out[2], err[2], fail[2];
- const char **argv = NULL;
- const char **envv = NULL;
- const char *pprog = getBytes(env, prog);
- const char *pargBlock = getBytes(env, argBlock);
- const char *penvBlock = getBytes(env, envBlock);
- const char *pdir = getBytes(env, dir);
jint *fds = NULL;
+ const char *pprog = NULL;
+ const char *pargBlock = NULL;
+ const char *penvBlock = NULL;
+ ChildStuff *c;
in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;
- assert(prog != NULL && argBlock != NULL);
- if (pprog == NULL) goto Catch;
- if (pargBlock == NULL) goto Catch;
- if (envBlock != NULL && penvBlock == NULL) goto Catch;
- if (dir != NULL && pdir == NULL) goto Catch;
+ if ((c = NEW(ChildStuff, 1)) == NULL) return -1;
+ c->argv = NULL;
+ c->envv = NULL;
+ c->pdir = NULL;
- /* Convert pprog + pargBlock into a char ** argv */
- if ((argv = NEW(const char *, argc + 2)) == NULL)
- goto Catch;
- argv[0] = pprog;
- initVectorFromBlock(argv+1, pargBlock, argc);
+ /* Convert prog + argBlock into a char ** argv.
+ * Add one word room for expansion of argv for use by
+ * execve_as_traditional_shell_script.
+ */
+ assert(prog != NULL && argBlock != NULL);
+ if ((pprog = getBytes(env, prog)) == NULL) goto Catch;
+ if ((pargBlock = getBytes(env, argBlock)) == NULL) goto Catch;
+ if ((c->argv = NEW(const char *, argc + 3)) == NULL) goto Catch;
+ c->argv[0] = pprog;
+ initVectorFromBlock(c->argv+1, pargBlock, argc);
if (envBlock != NULL) {
- /* Convert penvBlock into a char ** envv */
- if ((envv = NEW(const char *, envc + 1)) == NULL)
- goto Catch;
- initVectorFromBlock(envv, penvBlock, envc);
+ /* Convert envBlock into a char ** envv */
+ if ((penvBlock = getBytes(env, envBlock)) == NULL) goto Catch;
+ if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch;
+ initVectorFromBlock(c->envv, penvBlock, envc);
+ }
+
+ if (dir != NULL) {
+ if ((c->pdir = getBytes(env, dir)) == NULL) goto Catch;
}
assert(std_fds != NULL);
@@ -574,72 +716,45 @@
throwIOException(env, errno, "Bad file descriptor");
goto Catch;
}
+ c->fds[0] = fds[0];
+ c->fds[1] = fds[1];
+ c->fds[2] = fds[2];
- resultPid = fork1();
+ copyPipe(in, c->in);
+ copyPipe(out, c->out);
+ copyPipe(err, c->err);
+ copyPipe(fail, c->fail);
+
+ c->redirectErrorStream = redirectErrorStream;
+
+ {
+#if USE_CLONE
+ /* See clone(2).
+ * Instead of worrying about which direction the stack grows, just
+ * allocate twice as much and start the stack in the middle. */
+ const int stack_size = 64 * 1024;
+ if ((clone_stack = NEW(char, 2 * stack_size)) == NULL) goto Catch;
+ resultPid = clone(childProcess, clone_stack + stack_size,
+ /* CLONE_VFORK | // works, but unnecessary */
+ CLONE_VM | SIGCHLD, c);
+#else
+ /* From fork(2): In Solaris 10, a call to fork() is identical
+ * to a call to fork1(); only the calling thread is replicated
+ * in the child process. This is the POSIX-specified behavior
+ * for fork(). */
+ resultPid = fork();
+ if (resultPid == 0) {
+ childProcess(c);
+ assert(0); /* childProcess must not return */
+ }
+#endif
+ }
+
if (resultPid < 0) {
throwIOException(env, errno, "Fork failed");
goto Catch;
}
- if (resultPid == 0) {
- /* Child process */
-
- /* Close the parent sides of the pipes.
- Closing pipe fds here is redundant, since closeDescriptors()
- would do it anyways, but a little paranoia is a good thing. */
- closeSafely(in[1]);
- closeSafely(out[0]);
- closeSafely(err[0]);
- closeSafely(fail[0]);
-
- /* Give the child sides of the pipes the right fileno's. */
- /* Note: it is possible for in[0] == 0 */
- moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO);
- moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO);
-
- if (redirectErrorStream) {
- closeSafely(err[1]);
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO);
- }
-
- moveDescriptor(fail[1], FAIL_FILENO);
-
- /* close everything */
- if (closeDescriptors() == 0) { /* failed, close the old way */
- int max_fd = (int)sysconf(_SC_OPEN_MAX);
- int i;
- for (i = FAIL_FILENO + 1; i < max_fd; i++)
- close(i);
- }
-
- /* change to the new working directory */
- if (pdir != NULL && chdir(pdir) < 0)
- goto WhyCantJohnnyExec;
-
- if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1)
- goto WhyCantJohnnyExec;
-
- execvpe(argv[0], argv, envv);
-
- WhyCantJohnnyExec:
- /* We used to go to an awful lot of trouble to predict whether the
- * child would fail, but there is no reliable way to predict the
- * success of an operation without *trying* it, and there's no way
- * to try a chdir or exec in the parent. Instead, all we need is a
- * way to communicate any failure back to the parent. Easy; we just
- * send the errno back to the parent over a pipe in case of failure.
- * The tricky thing is, how do we communicate the *success* of exec?
- * We use FD_CLOEXEC together with the fact that a read() on a pipe
- * yields EOF when the write ends (we have two of them!) are closed.
- */
- errnum = errno;
- write(FAIL_FILENO, &errnum, sizeof(errnum));
- close(FAIL_FILENO);
- _exit(-1);
- }
-
/* parent process */
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
@@ -660,6 +775,10 @@
fds[2] = (err[0] != -1) ? err[0] : -1;
Finally:
+#if USE_CLONE
+ free(clone_stack);
+#endif
+
/* Always clean up the child's side of the pipes */
closeSafely(in [0]);
closeSafely(out[1]);
@@ -669,13 +788,14 @@
closeSafely(fail[0]);
closeSafely(fail[1]);
- free(argv);
- free(envv);
-
releaseBytes(env, prog, pprog);
releaseBytes(env, argBlock, pargBlock);
releaseBytes(env, envBlock, penvBlock);
- releaseBytes(env, dir, pdir);
+ releaseBytes(env, dir, c->pdir);
+
+ free(c->argv);
+ free(c->envv);
+ free(c);
if (fds != NULL)
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java Wed Jun 17 13:12:42 2009 -0700
@@ -257,6 +257,18 @@
s.write(bytes); // Might hang!
}
+ static void checkPermissionDenied(ProcessBuilder pb) {
+ try {
+ pb.start();
+ fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ String m = e.getMessage();
+ if (EnglishUnix.is() &&
+ ! matches(m, "Permission denied"))
+ unexpected(e);
+ } catch (Throwable t) { unexpected(t); }
+ }
+
public static class JavaChild {
public static void main(String args[]) throws Throwable {
String action = args[0];
@@ -317,12 +329,10 @@
for (final ProcessBuilder pb :
new ProcessBuilder[] {pb1, pb2}) {
pb.command("true");
- r = run(pb.start());
- equal(r.exitValue(), True.exitValue());
+ equal(run(pb).exitValue(), True.exitValue());
pb.command("false");
- r = run(pb.start());
- equal(r.exitValue(), False.exitValue());
+ equal(run(pb).exitValue(), False.exitValue());
}
if (failed != 0) throw new Error("null PATH");
@@ -367,31 +377,82 @@
// Can't execute a directory -- permission denied
// Report EACCES errno
new File("dir1/prog").mkdirs();
- try {
- pb.start();
- fail("Expected IOException not thrown");
- } catch (IOException e) {
- String m = e.getMessage();
- if (EnglishUnix.is() &&
- ! matches(m, "Permission denied"))
- unexpected(e);
- } catch (Throwable t) { unexpected(t); }
+ checkPermissionDenied(pb);
// continue searching if EACCES
copy("/bin/true", "dir2/prog");
- equal(run(pb.start()).exitValue(), True.exitValue());
+ equal(run(pb).exitValue(), True.exitValue());
new File("dir1/prog").delete();
new File("dir2/prog").delete();
new File("dir2/prog").mkdirs();
copy("/bin/true", "dir1/prog");
- equal(run(pb.start()).exitValue(), True.exitValue());
+ equal(run(pb).exitValue(), True.exitValue());
- // Check empty PATH component means current directory
+ // Check empty PATH component means current directory.
+ //
+ // While we're here, let's test different kinds of
+ // Unix executables, and PATH vs explicit searching.
new File("dir1/prog").delete();
new File("dir2/prog").delete();
- copy("/bin/true", "./prog");
- equal(run(pb.start()).exitValue(), True.exitValue());
+ for (String[] command :
+ new String[][] {
+ new String[] {"./prog"},
+ cmd}) {
+ pb.command(command);
+ File prog = new File("./prog");
+ // "Normal" binaries
+ copy("/bin/true", "./prog");
+ equal(run(pb).exitValue(),
+ True.exitValue());
+ copy("/bin/false", "./prog");
+ equal(run(pb).exitValue(),
+ False.exitValue());
+ prog.delete();
+ // Interpreter scripts with #!
+ setFileContents(prog, "#!/bin/true\n");
+ prog.setExecutable(true);
+ equal(run(pb).exitValue(),
+ True.exitValue());
+ prog.delete();
+ setFileContents(prog, "#!/bin/false\n");
+ prog.setExecutable(true);
+ equal(run(pb).exitValue(),
+ False.exitValue());
+ // Traditional shell scripts without #!
+ setFileContents(prog, "exec /bin/true\n");
+ prog.setExecutable(true);
+ equal(run(pb).exitValue(),
+ True.exitValue());
+ prog.delete();
+ setFileContents(prog, "exec /bin/false\n");
+ prog.setExecutable(true);
+ equal(run(pb).exitValue(),
+ False.exitValue());
+ prog.delete();
+ }
+
+ // Test Unix interpreter scripts
+ File dir1Prog = new File("dir1/prog");
+ dir1Prog.delete();
+ pb.command(new String[] {"prog", "world"});
+ setFileContents(dir1Prog, "#!/bin/echo hello\n");
+ checkPermissionDenied(pb);
+ dir1Prog.setExecutable(true);
+ equal(run(pb).out(), "hello dir1/prog world\n");
+ equal(run(pb).exitValue(), True.exitValue());
+ dir1Prog.delete();
+ pb.command(cmd);
+
+ // Test traditional shell scripts without #!
+ setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
+ pb.command(new String[] {"prog", "hello", "world"});
+ checkPermissionDenied(pb);
+ dir1Prog.setExecutable(true);
+ equal(run(pb).out(), "hello world\n");
+ equal(run(pb).exitValue(), True.exitValue());
+ dir1Prog.delete();
+ pb.command(cmd);
// If prog found on both parent and child's PATH,
// parent's is used.
@@ -402,10 +463,10 @@
copy("/bin/true", "dir1/prog");
copy("/bin/false", "dir3/prog");
pb.environment().put("PATH","dir3");
- equal(run(pb.start()).exitValue(), True.exitValue());
+ equal(run(pb).exitValue(), True.exitValue());
copy("/bin/true", "dir3/prog");
copy("/bin/false", "dir1/prog");
- equal(run(pb.start()).exitValue(), False.exitValue());
+ equal(run(pb).exitValue(), False.exitValue());
} finally {
// cleanup
@@ -1503,21 +1564,19 @@
childArgs.add("OutErr");
ProcessBuilder pb = new ProcessBuilder(childArgs);
{
- ProcessResults r = run(pb.start());
+ ProcessResults r = run(pb);
equal(r.out(), "outout");
equal(r.err(), "errerr");
}
{
pb.redirectErrorStream(true);
- ProcessResults r = run(pb.start());
+ ProcessResults r = run(pb);
equal(r.out(), "outerrouterr");
equal(r.err(), "");
}
} catch (Throwable t) { unexpected(t); }
- if (! Windows.is() &&
- new File("/bin/true").exists() &&
- new File("/bin/false").exists()) {
+ if (Unix.is()) {
//----------------------------------------------------------------
// We can find true and false when PATH is null
//----------------------------------------------------------------
@@ -1526,7 +1585,7 @@
childArgs.add("null PATH");
ProcessBuilder pb = new ProcessBuilder(childArgs);
pb.environment().remove("PATH");
- ProcessResults r = run(pb.start());
+ ProcessResults r = run(pb);
equal(r.out(), "");
equal(r.err(), "");
equal(r.exitValue(), 0);
@@ -1540,7 +1599,7 @@
childArgs.add("PATH search algorithm");
ProcessBuilder pb = new ProcessBuilder(childArgs);
pb.environment().put("PATH", "dir1:dir2:");
- ProcessResults r = run(pb.start());
+ ProcessResults r = run(pb);
equal(r.out(), "");
equal(r.err(), "");
equal(r.exitValue(), True.exitValue());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/BigFork.java Wed Jun 17 13:12:42 2009 -0700
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009 Google Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * A manual test that demonstrates the ability to start a subprocess
+ * on Linux without getting ENOMEM. Run this test like:
+ *
+ * java -Xmx7000m BigFork
+ *
+ * providing a -Xmx flag suitable for your operating environment.
+ * Here's the bad old behavior:
+ *
+ * ==> java -Xmx7000m -esa -ea BigFork
+ * -------
+ * CommitLimit: 6214700 kB
+ * Committed_AS: 2484452 kB
+ * -------
+ * size=4.6GB
+ * -------
+ * CommitLimit: 6214700 kB
+ * Committed_AS: 7219680 kB
+ * -------
+ * Exception in thread "main" java.io.IOException: Cannot run program "/bin/true": java.io.IOException: error=12, Cannot allocate memory
+ * at java.lang.ProcessBuilder.start(ProcessBuilder.java:1018)
+ * at BigFork.main(BigFork.java:79)
+ * Caused by: java.io.IOException: java.io.IOException: error=12, Cannot allocate memory
+ * at java.lang.UNIXProcess.<init>(UNIXProcess.java:190)
+ * at java.lang.ProcessImpl.start(ProcessImpl.java:128)
+ * at java.lang.ProcessBuilder.start(ProcessBuilder.java:1010)
+ * ... 1 more
+ */
+public class BigFork {
+ static final Random rnd = new Random();
+ static void touchPages(byte[] chunk) {
+ final int pageSize = 4096;
+ for (int i = 0; i < chunk.length; i+= pageSize) {
+ chunk[i] = (byte) rnd.nextInt();
+ }
+ }
+
+ static void showCommittedMemory() throws IOException {
+ BufferedReader r =
+ new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream("/proc/meminfo")));
+ System.out.println("-------");
+ String line;
+ while ((line = r.readLine()) != null) {
+ if (line.startsWith("Commit")) {
+ System.out.printf("%s%n", line);
+ }
+ }
+ System.out.println("-------");
+ }
+
+ public static void main(String[] args) throws Throwable {
+ showCommittedMemory();
+
+ final int chunkSize = 1024 * 1024 * 100;
+ List<byte[]> chunks = new ArrayList<byte[]>(100);
+ try {
+ for (;;) {
+ byte[] chunk = new byte[chunkSize];
+ touchPages(chunk);
+ chunks.add(chunk);
+ }
+ } catch (OutOfMemoryError e) {
+ chunks.set(0, null); // Free up one chunk
+ System.gc();
+ int size = chunks.size();
+ System.out.printf("size=%.2gGB%n", (double)size/10);
+
+ showCommittedMemory();
+
+ // Can we fork/exec in our current bloated state?
+ Process p = new ProcessBuilder("/bin/true").start();
+ p.waitFor();
+ }
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/CrossRealm.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/CrossRealm.java Wed Jun 17 13:12:42 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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
@@ -26,6 +26,7 @@
* @bug 6706974
* @summary Add krb5 test infrastructure
*/
+import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Security;
@@ -50,17 +51,20 @@
KDC kdc1 = KDC.create("RABBIT.HOLE");
kdc1.addPrincipal("dummy", "bogus".toCharArray());
kdc1.addPrincipalRandKey("krbtgt/RABBIT.HOLE");
- kdc1.addPrincipal("krbtgt/SNAKE.HOLE", "sharedsec".toCharArray());
+ kdc1.addPrincipal("krbtgt/SNAKE.HOLE@RABBIT.HOLE",
+ "rabbit->snake".toCharArray());
KDC kdc2 = KDC.create("SNAKE.HOLE");
kdc2.addPrincipalRandKey("krbtgt/SNAKE.HOLE");
- kdc2.addPrincipal("krbtgt/RABBIT.HOLE", "sharedsec".toCharArray());
+ kdc2.addPrincipal("krbtgt/SNAKE.HOLE@RABBIT.HOLE",
+ "rabbit->snake".toCharArray());
kdc2.addPrincipalRandKey("host/www.snake.hole");
KDC.saveConfig("krb5-localkdc.conf", kdc1, kdc2,
"forwardable=true",
"[domain_realm]",
".snake.hole=SNAKE.HOLE");
+ new File("krb5-localkdc.conf").deleteOnExit();
System.setProperty("java.security.krb5.conf", "krb5-localkdc.conf");
}
@@ -68,6 +72,7 @@
Security.setProperty("auth.login.defaultCallbackHandler", "CrossRealm");
System.setProperty("java.security.auth.login.config", "jaas-localkdc.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+ new File("jaas-localkdc.conf").deleteOnExit();
FileOutputStream fos = new FileOutputStream("jaas-localkdc.conf");
fos.write(("com.sun.security.jgss.krb5.initiate {\n" +
" com.sun.security.auth.module.Krb5LoginModule\n" +
--- a/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java Wed Jun 17 13:12:42 2009 -0700
@@ -25,7 +25,6 @@
* @test
* @bug 6578647
* @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
- * @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock HttpNegotiateServer
*/
import com.sun.net.httpserver.Headers;
@@ -40,12 +39,10 @@
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
-import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.security.PrivilegedExceptionAction;
-import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
@@ -53,8 +50,6 @@
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSManager;
import sun.security.jgss.GSSUtil;
-import sun.net.spi.nameservice.NameService;
-import sun.net.spi.nameservice.NameServiceDescriptor;
import sun.security.krb5.Config;
/**
@@ -62,7 +57,7 @@
* party uses JAAS login to get subjects and executes JGSS calls using
* Subject.doAs.
*/
-public class HttpNegotiateServer implements NameServiceDescriptor {
+public class HttpNegotiateServer {
// Two realm, web server in one, proxy server in another
final static String REALM_WEB = "WEB.DOMAIN";
@@ -142,12 +137,12 @@
public static void main(String[] args)
throws Exception {
- KDC kdcw = new KDC(REALM_WEB, 0, true);
+ KDC kdcw = KDC.create(REALM_WEB);
kdcw.addPrincipal(WEB_USER, WEB_PASS);
kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);
kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);
- KDC kdcp = new KDC(REALM_PROXY, 0, true);
+ KDC kdcp = KDC.create(REALM_PROXY);
kdcp.addPrincipal(PROXY_USER, PROXY_PASS);
kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
@@ -306,36 +301,5 @@
}
}
}
-
- @Override
- public NameService createNameService() throws Exception {
- NameService ns = new NameService() {
- @Override
- public InetAddress[] lookupAllHostAddr(String host)
- throws UnknownHostException {
- // Everything is localhost
- return new InetAddress[]{
- InetAddress.getByAddress(host, new byte[]{127,0,0,1})
- };
- }
- @Override
- public String getHostByAddr(byte[] addr)
- throws UnknownHostException {
- // No reverse lookup
- throw new UnknownHostException();
- }
- };
- return ns;
- }
-
- @Override
- public String getProviderName() {
- return "mock";
- }
-
- @Override
- public String getType() {
- return "ns";
- }
}
--- a/jdk/test/sun/security/krb5/auto/KDC.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/KDC.java Wed Jun 17 13:12:42 2009 -0700
@@ -30,6 +30,8 @@
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.*;
+import sun.net.spi.nameservice.NameService;
+import sun.net.spi.nameservice.NameServiceDescriptor;
import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
@@ -118,14 +120,16 @@
// The random generator to generate random keys (including session keys)
private static SecureRandom secureRandom = new SecureRandom();
- // Principal db
+ // Principal db. principal -> pass
private Map<String,char[]> passwords = new HashMap<String,char[]>();
// Realm name
private String realm;
+ // KDC
+ private String kdc;
+ // Service port number
+ private int port;
// The request/response job queue
private BlockingQueue<Job> q = new ArrayBlockingQueue<Job>(100);
- // Service port number
- private int port;
// Options
private Map<Option,Object> options = new HashMap<Option,Object>();
@@ -139,33 +143,21 @@
PREAUTH_REQUIRED,
};
+ static {
+ System.setProperty("sun.net.spi.nameservice.provider.1", "ns,mock");
+ }
+
/**
* A standalone KDC server.
- * @param args
- * @throws java.lang.Exception
*/
public static void main(String[] args) throws Exception {
- if (args.length > 0) {
- if (args[0].equals("-help") || args[0].equals("--help")) {
- System.out.println("Usage:");
- System.out.println(" java " + KDC.class + " " +
- "Start KDC on port 8888");
- return;
- }
- }
- String localhost = "localhost";
- try {
- localhost = InetAddress.getByName(localhost)
- .getCanonicalHostName();
- } catch (UnknownHostException uhe) {
- ; // Ignore, localhost is still "localhost"
- }
- KDC kdc = create("RABBIT.HOLE", 8888, false);
+ KDC kdc = create("RABBIT.HOLE", "kdc.rabbit,hole", 0, false);
kdc.addPrincipal("dummy", "bogus".toCharArray());
kdc.addPrincipal("foo", "bar".toCharArray());
- kdc.addPrincipalRandKey("krbtgt/" + kdc.realm);
- kdc.addPrincipalRandKey("server/" + localhost);
- kdc.addPrincipalRandKey("backend/" + localhost);
+ kdc.addPrincipalRandKey("krbtgt/RABBIT.HOLE");
+ kdc.addPrincipalRandKey("server/host.rabbit.hole");
+ kdc.addPrincipalRandKey("backend/host.rabbit.hole");
+ KDC.saveConfig("krb5.conf", kdc, "forwardable = true");
}
/**
@@ -175,7 +167,7 @@
* @throws java.io.IOException for any socket creation error
*/
public static KDC create(String realm) throws IOException {
- return create(realm, 0, true);
+ return create(realm, "kdc." + realm.toLowerCase(), 0, true);
}
/**
@@ -187,8 +179,8 @@
* @return the running KDC instance
* @throws java.io.IOException for any socket creation error
*/
- public static KDC create(String realm, int port, boolean asDaemon) throws IOException {
- return new KDC(realm, port, asDaemon);
+ public static KDC create(String realm, String kdc, int port, boolean asDaemon) throws IOException {
+ return new KDC(realm, kdc, port, asDaemon);
}
/**
@@ -228,10 +220,7 @@
KeyTab ktab = KeyTab.create(tab);
for (KDC kdc: kdcs) {
for (String name : kdc.passwords.keySet()) {
- if (name.equals("krbtgt/" + kdc.realm)) {
- continue;
- }
- ktab.addEntry(new PrincipalName(name + "@" + kdc.realm,
+ ktab.addEntry(new PrincipalName(name,
name.indexOf('/') < 0 ?
PrincipalName.KRB_NT_UNKNOWN :
PrincipalName.KRB_NT_SRV_HST),
@@ -255,6 +244,9 @@
* @param pass the password for the principal
*/
public void addPrincipal(String user, char[] pass) {
+ if (user.indexOf('@') < 0) {
+ user = user + "@" + realm;
+ }
passwords.put(user, pass);
}
@@ -264,7 +256,7 @@
* form of host/f.q.d.n
*/
public void addPrincipalRandKey(String user) {
- passwords.put(user, randomPassword());
+ addPrincipal(user, randomPassword());
}
/**
@@ -276,6 +268,14 @@
}
/**
+ * Returns the name of kdc
+ * @return the name of kdc
+ */
+ public String getKDC() {
+ return kdc;
+ }
+
+ /**
* Writes a krb5.conf for one or more KDC that includes KDC locations for
* each realm and the default realm name. You can also add extra strings
* into the file. The method should be called like:
@@ -299,7 +299,7 @@
*
* [realms]
* REALM.NAME = {
- * kdc = localhost:port_number
+ * kdc = host:port_number
* }
* </pre>
*
@@ -320,10 +320,10 @@
*
* [realms]
* KDC1.NAME = {
- * kdc = localhost:port1
+ * kdc = host:port1
* }
* KDC2.NAME = {
- * kdc = localhost:port2
+ * kdc = host:port2
* }
* </pre>
* @param file the name of the file to write into
@@ -372,16 +372,17 @@
* Private constructor, cannot be called outside.
* @param realm
*/
- private KDC(String realm) {
+ private KDC(String realm, String kdc) {
this.realm = realm;
+ this.kdc = kdc;
}
/**
* A constructor that starts the KDC service also.
*/
- protected KDC(String realm, int port, boolean asDaemon)
+ protected KDC(String realm, String kdc, int port, boolean asDaemon)
throws IOException {
- this(realm);
+ this(realm, kdc);
startServer(port, asDaemon);
}
/**
@@ -426,7 +427,11 @@
* the database.
*/
private char[] getPassword(PrincipalName p) throws KrbException {
- char[] pass = passwords.get(p.getNameString());
+ String pn = p.toString();
+ if (p.getRealmString() == null) {
+ pn = pn + "@" + getRealm();
+ }
+ char[] pass = passwords.get(pn);
if (pass == null) {
throw new KrbException(Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN);
}
@@ -434,29 +439,18 @@
}
/**
- * Returns the salt string for the principal. For normal users, the
- * concatenation for the realm name and the sections of the principal;
- * for krgtgt/A@B and krbtgt/B@A, always return AB (so that inter-realm
- * principals have the same key).
+ * Returns the salt string for the principal.
* @param p principal
* @return the salt
*/
private String getSalt(PrincipalName p) {
String[] ns = p.getNameStrings();
- if (ns[0].equals("krbtgt") && ns.length > 1) {
- // Shared cross-realm keys must be the same
- if (ns[1].compareTo(realm) < 0) {
- return ns[1] + realm;
- } else {
- return realm + ns[1];
- }
- } else {
- String s = getRealm();
- for (String n: p.getNameStrings()) {
- s += n;
- }
- return s;
+ String s = p.getRealmString();
+ if (s == null) s = getRealm();
+ for (String n: p.getNameStrings()) {
+ s += n;
}
+ return s;
}
/**
@@ -525,14 +519,8 @@
EncryptedData ed = apReq.authenticator;
tkt = apReq.ticket;
etype = tkt.encPart.getEType();
- EncryptionKey kkey = null;
- if (!tkt.realm.toString().equals(realm)) {
- if (tkt.sname.getNameString().equals("krbtgt/" + realm)) {
- kkey = keyForUser(new PrincipalName("krbtgt/" + tkt.realm.toString(), realm), etype);
- }
- } else {
- kkey = keyForUser(tkt.sname, etype);
- }
+ tkt.sname.setRealm(tkt.realm);
+ EncryptionKey kkey = keyForUser(tkt.sname, etype);
byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET);
DerInputStream derIn = new DerInputStream(bb);
DerValue der = derIn.getDerValue();
@@ -857,10 +845,13 @@
/**
* Generates a line for a KDC to put inside [realms] of krb5.conf
* @param kdc the KDC
- * @return REALM.NAME = { kdc = localhost:port }
+ * @return REALM.NAME = { kdc = host:port }
*/
private static String realmLineForKDC(KDC kdc) {
- return String.format(" %s = {\n kdc = localhost:%d\n }\n", kdc.realm, kdc.port);
+ return String.format(" %s = {\n kdc = %s:%d\n }\n",
+ kdc.realm,
+ kdc.kdc,
+ kdc.port);
}
/**
@@ -1000,4 +991,37 @@
}
}
}
+
+ public static class KDCNameService implements NameServiceDescriptor {
+ @Override
+ public NameService createNameService() throws Exception {
+ NameService ns = new NameService() {
+ @Override
+ public InetAddress[] lookupAllHostAddr(String host)
+ throws UnknownHostException {
+ // Everything is localhost
+ return new InetAddress[]{
+ InetAddress.getByAddress(host, new byte[]{127,0,0,1})
+ };
+ }
+ @Override
+ public String getHostByAddr(byte[] addr)
+ throws UnknownHostException {
+ // No reverse lookup, PrincipalName use original string
+ throw new UnknownHostException();
+ }
+ };
+ return ns;
+ }
+
+ @Override
+ public String getProviderName() {
+ return "mock";
+ }
+
+ @Override
+ public String getType() {
+ return "ns";
+ }
+ }
}
--- a/jdk/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor Wed Jun 17 13:12:42 2009 -0700
@@ -1,1 +1,1 @@
-HttpNegotiateServer
+KDC$KDCNameService
--- a/jdk/test/sun/security/krb5/auto/OneKDC.java Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/OneKDC.java Wed Jun 17 13:12:42 2009 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Sun Microsystems, Inc. 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,35 +46,22 @@
*/
public class OneKDC extends KDC {
- // The krb5 codes would try to canonicalize hostnames before creating
- // a service principal name, so let's find out the canonicalized form
- // of localhost first. The following codes mimic the process inside
- // PrincipalName.java.
- static String localhost = "localhost";
- static {
- try {
- localhost = InetAddress.getByName(localhost)
- .getCanonicalHostName();
- } catch (UnknownHostException uhe) {
- ; // Ignore, localhost is still "localhost"
- }
- }
public static final String USER = "dummy";
public static final char[] PASS = "bogus".toCharArray();
- public static String SERVER = "server/" + localhost;
- public static String BACKEND = "backend/" + localhost;
public static final String KRB5_CONF = "localkdc-krb5.conf";
public static final String KTAB = "localkdc.ktab";
public static final String JAAS_CONF = "localkdc-jaas.conf";
public static final String REALM = "RABBIT.HOLE";
-
+ public static String SERVER = "server/host." + REALM.toLowerCase();
+ public static String BACKEND = "backend/host." + REALM.toLowerCase();
+ public static String KDCHOST = "kdc." + REALM.toLowerCase();
/**
* Creates the KDC and starts it.
* @param etype Encryption type, null if not specified
* @throws java.lang.Exception if there's anything wrong
*/
public OneKDC(String etype) throws Exception {
- super(REALM, 0, true);
+ super(REALM, KDCHOST, 0, true);
addPrincipal(USER, PASS);
addPrincipalRandKey("krbtgt/" + REALM);
addPrincipalRandKey(SERVER);
--- a/jdk/test/sun/security/krb5/auto/basic.sh Fri Jun 12 12:26:20 2009 -0700
+++ b/jdk/test/sun/security/krb5/auto/basic.sh Wed Jun 17 13:12:42 2009 -0700
@@ -1,5 +1,5 @@
#
-# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+# Copyright 2008-2009 Sun Microsystems, Inc. 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
@@ -41,25 +41,31 @@
case "$OS" in
Windows_* )
FS="\\"
+ SEP=";"
;;
* )
FS="/"
+ SEP=":"
;;
esac
-${TESTJAVA}${FS}bin${FS}javac -d . \
+${TESTJAVA}${FS}bin${FS}javac -XDignore.symbol.file -d . \
${TESTSRC}${FS}BasicKrb5Test.java \
${TESTSRC}${FS}KDC.java \
${TESTSRC}${FS}OneKDC.java \
${TESTSRC}${FS}Action.java \
${TESTSRC}${FS}Context.java \
|| exit 10
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test || exit 100
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des-cbc-crc || exit 1
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des-cbc-md5 || exit 3
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des3-cbc-sha1 || exit 16
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test aes128-cts || exit 17
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test aes256-cts || exit 18
-${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test rc4-hmac || exit 23
+
+# Add $TESTSRC to classpath so that customized nameservice can be used
+J="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. BasicKrb5Test"
+
+$J || exit 100
+$J des-cbc-crc || exit 1
+$J des-cbc-md5 || exit 3
+$J des3-cbc-sha1 || exit 16
+$J aes128-cts || exit 17
+$J aes256-cts || exit 18
+$J rc4-hmac || exit 23
exit 0