changeset 1511:a209372d6de8

Merge
author asaha
date Wed, 17 Jun 2009 13:12:42 -0700
parents 8156e661d016 bc2c9dbdcc70
children 2f126d8c369d
files
diffstat 15 files changed, 653 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/System.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/src/share/classes/java/lang/Throwable.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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 &lt; 0 ||
      *         index &gt;= getStackTraceDepth() </tt>
      */
-    private native StackTraceElement getStackTraceElement(int index);
+    native StackTraceElement getStackTraceElement(int index);
 
     private synchronized void writeObject(java.io.ObjectOutputStream s)
         throws IOException
--- a/src/share/classes/java/util/logging/LogRecord.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/src/share/classes/sun/misc/JavaLangAccess.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/src/share/classes/sun/security/x509/CertificateVersion.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/src/share/classes/sun/security/x509/InvalidityDateExtension.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/src/solaris/native/java/lang/UNIXProcess_md.c	Fri Jun 12 12:26:20 2009 -0700
+++ b/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;
 
+    if ((c = NEW(ChildStuff, 1)) == NULL) return -1;
+    c->argv = NULL;
+    c->envv = NULL;
+    c->pdir = NULL;
+
+    /* 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     == NULL) goto Catch;
-    if (pargBlock == NULL) goto Catch;
-    if (envBlock  != NULL && penvBlock == NULL) goto Catch;
-    if (dir       != NULL && pdir      == NULL) goto Catch;
-
-    /* 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);
+    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/test/java/lang/ProcessBuilder/Basic.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/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/test/sun/security/krb5/auto/CrossRealm.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/test/sun/security/krb5/auto/HttpNegotiateServer.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/test/sun/security/krb5/auto/KDC.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/test/sun/security/krb5/auto/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/test/sun/security/krb5/auto/OneKDC.java	Fri Jun 12 12:26:20 2009 -0700
+++ b/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/test/sun/security/krb5/auto/basic.sh	Fri Jun 12 12:26:20 2009 -0700
+++ b/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