1 /*
   2  * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
  27  *      5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
  28  *      6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
  29  *      4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464
  30  *      8067796 8224905
  31  * @key intermittent
  32  * @summary Basic tests for Process and Environment Variable code
  33  * @modules java.base/java.lang:open
  34  * @run main/othervm/timeout=300 Basic
  35  * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic
  36  * @author Martin Buchholz
  37  */
  38 
  39 /*
  40  * @test
  41  * @modules java.base/java.lang:open
  42  * @requires (os.family == "linux")
  43  * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=posix_spawn Basic
  44  */
  45 
  46 import java.lang.ProcessBuilder.Redirect;
  47 import java.lang.ProcessHandle;
  48 import static java.lang.ProcessBuilder.Redirect.*;
  49 
  50 import java.io.*;
  51 import java.lang.reflect.Field;
  52 import java.nio.file.Files;
  53 import java.nio.file.Paths;
  54 import java.nio.file.StandardCopyOption;
  55 import java.util.*;
  56 import java.util.concurrent.CountDownLatch;
  57 import java.util.concurrent.TimeUnit;
  58 import java.security.*;
  59 import java.util.regex.Pattern;
  60 import java.util.regex.Matcher;
  61 import static java.lang.System.getenv;
  62 import static java.lang.System.out;
  63 import static java.lang.Boolean.TRUE;
  64 import static java.util.AbstractMap.SimpleImmutableEntry;
  65 
  66 public class Basic {
  67 
  68     /* used for Windows only */
  69     static final String systemRoot = System.getenv("SystemRoot");
  70 
  71     /* used for Mac OS X only */
  72     static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");
  73 
  74     /* used for AIX only */
  75     static final String libpath = System.getenv("LIBPATH");
  76 
  77     /* Used for regex String matching for long error messages */
  78     static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13, The file access permissions do not allow the specified action)";
  79     static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2, A file or directory in the path name does not exist.)";
  80     static final String NO_SUCH_FILE_OR_DIR_ERROR_MSG = "(No such file or directory|error=2, A file or directory in the path name does not exist)";
  81 
  82     /**
  83      * Returns the number of milliseconds since time given by
  84      * startNanoTime, which must have been previously returned from a
  85      * call to {@link System.nanoTime()}.
  86      */
  87     private static long millisElapsedSince(long startNanoTime) {
  88         return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
  89     }
  90 
  91     private static String commandOutput(Reader r) throws Throwable {
  92         StringBuilder sb = new StringBuilder();
  93         int c;
  94         while ((c = r.read()) > 0)
  95             if (c != '\r')
  96                 sb.append((char) c);
  97         return sb.toString();
  98     }
  99 
 100     private static String commandOutput(Process p) throws Throwable {
 101         check(p.getInputStream()  == p.getInputStream());
 102         check(p.getOutputStream() == p.getOutputStream());
 103         check(p.getErrorStream()  == p.getErrorStream());
 104         Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");
 105         String output = commandOutput(r);
 106         equal(p.waitFor(), 0);
 107         equal(p.exitValue(), 0);
 108         // The debug/fastdebug versions of the VM may write some warnings to stdout
 109         // (i.e. "Warning:  Cannot open log file: hotspot.log" if the VM is started
 110         // in a directory without write permissions). These warnings will confuse tests
 111         // which match the entire output of the child process so better filter them out.
 112         return output.replaceAll("Warning:.*\\n", "");
 113     }
 114 
 115     private static String commandOutput(ProcessBuilder pb) {
 116         try {
 117             return commandOutput(pb.start());
 118         } catch (Throwable t) {
 119             String commandline = "";
 120             for (String arg : pb.command())
 121                 commandline += " " + arg;
 122             System.out.println("Exception trying to run process: " + commandline);
 123             unexpected(t);
 124             return "";
 125         }
 126     }
 127 
 128     private static String commandOutput(String...command) {
 129         try {
 130             return commandOutput(Runtime.getRuntime().exec(command));
 131         } catch (Throwable t) {
 132             String commandline = "";
 133             for (String arg : command)
 134                 commandline += " " + arg;
 135             System.out.println("Exception trying to run process: " + commandline);
 136             unexpected(t);
 137             return "";
 138         }
 139     }
 140 
 141     private static void checkCommandOutput(ProcessBuilder pb,
 142                                            String expected,
 143                                            String failureMsg) {
 144         String got = commandOutput(pb);
 145         check(got.equals(expected),
 146               failureMsg + "\n" +
 147               "Expected: \"" + expected + "\"\n" +
 148               "Got: \"" + got + "\"");
 149     }
 150 
 151     private static String absolutifyPath(String path) {
 152         StringBuilder sb = new StringBuilder();
 153         for (String file : path.split(File.pathSeparator)) {
 154             if (sb.length() != 0)
 155                 sb.append(File.pathSeparator);
 156             sb.append(new File(file).getAbsolutePath());
 157         }
 158         return sb.toString();
 159     }
 160 
 161     // compare windows-style, by canonicalizing to upper case,
 162     // not lower case as String.compareToIgnoreCase does
 163     private static class WindowsComparator
 164         implements Comparator<String> {
 165         public int compare(String x, String y) {
 166             return x.toUpperCase(Locale.US)
 167                 .compareTo(y.toUpperCase(Locale.US));
 168         }
 169     }
 170 
 171     private static String sortedLines(String lines) {
 172         String[] arr = lines.split("\n");
 173         List<String> ls = new ArrayList<String>();
 174         for (String s : arr)
 175             ls.add(s);
 176         Collections.sort(ls, new WindowsComparator());
 177         StringBuilder sb = new StringBuilder();
 178         for (String s : ls)
 179             sb.append(s + "\n");
 180         return sb.toString();
 181     }
 182 
 183     private static void compareLinesIgnoreCase(String lines1, String lines2) {
 184         if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {
 185             String dashes =
 186                 "-----------------------------------------------------";
 187             out.println(dashes);
 188             out.print(sortedLines(lines1));
 189             out.println(dashes);
 190             out.print(sortedLines(lines2));
 191             out.println(dashes);
 192             out.println("sizes: " + sortedLines(lines1).length() +
 193                         " " + sortedLines(lines2).length());
 194 
 195             fail("Sorted string contents differ");
 196         }
 197     }
 198 
 199     private static final Runtime runtime = Runtime.getRuntime();
 200 
 201     private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};
 202 
 203     private static String winEnvFilter(String env) {
 204         return env.replaceAll("\r", "")
 205             .replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");
 206     }
 207 
 208     private static String unixEnvProg() {
 209         return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"
 210             : "/bin/env";
 211     }
 212 
 213     private static String nativeEnv(String[] env) {
 214         try {
 215             if (Windows.is()) {
 216                 return winEnvFilter
 217                     (commandOutput(runtime.exec(winEnvCommand, env)));
 218             } else {
 219                 return commandOutput(runtime.exec(unixEnvProg(), env));
 220             }
 221         } catch (Throwable t) { throw new Error(t); }
 222     }
 223 
 224     private static String nativeEnv(ProcessBuilder pb) {
 225         try {
 226             if (Windows.is()) {
 227                 pb.command(winEnvCommand);
 228                 return winEnvFilter(commandOutput(pb));
 229             } else {
 230                 pb.command(new String[]{unixEnvProg()});
 231                 return commandOutput(pb);
 232             }
 233         } catch (Throwable t) { throw new Error(t); }
 234     }
 235 
 236     private static void checkSizes(Map<String,String> environ, int size) {
 237         try {
 238             equal(size, environ.size());
 239             equal(size, environ.entrySet().size());
 240             equal(size, environ.keySet().size());
 241             equal(size, environ.values().size());
 242 
 243             boolean isEmpty = (size == 0);
 244             equal(isEmpty, environ.isEmpty());
 245             equal(isEmpty, environ.entrySet().isEmpty());
 246             equal(isEmpty, environ.keySet().isEmpty());
 247             equal(isEmpty, environ.values().isEmpty());
 248         } catch (Throwable t) { unexpected(t); }
 249     }
 250 
 251     private interface EnvironmentFrobber {
 252         void doIt(Map<String,String> environ);
 253     }
 254 
 255     private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {
 256         try {
 257             Map<String,String> environ = new ProcessBuilder().environment();
 258             environ.put("Foo", "BAAR");
 259             fooDeleter.doIt(environ);
 260             equal(environ.get("Foo"), null);
 261             equal(environ.remove("Foo"), null);
 262         } catch (Throwable t) { unexpected(t); }
 263     }
 264 
 265     private static void testVariableAdder(EnvironmentFrobber fooAdder) {
 266         try {
 267             Map<String,String> environ = new ProcessBuilder().environment();
 268             environ.remove("Foo");
 269             fooAdder.doIt(environ);
 270             equal(environ.get("Foo"), "Bahrein");
 271         } catch (Throwable t) { unexpected(t); }
 272     }
 273 
 274     private static void testVariableModifier(EnvironmentFrobber fooModifier) {
 275         try {
 276             Map<String,String> environ = new ProcessBuilder().environment();
 277             environ.put("Foo","OldValue");
 278             fooModifier.doIt(environ);
 279             equal(environ.get("Foo"), "NewValue");
 280         } catch (Throwable t) { unexpected(t); }
 281     }
 282 
 283     private static void printUTF8(String s) throws IOException {
 284         out.write(s.getBytes("UTF-8"));
 285     }
 286 
 287     private static String getenvAsString(Map<String,String> environment) {
 288         StringBuilder sb = new StringBuilder();
 289         environment = new TreeMap<>(environment);
 290         for (Map.Entry<String,String> e : environment.entrySet())
 291             // Ignore magic environment variables added by the launcher
 292             if (! e.getKey().equals("LD_LIBRARY_PATH"))
 293                 sb.append(e.getKey())
 294                     .append('=')
 295                     .append(e.getValue())
 296                     .append(',');
 297         return sb.toString();
 298     }
 299 
 300     static void print4095(OutputStream s, byte b) throws Throwable {
 301         byte[] bytes = new byte[4095];
 302         Arrays.fill(bytes, b);
 303         s.write(bytes);         // Might hang!
 304     }
 305 
 306     static void checkPermissionDenied(ProcessBuilder pb) {
 307         try {
 308             pb.start();
 309             fail("Expected IOException not thrown");
 310         } catch (IOException e) {
 311             String m = e.getMessage();
 312             if (EnglishUnix.is() &&
 313                 ! matches(m, PERMISSION_DENIED_ERROR_MSG))
 314                 unexpected(e);
 315         } catch (Throwable t) { unexpected(t); }
 316     }
 317 
 318     public static class JavaChild {
 319         public static void main(String args[]) throws Throwable {
 320             String action = args[0];
 321             if (action.equals("sleep")) {
 322                 Thread.sleep(10 * 60 * 1000L);
 323             } else if (action.equals("pid")) {
 324                 System.out.println(ProcessHandle.current().pid());
 325             } else if (action.equals("testIO")) {
 326                 String expected = "standard input";
 327                 char[] buf = new char[expected.length()+1];
 328                 int n = new InputStreamReader(System.in).read(buf,0,buf.length);
 329                 if (n != expected.length())
 330                     System.exit(5);
 331                 if (! new String(buf,0,n).equals(expected))
 332                     System.exit(5);
 333                 System.err.print("standard error");
 334                 System.out.print("standard output");
 335             } else if (action.equals("testInheritIO")
 336                     || action.equals("testRedirectInherit")) {
 337                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
 338                 childArgs.add("testIO");
 339                 ProcessBuilder pb = new ProcessBuilder(childArgs);
 340                 if (action.equals("testInheritIO"))
 341                     pb.inheritIO();
 342                 else
 343                     redirectIO(pb, INHERIT, INHERIT, INHERIT);
 344                 ProcessResults r = run(pb);
 345                 if (! r.out().equals(""))
 346                     System.exit(7);
 347                 if (! r.err().equals(""))
 348                     System.exit(8);
 349                 if (r.exitValue() != 0)
 350                     System.exit(9);
 351             } else if (action.equals("System.getenv(String)")) {
 352                 String val = System.getenv(args[1]);
 353                 printUTF8(val == null ? "null" : val);
 354             } else if (action.equals("System.getenv(\\u1234)")) {
 355                 String val = System.getenv("\u1234");
 356                 printUTF8(val == null ? "null" : val);
 357             } else if (action.equals("System.getenv()")) {
 358                 printUTF8(getenvAsString(System.getenv()));
 359             } else if (action.equals("ArrayOOME")) {
 360                 Object dummy;
 361                 switch(new Random().nextInt(3)) {
 362                 case 0: dummy = new Integer[Integer.MAX_VALUE]; break;
 363                 case 1: dummy = new double[Integer.MAX_VALUE];  break;
 364                 case 2: dummy = new byte[Integer.MAX_VALUE][];  break;
 365                 default: throw new InternalError();
 366                 }
 367             } else if (action.equals("pwd")) {
 368                 printUTF8(new File(System.getProperty("user.dir"))
 369                           .getCanonicalPath());
 370             } else if (action.equals("print4095")) {
 371                 print4095(System.out, (byte) '!');
 372                 print4095(System.err, (byte) 'E');
 373                 System.exit(5);
 374             } else if (action.equals("OutErr")) {
 375                 // You might think the system streams would be
 376                 // buffered, and in fact they are implemented using
 377                 // BufferedOutputStream, but each and every print
 378                 // causes immediate operating system I/O.
 379                 System.out.print("out");
 380                 System.err.print("err");
 381                 System.out.print("out");
 382                 System.err.print("err");
 383             } else if (action.equals("null PATH")) {
 384                 equal(System.getenv("PATH"), null);
 385                 check(new File("/bin/true").exists());
 386                 check(new File("/bin/false").exists());
 387                 ProcessBuilder pb1 = new ProcessBuilder();
 388                 ProcessBuilder pb2 = new ProcessBuilder();
 389                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
 390                 ProcessResults r;
 391 
 392                 for (final ProcessBuilder pb :
 393                          new ProcessBuilder[] {pb1, pb2}) {
 394                     pb.command("true");
 395                     equal(run(pb).exitValue(), True.exitValue());
 396 
 397                     pb.command("false");
 398                     equal(run(pb).exitValue(), False.exitValue());
 399                 }
 400 
 401                 if (failed != 0) throw new Error("null PATH");
 402             } else if (action.equals("PATH search algorithm")) {
 403                 equal(System.getenv("PATH"), "dir1:dir2:");
 404                 check(new File("/bin/true").exists());
 405                 check(new File("/bin/false").exists());
 406                 String[] cmd = {"prog"};
 407                 ProcessBuilder pb1 = new ProcessBuilder(cmd);
 408                 ProcessBuilder pb2 = new ProcessBuilder(cmd);
 409                 ProcessBuilder pb3 = new ProcessBuilder(cmd);
 410                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
 411                 pb3.environment().remove("PATH");
 412 
 413                 for (final ProcessBuilder pb :
 414                          new ProcessBuilder[] {pb1, pb2, pb3}) {
 415                     try {
 416                         // Not on PATH at all; directories don't exist
 417                         try {
 418                             pb.start();
 419                             fail("Expected IOException not thrown");
 420                         } catch (IOException e) {
 421                             String m = e.getMessage();
 422                             if (EnglishUnix.is() &&
 423                                 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
 424                                 unexpected(e);
 425                         } catch (Throwable t) { unexpected(t); }
 426 
 427                         // Not on PATH at all; directories exist
 428                         new File("dir1").mkdirs();
 429                         new File("dir2").mkdirs();
 430                         try {
 431                             pb.start();
 432                             fail("Expected IOException not thrown");
 433                         } catch (IOException e) {
 434                             String m = e.getMessage();
 435                             if (EnglishUnix.is() &&
 436                                 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
 437                                 unexpected(e);
 438                         } catch (Throwable t) { unexpected(t); }
 439 
 440                         // Can't execute a directory -- permission denied
 441                         // Report EACCES errno
 442                         new File("dir1/prog").mkdirs();
 443                         checkPermissionDenied(pb);
 444 
 445                         // continue searching if EACCES
 446                         copy("/bin/true", "dir2/prog");
 447                         equal(run(pb).exitValue(), True.exitValue());
 448                         new File("dir1/prog").delete();
 449                         new File("dir2/prog").delete();
 450 
 451                         new File("dir2/prog").mkdirs();
 452                         copy("/bin/true", "dir1/prog");
 453                         equal(run(pb).exitValue(), True.exitValue());
 454 
 455                         // Check empty PATH component means current directory.
 456                         //
 457                         // While we're here, let's test different kinds of
 458                         // Unix executables, and PATH vs explicit searching.
 459                         new File("dir1/prog").delete();
 460                         new File("dir2/prog").delete();
 461                         for (String[] command :
 462                                  new String[][] {
 463                                      new String[] {"./prog"},
 464                                      cmd}) {
 465                             pb.command(command);
 466                             File prog = new File("./prog");
 467                             // "Normal" binaries
 468                             copy("/bin/true", "./prog");
 469                             equal(run(pb).exitValue(),
 470                                   True.exitValue());
 471                             copy("/bin/false", "./prog");
 472                             equal(run(pb).exitValue(),
 473                                   False.exitValue());
 474                             prog.delete();
 475                             // Interpreter scripts with #!
 476                             setFileContents(prog, "#!/bin/true\n");
 477                             prog.setExecutable(true);
 478                             equal(run(pb).exitValue(),
 479                                   True.exitValue());
 480                             prog.delete();
 481                             setFileContents(prog, "#!/bin/false\n");
 482                             prog.setExecutable(true);
 483                             equal(run(pb).exitValue(),
 484                                   False.exitValue());
 485                             // Traditional shell scripts without #!
 486                             setFileContents(prog, "exec /bin/true\n");
 487                             prog.setExecutable(true);
 488                             equal(run(pb).exitValue(),
 489                                   True.exitValue());
 490                             prog.delete();
 491                             setFileContents(prog, "exec /bin/false\n");
 492                             prog.setExecutable(true);
 493                             equal(run(pb).exitValue(),
 494                                   False.exitValue());
 495                             prog.delete();
 496                         }
 497 
 498                         // Test Unix interpreter scripts
 499                         File dir1Prog = new File("dir1/prog");
 500                         dir1Prog.delete();
 501                         pb.command(new String[] {"prog", "world"});
 502                         setFileContents(dir1Prog, "#!/bin/echo hello\n");
 503                         checkPermissionDenied(pb);
 504                         dir1Prog.setExecutable(true);
 505                         equal(run(pb).out(), "hello dir1/prog world\n");
 506                         equal(run(pb).exitValue(), True.exitValue());
 507                         dir1Prog.delete();
 508                         pb.command(cmd);
 509 
 510                         // Test traditional shell scripts without #!
 511                         setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
 512                         pb.command(new String[] {"prog", "hello", "world"});
 513                         checkPermissionDenied(pb);
 514                         dir1Prog.setExecutable(true);
 515                         equal(run(pb).out(), "hello world\n");
 516                         equal(run(pb).exitValue(), True.exitValue());
 517                         dir1Prog.delete();
 518                         pb.command(cmd);
 519 
 520                         // If prog found on both parent and child's PATH,
 521                         // parent's is used.
 522                         new File("dir1/prog").delete();
 523                         new File("dir2/prog").delete();
 524                         new File("prog").delete();
 525                         new File("dir3").mkdirs();
 526                         copy("/bin/true", "dir1/prog");
 527                         copy("/bin/false", "dir3/prog");
 528                         pb.environment().put("PATH","dir3");
 529                         equal(run(pb).exitValue(), True.exitValue());
 530                         copy("/bin/true", "dir3/prog");
 531                         copy("/bin/false", "dir1/prog");
 532                         equal(run(pb).exitValue(), False.exitValue());
 533 
 534                     } finally {
 535                         // cleanup
 536                         new File("dir1/prog").delete();
 537                         new File("dir2/prog").delete();
 538                         new File("dir3/prog").delete();
 539                         new File("dir1").delete();
 540                         new File("dir2").delete();
 541                         new File("dir3").delete();
 542                         new File("prog").delete();
 543                     }
 544                 }
 545 
 546                 if (failed != 0) throw new Error("PATH search algorithm");
 547             }
 548             else throw new Error("JavaChild invocation error");
 549         }
 550     }
 551 
 552     private static void copy(String src, String dst) throws IOException {
 553         Files.copy(Paths.get(src), Paths.get(dst),
 554                    StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
 555     }
 556 
 557     private static String javaChildOutput(ProcessBuilder pb, String...args) {
 558         List<String> list = new ArrayList<String>(javaChildArgs);
 559         for (String arg : args)
 560             list.add(arg);
 561         pb.command(list);
 562         return commandOutput(pb);
 563     }
 564 
 565     private static String getenvInChild(ProcessBuilder pb) {
 566         return javaChildOutput(pb, "System.getenv()");
 567     }
 568 
 569     private static String getenvInChild1234(ProcessBuilder pb) {
 570         return javaChildOutput(pb, "System.getenv(\\u1234)");
 571     }
 572 
 573     private static String getenvInChild(ProcessBuilder pb, String name) {
 574         return javaChildOutput(pb, "System.getenv(String)", name);
 575     }
 576 
 577     private static String pwdInChild(ProcessBuilder pb) {
 578         return javaChildOutput(pb, "pwd");
 579     }
 580 
 581     private static final String javaExe =
 582         System.getProperty("java.home") +
 583         File.separator + "bin" + File.separator + "java";
 584 
 585     private static final String classpath =
 586         System.getProperty("java.class.path");
 587 
 588     private static final List<String> javaChildArgs =
 589         Arrays.asList(javaExe,
 590                       "-XX:+DisplayVMOutputToStderr",
 591                       "-classpath", absolutifyPath(classpath),
 592                       "Basic$JavaChild");
 593 
 594     private static void testEncoding(String encoding, String tested) {
 595         try {
 596             // If round trip conversion works, should be able to set env vars
 597             // correctly in child.
 598             if (new String(tested.getBytes()).equals(tested)) {
 599                 out.println("Testing " + encoding + " environment values");
 600                 ProcessBuilder pb = new ProcessBuilder();
 601                 pb.environment().put("ASCIINAME",tested);
 602                 equal(getenvInChild(pb,"ASCIINAME"), tested);
 603             }
 604         } catch (Throwable t) { unexpected(t); }
 605     }
 606 
 607     static class Windows {
 608         public static boolean is() { return is; }
 609         private static final boolean is =
 610             System.getProperty("os.name").startsWith("Windows");
 611     }
 612 
 613     static class AIX {
 614         public static boolean is() { return is; }
 615         private static final boolean is =
 616             System.getProperty("os.name").equals("AIX");
 617     }
 618 
 619     static class Unix {
 620         public static boolean is() { return is; }
 621         private static final boolean is =
 622             (! Windows.is() &&
 623              new File("/bin/sh").exists() &&
 624              new File("/bin/true").exists() &&
 625              new File("/bin/false").exists());
 626     }
 627 
 628     static class UnicodeOS {
 629         public static boolean is() { return is; }
 630         private static final String osName = System.getProperty("os.name");
 631         private static final boolean is =
 632             // MacOS X would probably also qualify
 633             osName.startsWith("Windows")   &&
 634             ! osName.startsWith("Windows 9") &&
 635             ! osName.equals("Windows Me");
 636     }
 637 
 638     static class MacOSX {
 639         public static boolean is() { return is; }
 640         private static final String osName = System.getProperty("os.name");
 641         private static final boolean is = osName.contains("OS X");
 642     }
 643 
 644     static class True {
 645         public static int exitValue() { return 0; }
 646     }
 647 
 648     private static class False {
 649         public static int exitValue() { return exitValue; }
 650         private static final int exitValue = exitValue0();
 651         private static int exitValue0() {
 652             // /bin/false returns an *unspecified* non-zero number.
 653             try {
 654                 if (! Unix.is())
 655                     return -1;
 656                 else {
 657                     int rc = new ProcessBuilder("/bin/false")
 658                         .start().waitFor();
 659                     check(rc != 0);
 660                     return rc;
 661                 }
 662             } catch (Throwable t) { unexpected(t); return -1; }
 663         }
 664     }
 665 
 666     static class EnglishUnix {
 667         private static final Boolean is =
 668             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
 669 
 670         private static boolean isEnglish(String envvar) {
 671             String val = getenv(envvar);
 672             return (val == null) || val.matches("en.*") || val.matches("C");
 673         }
 674 
 675         /** Returns true if we can expect English OS error strings */
 676         static boolean is() { return is; }
 677     }
 678 
 679     static class DelegatingProcess extends Process {
 680         final Process p;
 681 
 682         DelegatingProcess(Process p) {
 683             this.p = p;
 684         }
 685 
 686         @Override
 687         public void destroy() {
 688             p.destroy();
 689         }
 690 
 691         @Override
 692         public int exitValue() {
 693             return p.exitValue();
 694         }
 695 
 696         @Override
 697         public int waitFor() throws InterruptedException {
 698             return p.waitFor();
 699         }
 700 
 701         @Override
 702         public OutputStream getOutputStream() {
 703             return p.getOutputStream();
 704         }
 705 
 706         @Override
 707         public InputStream getInputStream() {
 708             return p.getInputStream();
 709         }
 710 
 711         @Override
 712         public InputStream getErrorStream() {
 713             return p.getErrorStream();
 714         }
 715     }
 716 
 717     private static boolean matches(String str, String regex) {
 718         return Pattern.compile(regex).matcher(str).find();
 719     }
 720 
 721     private static String matchAndExtract(String str, String regex) {
 722         Matcher matcher = Pattern.compile(regex).matcher(str);
 723         if (matcher.find()) {
 724             return matcher.group();
 725         } else {
 726             return "";
 727         }
 728     }
 729 
 730     /* Only used for Mac OS X --
 731      * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
 732      * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
 733      * be set in Mac OS X.
 734      * Remove them both from the list of env variables
 735      */
 736     private static String removeMacExpectedVars(String vars) {
 737         // Check for __CF_USER_TEXT_ENCODING
 738         String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
 739                                             +cfUserTextEncoding+",","");
 740         // Check for JAVA_MAIN_CLASS_<pid>
 741         String javaMainClassStr
 742                 = matchAndExtract(cleanedVars,
 743                                     "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
 744         return cleanedVars.replace(javaMainClassStr,"");
 745     }
 746 
 747     /* Only used for AIX --
 748      * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.
 749      * Remove it from the list of env variables
 750      */
 751     private static String removeAixExpectedVars(String vars) {
 752         return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");
 753     }
 754 
 755     private static String sortByLinesWindowsly(String text) {
 756         String[] lines = text.split("\n");
 757         Arrays.sort(lines, new WindowsComparator());
 758         StringBuilder sb = new StringBuilder();
 759         for (String line : lines)
 760             sb.append(line).append("\n");
 761         return sb.toString();
 762     }
 763 
 764     private static void checkMapSanity(Map<String,String> map) {
 765         try {
 766             Set<String> keySet = map.keySet();
 767             Collection<String> values = map.values();
 768             Set<Map.Entry<String,String>> entrySet = map.entrySet();
 769 
 770             equal(entrySet.size(), keySet.size());
 771             equal(entrySet.size(), values.size());
 772 
 773             StringBuilder s1 = new StringBuilder();
 774             for (Map.Entry<String,String> e : entrySet)
 775                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
 776 
 777             StringBuilder s2 = new StringBuilder();
 778             for (String var : keySet)
 779                 s2.append(var + "=" + map.get(var) + "\n");
 780 
 781             equal(s1.toString(), s2.toString());
 782 
 783             Iterator<String> kIter = keySet.iterator();
 784             Iterator<String> vIter = values.iterator();
 785             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
 786 
 787             while (eIter.hasNext()) {
 788                 Map.Entry<String,String> entry = eIter.next();
 789                 String key   = kIter.next();
 790                 String value = vIter.next();
 791                 check(entrySet.contains(entry));
 792                 check(keySet.contains(key));
 793                 check(values.contains(value));
 794                 check(map.containsKey(key));
 795                 check(map.containsValue(value));
 796                 equal(entry.getKey(), key);
 797                 equal(entry.getValue(), value);
 798             }
 799             check(!kIter.hasNext() &&
 800                     !vIter.hasNext());
 801 
 802         } catch (Throwable t) { unexpected(t); }
 803     }
 804 
 805     private static void checkMapEquality(Map<String,String> map1,
 806                                          Map<String,String> map2) {
 807         try {
 808             equal(map1.size(), map2.size());
 809             equal(map1.isEmpty(), map2.isEmpty());
 810             for (String key : map1.keySet()) {
 811                 equal(map1.get(key), map2.get(key));
 812                 check(map2.keySet().contains(key));
 813             }
 814             equal(map1, map2);
 815             equal(map2, map1);
 816             equal(map1.entrySet(), map2.entrySet());
 817             equal(map2.entrySet(), map1.entrySet());
 818             equal(map1.keySet(), map2.keySet());
 819             equal(map2.keySet(), map1.keySet());
 820 
 821             equal(map1.hashCode(), map2.hashCode());
 822             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
 823             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
 824         } catch (Throwable t) { unexpected(t); }
 825     }
 826 
 827     static void checkRedirects(ProcessBuilder pb,
 828                                Redirect in, Redirect out, Redirect err) {
 829         equal(pb.redirectInput(), in);
 830         equal(pb.redirectOutput(), out);
 831         equal(pb.redirectError(), err);
 832     }
 833 
 834     static void redirectIO(ProcessBuilder pb,
 835                            Redirect in, Redirect out, Redirect err) {
 836         pb.redirectInput(in);
 837         pb.redirectOutput(out);
 838         pb.redirectError(err);
 839     }
 840 
 841     static void setFileContents(File file, String contents) {
 842         try {
 843             Writer w = new FileWriter(file);
 844             w.write(contents);
 845             w.close();
 846         } catch (Throwable t) { unexpected(t); }
 847     }
 848 
 849     static String fileContents(File file) {
 850         try {
 851             Reader r = new FileReader(file);
 852             StringBuilder sb = new StringBuilder();
 853             char[] buffer = new char[1024];
 854             int n;
 855             while ((n = r.read(buffer)) != -1)
 856                 sb.append(buffer,0,n);
 857             r.close();
 858             return new String(sb);
 859         } catch (Throwable t) { unexpected(t); return ""; }
 860     }
 861 
 862     static void testIORedirection() throws Throwable {
 863         final File ifile = new File("ifile");
 864         final File ofile = new File("ofile");
 865         final File efile = new File("efile");
 866         ifile.delete();
 867         ofile.delete();
 868         efile.delete();
 869 
 870         //----------------------------------------------------------------
 871         // Check mutual inequality of different types of Redirect
 872         //----------------------------------------------------------------
 873         Redirect[] redirects =
 874             { PIPE,
 875               INHERIT,
 876               DISCARD,
 877               Redirect.from(ifile),
 878               Redirect.to(ifile),
 879               Redirect.appendTo(ifile),
 880               Redirect.from(ofile),
 881               Redirect.to(ofile),
 882               Redirect.appendTo(ofile),
 883             };
 884         for (int i = 0; i < redirects.length; i++)
 885             for (int j = 0; j < redirects.length; j++)
 886                 equal(redirects[i].equals(redirects[j]), (i == j));
 887 
 888         //----------------------------------------------------------------
 889         // Check basic properties of different types of Redirect
 890         //----------------------------------------------------------------
 891         equal(PIPE.type(), Redirect.Type.PIPE);
 892         equal(PIPE.toString(), "PIPE");
 893         equal(PIPE.file(), null);
 894 
 895         equal(INHERIT.type(), Redirect.Type.INHERIT);
 896         equal(INHERIT.toString(), "INHERIT");
 897         equal(INHERIT.file(), null);
 898 
 899         equal(DISCARD.type(), Redirect.Type.WRITE);
 900         equal(DISCARD.toString(), "WRITE");
 901         equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));
 902 
 903         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
 904         equal(Redirect.from(ifile).toString(),
 905               "redirect to read from file \"ifile\"");
 906         equal(Redirect.from(ifile).file(), ifile);
 907         equal(Redirect.from(ifile),
 908               Redirect.from(ifile));
 909         equal(Redirect.from(ifile).hashCode(),
 910               Redirect.from(ifile).hashCode());
 911 
 912         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
 913         equal(Redirect.to(ofile).toString(),
 914               "redirect to write to file \"ofile\"");
 915         equal(Redirect.to(ofile).file(), ofile);
 916         equal(Redirect.to(ofile),
 917               Redirect.to(ofile));
 918         equal(Redirect.to(ofile).hashCode(),
 919               Redirect.to(ofile).hashCode());
 920 
 921         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
 922         equal(Redirect.appendTo(efile).toString(),
 923               "redirect to append to file \"efile\"");
 924         equal(Redirect.appendTo(efile).file(), efile);
 925         equal(Redirect.appendTo(efile),
 926               Redirect.appendTo(efile));
 927         equal(Redirect.appendTo(efile).hashCode(),
 928               Redirect.appendTo(efile).hashCode());
 929 
 930         //----------------------------------------------------------------
 931         // Check initial values of redirects
 932         //----------------------------------------------------------------
 933         List<String> childArgs = new ArrayList<String>(javaChildArgs);
 934         childArgs.add("testIO");
 935         final ProcessBuilder pb = new ProcessBuilder(childArgs);
 936         checkRedirects(pb, PIPE, PIPE, PIPE);
 937 
 938         //----------------------------------------------------------------
 939         // Check inheritIO
 940         //----------------------------------------------------------------
 941         pb.inheritIO();
 942         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 943 
 944         //----------------------------------------------------------------
 945         // Check DISCARD for stdout,stderr
 946         //----------------------------------------------------------------
 947         redirectIO(pb, INHERIT, DISCARD, DISCARD);
 948         checkRedirects(pb, INHERIT, DISCARD, DISCARD);
 949 
 950         //----------------------------------------------------------------
 951         // Check setters and getters agree
 952         //----------------------------------------------------------------
 953         pb.redirectInput(ifile);
 954         equal(pb.redirectInput().file(), ifile);
 955         equal(pb.redirectInput(), Redirect.from(ifile));
 956 
 957         pb.redirectOutput(ofile);
 958         equal(pb.redirectOutput().file(), ofile);
 959         equal(pb.redirectOutput(), Redirect.to(ofile));
 960 
 961         pb.redirectError(efile);
 962         equal(pb.redirectError().file(), efile);
 963         equal(pb.redirectError(), Redirect.to(efile));
 964 
 965         THROWS(IllegalArgumentException.class,
 966                () -> pb.redirectInput(Redirect.to(ofile)),
 967                () -> pb.redirectOutput(Redirect.from(ifile)),
 968                () -> pb.redirectError(Redirect.from(ifile)),
 969                () -> pb.redirectInput(DISCARD));
 970 
 971         THROWS(NullPointerException.class,
 972                 () -> pb.redirectInput((File)null),
 973                 () -> pb.redirectOutput((File)null),
 974                 () -> pb.redirectError((File)null),
 975                 () -> pb.redirectInput((Redirect)null),
 976                 () -> pb.redirectOutput((Redirect)null),
 977                 () -> pb.redirectError((Redirect)null));
 978 
 979         THROWS(IOException.class,
 980                // Input file does not exist
 981                () -> pb.start());
 982         setFileContents(ifile, "standard input");
 983 
 984         //----------------------------------------------------------------
 985         // Writing to non-existent files
 986         //----------------------------------------------------------------
 987         {
 988             ProcessResults r = run(pb);
 989             equal(r.exitValue(), 0);
 990             equal(fileContents(ofile), "standard output");
 991             equal(fileContents(efile), "standard error");
 992             equal(r.out(), "");
 993             equal(r.err(), "");
 994             ofile.delete();
 995             efile.delete();
 996         }
 997 
 998         //----------------------------------------------------------------
 999         // Both redirectErrorStream + redirectError
1000         //----------------------------------------------------------------
1001         {
1002             pb.redirectErrorStream(true);
1003             ProcessResults r = run(pb);
1004             equal(r.exitValue(), 0);
1005             equal(fileContents(ofile),
1006                     "standard error" + "standard output");
1007             equal(fileContents(efile), "");
1008             equal(r.out(), "");
1009             equal(r.err(), "");
1010             ofile.delete();
1011             efile.delete();
1012         }
1013 
1014         //----------------------------------------------------------------
1015         // Appending to existing files
1016         //----------------------------------------------------------------
1017         {
1018             setFileContents(ofile, "ofile-contents");
1019             setFileContents(efile, "efile-contents");
1020             pb.redirectOutput(Redirect.appendTo(ofile));
1021             pb.redirectError(Redirect.appendTo(efile));
1022             pb.redirectErrorStream(false);
1023             ProcessResults r = run(pb);
1024             equal(r.exitValue(), 0);
1025             equal(fileContents(ofile),
1026                   "ofile-contents" + "standard output");
1027             equal(fileContents(efile),
1028                   "efile-contents" + "standard error");
1029             equal(r.out(), "");
1030             equal(r.err(), "");
1031             ofile.delete();
1032             efile.delete();
1033         }
1034 
1035         //----------------------------------------------------------------
1036         // Replacing existing files
1037         //----------------------------------------------------------------
1038         {
1039             setFileContents(ofile, "ofile-contents");
1040             setFileContents(efile, "efile-contents");
1041             pb.redirectOutput(ofile);
1042             pb.redirectError(Redirect.to(efile));
1043             ProcessResults r = run(pb);
1044             equal(r.exitValue(), 0);
1045             equal(fileContents(ofile), "standard output");
1046             equal(fileContents(efile), "standard error");
1047             equal(r.out(), "");
1048             equal(r.err(), "");
1049             ofile.delete();
1050             efile.delete();
1051         }
1052 
1053         //----------------------------------------------------------------
1054         // Appending twice to the same file?
1055         //----------------------------------------------------------------
1056         {
1057             setFileContents(ofile, "ofile-contents");
1058             setFileContents(efile, "efile-contents");
1059             Redirect appender = Redirect.appendTo(ofile);
1060             pb.redirectOutput(appender);
1061             pb.redirectError(appender);
1062             ProcessResults r = run(pb);
1063             equal(r.exitValue(), 0);
1064             equal(fileContents(ofile),
1065                   "ofile-contents" +
1066                   "standard error" +
1067                   "standard output");
1068             equal(fileContents(efile), "efile-contents");
1069             equal(r.out(), "");
1070             equal(r.err(), "");
1071             ifile.delete();
1072             ofile.delete();
1073             efile.delete();
1074         }
1075 
1076         //----------------------------------------------------------------
1077         // DISCARDing output
1078         //----------------------------------------------------------------
1079         {
1080             setFileContents(ifile, "standard input");
1081             pb.redirectOutput(DISCARD);
1082             pb.redirectError(DISCARD);
1083             ProcessResults r = run(pb);
1084             equal(r.exitValue(), 0);
1085             equal(r.out(), "");
1086             equal(r.err(), "");
1087         }
1088 
1089         //----------------------------------------------------------------
1090         // DISCARDing output and redirecting error
1091         //----------------------------------------------------------------
1092         {
1093             setFileContents(ifile, "standard input");
1094             setFileContents(ofile, "ofile-contents");
1095             setFileContents(efile, "efile-contents");
1096             pb.redirectOutput(DISCARD);
1097             pb.redirectError(efile);
1098             ProcessResults r = run(pb);
1099             equal(r.exitValue(), 0);
1100             equal(fileContents(ofile), "ofile-contents");
1101             equal(fileContents(efile), "standard error");
1102             equal(r.out(), "");
1103             equal(r.err(), "");
1104             ofile.delete();
1105             efile.delete();
1106         }
1107 
1108         //----------------------------------------------------------------
1109         // DISCARDing error and redirecting output
1110         //----------------------------------------------------------------
1111         {
1112             setFileContents(ifile, "standard input");
1113             setFileContents(ofile, "ofile-contents");
1114             setFileContents(efile, "efile-contents");
1115             pb.redirectOutput(ofile);
1116             pb.redirectError(DISCARD);
1117             ProcessResults r = run(pb);
1118             equal(r.exitValue(), 0);
1119             equal(fileContents(ofile), "standard output");
1120             equal(fileContents(efile), "efile-contents");
1121             equal(r.out(), "");
1122             equal(r.err(), "");
1123             ofile.delete();
1124             efile.delete();
1125         }
1126 
1127         //----------------------------------------------------------------
1128         // DISCARDing output and merging error into output
1129         //----------------------------------------------------------------
1130         {
1131             setFileContents(ifile, "standard input");
1132             setFileContents(ofile, "ofile-contents");
1133             setFileContents(efile, "efile-contents");
1134             pb.redirectOutput(DISCARD);
1135             pb.redirectErrorStream(true);
1136             pb.redirectError(efile);
1137             ProcessResults r = run(pb);
1138             equal(r.exitValue(), 0);
1139             equal(fileContents(ofile), "ofile-contents");   // untouched
1140             equal(fileContents(efile), "");                 // empty
1141             equal(r.out(), "");
1142             equal(r.err(), "");
1143             ifile.delete();
1144             ofile.delete();
1145             efile.delete();
1146             pb.redirectErrorStream(false);                  // reset for next test
1147         }
1148 
1149         //----------------------------------------------------------------
1150         // Testing INHERIT is harder.
1151         // Note that this requires __FOUR__ nested JVMs involved in one test,
1152         // if you count the harness JVM.
1153         //----------------------------------------------------------------
1154         for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
1155             redirectIO(pb, PIPE, PIPE, PIPE);
1156             List<String> command = pb.command();
1157             command.set(command.size() - 1, testName);
1158             Process p = pb.start();
1159             new PrintStream(p.getOutputStream()).print("standard input");
1160             p.getOutputStream().close();
1161             ProcessResults r = run(p);
1162             equal(r.exitValue(), 0);
1163             equal(r.out(), "standard output");
1164             equal(r.err(), "standard error");
1165         }
1166 
1167         //----------------------------------------------------------------
1168         // Test security implications of I/O redirection
1169         //----------------------------------------------------------------
1170 
1171         // Read access to current directory is always granted;
1172         // So create a tmpfile for input instead.
1173         final File tmpFile = File.createTempFile("Basic", "tmp");
1174         setFileContents(tmpFile, "standard input");
1175 
1176         final Policy policy = new Policy();
1177         Policy.setPolicy(policy);
1178         System.setSecurityManager(new SecurityManager());
1179         try {
1180             final Permission xPermission
1181                 = new FilePermission("<<ALL FILES>>", "execute");
1182             final Permission rxPermission
1183                 = new FilePermission("<<ALL FILES>>", "read,execute");
1184             final Permission wxPermission
1185                 = new FilePermission("<<ALL FILES>>", "write,execute");
1186             final Permission rwxPermission
1187                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
1188 
1189             THROWS(SecurityException.class,
1190                    () -> { policy.setPermissions(xPermission);
1191                            redirectIO(pb, from(tmpFile), PIPE, PIPE);
1192                            pb.start();},
1193                    () -> { policy.setPermissions(rxPermission);
1194                            redirectIO(pb, PIPE, to(ofile), PIPE);
1195                            pb.start();},
1196                    () -> { policy.setPermissions(rxPermission);
1197                            redirectIO(pb, PIPE, PIPE, to(efile));
1198                            pb.start();});
1199 
1200             {
1201                 policy.setPermissions(rxPermission);
1202                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
1203                 ProcessResults r = run(pb);
1204                 equal(r.out(), "standard output");
1205                 equal(r.err(), "standard error");
1206             }
1207 
1208             {
1209                 policy.setPermissions(wxPermission);
1210                 redirectIO(pb, PIPE, to(ofile), to(efile));
1211                 Process p = pb.start();
1212                 new PrintStream(p.getOutputStream()).print("standard input");
1213                 p.getOutputStream().close();
1214                 ProcessResults r = run(p);
1215                 policy.setPermissions(rwxPermission);
1216                 equal(fileContents(ofile), "standard output");
1217                 equal(fileContents(efile), "standard error");
1218             }
1219 
1220             {
1221                 policy.setPermissions(rwxPermission);
1222                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1223                 ProcessResults r = run(pb);
1224                 policy.setPermissions(rwxPermission);
1225                 equal(fileContents(ofile), "standard output");
1226                 equal(fileContents(efile), "standard error");
1227             }
1228 
1229         } finally {
1230             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1231             System.setSecurityManager(null);
1232             tmpFile.delete();
1233             ifile.delete();
1234             ofile.delete();
1235             efile.delete();
1236         }
1237     }
1238 
1239     static void checkProcessPid() {
1240         ProcessBuilder pb = new ProcessBuilder();
1241         List<String> list = new ArrayList<String>(javaChildArgs);
1242         list.add("pid");
1243         pb.command(list);
1244         try {
1245             Process p = pb.start();
1246             String s = commandOutput(p);
1247             long actualPid = Long.valueOf(s.trim());
1248             long expectedPid = p.pid();
1249             equal(actualPid, expectedPid);
1250         } catch (Throwable t) {
1251             unexpected(t);
1252         }
1253 
1254 
1255         // Test the default implementation of Process.getPid
1256         DelegatingProcess p = new DelegatingProcess(null);
1257         THROWS(UnsupportedOperationException.class,
1258                 () -> p.pid(),
1259                 () -> p.toHandle(),
1260                 () -> p.supportsNormalTermination(),
1261                 () -> p.children(),
1262                 () -> p.descendants());
1263 
1264     }
1265 
1266     private static void realMain(String[] args) throws Throwable {
1267         if (Windows.is())
1268             System.out.println("This appears to be a Windows system.");
1269         if (Unix.is())
1270             System.out.println("This appears to be a Unix system.");
1271         if (UnicodeOS.is())
1272             System.out.println("This appears to be a Unicode-based OS.");
1273 
1274         try { testIORedirection(); }
1275         catch (Throwable t) { unexpected(t); }
1276 
1277         //----------------------------------------------------------------
1278         // Basic tests for getPid()
1279         //----------------------------------------------------------------
1280         checkProcessPid();
1281 
1282         //----------------------------------------------------------------
1283         // Basic tests for setting, replacing and deleting envvars
1284         //----------------------------------------------------------------
1285         try {
1286             ProcessBuilder pb = new ProcessBuilder();
1287             Map<String,String> environ = pb.environment();
1288 
1289             // New env var
1290             environ.put("QUUX", "BAR");
1291             equal(environ.get("QUUX"), "BAR");
1292             equal(getenvInChild(pb,"QUUX"), "BAR");
1293 
1294             // Modify env var
1295             environ.put("QUUX","bear");
1296             equal(environ.get("QUUX"), "bear");
1297             equal(getenvInChild(pb,"QUUX"), "bear");
1298             checkMapSanity(environ);
1299 
1300             // Remove env var
1301             environ.remove("QUUX");
1302             equal(environ.get("QUUX"), null);
1303             equal(getenvInChild(pb,"QUUX"), "null");
1304             checkMapSanity(environ);
1305 
1306             // Remove non-existent env var
1307             environ.remove("QUUX");
1308             equal(environ.get("QUUX"), null);
1309             equal(getenvInChild(pb,"QUUX"), "null");
1310             checkMapSanity(environ);
1311         } catch (Throwable t) { unexpected(t); }
1312 
1313         //----------------------------------------------------------------
1314         // Pass Empty environment to child
1315         //----------------------------------------------------------------
1316         try {
1317             ProcessBuilder pb = new ProcessBuilder();
1318             pb.environment().clear();
1319             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1320             expected = AIX.is() ? "LIBPATH="+libpath+",": expected;
1321             if (Windows.is()) {
1322                 pb.environment().put("SystemRoot", systemRoot);
1323             }
1324             if (AIX.is()) {
1325                 pb.environment().put("LIBPATH", libpath);
1326             }
1327             String result = getenvInChild(pb);
1328             if (MacOSX.is()) {
1329                 result = removeMacExpectedVars(result);
1330             }
1331             if (AIX.is()) {
1332                 result = removeAixExpectedVars(result);
1333             }
1334             equal(result, expected);
1335         } catch (Throwable t) { unexpected(t); }
1336 
1337         //----------------------------------------------------------------
1338         // System.getenv() is read-only.
1339         //----------------------------------------------------------------
1340         THROWS(UnsupportedOperationException.class,
1341                () -> getenv().put("FOO","BAR"),
1342                () -> getenv().remove("PATH"),
1343                () -> getenv().keySet().remove("PATH"),
1344                () -> getenv().values().remove("someValue"));
1345 
1346         try {
1347             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1348             if (! c.isEmpty())
1349                 try {
1350                     c.iterator().next().setValue("foo");
1351                     fail("Expected UnsupportedOperationException not thrown");
1352                 } catch (UnsupportedOperationException e) {} // OK
1353         } catch (Throwable t) { unexpected(t); }
1354 
1355         //----------------------------------------------------------------
1356         // System.getenv() always returns the same object in our implementation.
1357         //----------------------------------------------------------------
1358         try {
1359             check(System.getenv() == System.getenv());
1360         } catch (Throwable t) { unexpected(t); }
1361 
1362         //----------------------------------------------------------------
1363         // You can't create an env var name containing "=",
1364         // or an env var name or value containing NUL.
1365         //----------------------------------------------------------------
1366         {
1367             final Map<String,String> m = new ProcessBuilder().environment();
1368             THROWS(IllegalArgumentException.class,
1369                    () -> m.put("FOO=","BAR"),
1370                    () -> m.put("FOO\u0000","BAR"),
1371                    () -> m.put("FOO","BAR\u0000"));
1372         }
1373 
1374         //----------------------------------------------------------------
1375         // Commands must never be null.
1376         //----------------------------------------------------------------
1377         THROWS(NullPointerException.class,
1378                () -> new ProcessBuilder((List<String>)null),
1379                () -> new ProcessBuilder().command((List<String>)null));
1380 
1381         //----------------------------------------------------------------
1382         // Put in a command; get the same one back out.
1383         //----------------------------------------------------------------
1384         try {
1385             List<String> command = new ArrayList<String>();
1386             ProcessBuilder pb = new ProcessBuilder(command);
1387             check(pb.command() == command);
1388             List<String> command2 = new ArrayList<String>(2);
1389             command2.add("foo");
1390             command2.add("bar");
1391             pb.command(command2);
1392             check(pb.command() == command2);
1393             pb.command("foo", "bar");
1394             check(pb.command() != command2 && pb.command().equals(command2));
1395             pb.command(command2);
1396             command2.add("baz");
1397             equal(pb.command().get(2), "baz");
1398         } catch (Throwable t) { unexpected(t); }
1399 
1400         //----------------------------------------------------------------
1401         // Commands must contain at least one element.
1402         //----------------------------------------------------------------
1403         THROWS(IndexOutOfBoundsException.class,
1404                () -> new ProcessBuilder().start(),
1405                () -> new ProcessBuilder(new ArrayList<String>()).start(),
1406                () -> Runtime.getRuntime().exec(new String[]{}));
1407 
1408         //----------------------------------------------------------------
1409         // Commands must not contain null elements at start() time.
1410         //----------------------------------------------------------------
1411         THROWS(NullPointerException.class,
1412                () -> new ProcessBuilder("foo",null,"bar").start(),
1413                () -> new ProcessBuilder((String)null).start(),
1414                () -> new ProcessBuilder(new String[]{null}).start(),
1415                () -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());
1416 
1417         //----------------------------------------------------------------
1418         // Command lists are growable.
1419         //----------------------------------------------------------------
1420         try {
1421             new ProcessBuilder().command().add("foo");
1422             new ProcessBuilder("bar").command().add("foo");
1423             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1424         } catch (Throwable t) { unexpected(t); }
1425 
1426         //----------------------------------------------------------------
1427         // Nulls in environment updates generate NullPointerException
1428         //----------------------------------------------------------------
1429         try {
1430             final Map<String,String> env = new ProcessBuilder().environment();
1431             THROWS(NullPointerException.class,
1432                    () -> env.put("foo",null),
1433                    () -> env.put(null,"foo"),
1434                    () -> env.remove(null),
1435                    () -> { for (Map.Entry<String,String> e : env.entrySet())
1436                                e.setValue(null);},
1437                    () -> Runtime.getRuntime().exec(new String[]{"foo"},
1438                                                    new String[]{null}));
1439         } catch (Throwable t) { unexpected(t); }
1440 
1441         //----------------------------------------------------------------
1442         // Non-String types in environment updates generate ClassCastException
1443         //----------------------------------------------------------------
1444         try {
1445             final Map<String,String> env = new ProcessBuilder().environment();
1446             THROWS(ClassCastException.class,
1447                    () -> env.remove(TRUE),
1448                    () -> env.keySet().remove(TRUE),
1449                    () -> env.values().remove(TRUE),
1450                    () -> env.entrySet().remove(TRUE));
1451         } catch (Throwable t) { unexpected(t); }
1452 
1453         //----------------------------------------------------------------
1454         // Check query operations on environment maps
1455         //----------------------------------------------------------------
1456         try {
1457             List<Map<String,String>> envs =
1458                 new ArrayList<Map<String,String>>(2);
1459             envs.add(System.getenv());
1460             envs.add(new ProcessBuilder().environment());
1461             for (final Map<String,String> env : envs) {
1462                 //----------------------------------------------------------------
1463                 // Nulls in environment queries are forbidden.
1464                 //----------------------------------------------------------------
1465                 THROWS(NullPointerException.class,
1466                        () -> getenv(null),
1467                        () -> env.get(null),
1468                        () -> env.containsKey(null),
1469                        () -> env.containsValue(null),
1470                        () -> env.keySet().contains(null),
1471                        () -> env.values().contains(null));
1472 
1473                 //----------------------------------------------------------------
1474                 // Non-String types in environment queries are forbidden.
1475                 //----------------------------------------------------------------
1476                 THROWS(ClassCastException.class,
1477                        () -> env.get(TRUE),
1478                        () -> env.containsKey(TRUE),
1479                        () -> env.containsValue(TRUE),
1480                        () -> env.keySet().contains(TRUE),
1481                        () -> env.values().contains(TRUE));
1482 
1483                 //----------------------------------------------------------------
1484                 // Illegal String values in environment queries are (grumble) OK
1485                 //----------------------------------------------------------------
1486                 equal(env.get("\u0000"), null);
1487                 check(! env.containsKey("\u0000"));
1488                 check(! env.containsValue("\u0000"));
1489                 check(! env.keySet().contains("\u0000"));
1490                 check(! env.values().contains("\u0000"));
1491             }
1492 
1493         } catch (Throwable t) { unexpected(t); }
1494 
1495         try {
1496             final Set<Map.Entry<String,String>> entrySet =
1497                 new ProcessBuilder().environment().entrySet();
1498             THROWS(NullPointerException.class,
1499                    () -> entrySet.contains(null));
1500             THROWS(ClassCastException.class,
1501                    () -> entrySet.contains(TRUE),
1502                    () -> entrySet.contains(
1503                              new SimpleImmutableEntry<Boolean,String>(TRUE,"")));
1504 
1505             check(! entrySet.contains
1506                   (new SimpleImmutableEntry<String,String>("", "")));
1507         } catch (Throwable t) { unexpected(t); }
1508 
1509         //----------------------------------------------------------------
1510         // Put in a directory; get the same one back out.
1511         //----------------------------------------------------------------
1512         try {
1513             ProcessBuilder pb = new ProcessBuilder();
1514             File foo = new File("foo");
1515             equal(pb.directory(), null);
1516             equal(pb.directory(foo).directory(), foo);
1517             equal(pb.directory(null).directory(), null);
1518         } catch (Throwable t) { unexpected(t); }
1519 
1520         //----------------------------------------------------------------
1521         // If round-trip conversion works, check envvar pass-through to child
1522         //----------------------------------------------------------------
1523         try {
1524             testEncoding("ASCII",   "xyzzy");
1525             testEncoding("Latin1",  "\u00f1\u00e1");
1526             testEncoding("Unicode", "\u22f1\u11e1");
1527         } catch (Throwable t) { unexpected(t); }
1528 
1529         //----------------------------------------------------------------
1530         // A surprisingly large number of ways to delete an environment var.
1531         //----------------------------------------------------------------
1532         testVariableDeleter(new EnvironmentFrobber() {
1533                 public void doIt(Map<String,String> environ) {
1534                     environ.remove("Foo");}});
1535 
1536         testVariableDeleter(new EnvironmentFrobber() {
1537                 public void doIt(Map<String,String> environ) {
1538                     environ.keySet().remove("Foo");}});
1539 
1540         testVariableDeleter(new EnvironmentFrobber() {
1541                 public void doIt(Map<String,String> environ) {
1542                     environ.values().remove("BAAR");}});
1543 
1544         testVariableDeleter(new EnvironmentFrobber() {
1545                 public void doIt(Map<String,String> environ) {
1546                     // Legally fabricate a ProcessEnvironment.StringEntry,
1547                     // even though it's private.
1548                     Map<String,String> environ2
1549                         = new ProcessBuilder().environment();
1550                     environ2.clear();
1551                     environ2.put("Foo","BAAR");
1552                     // Subtlety alert.
1553                     Map.Entry<String,String> e
1554                         = environ2.entrySet().iterator().next();
1555                     environ.entrySet().remove(e);}});
1556 
1557         testVariableDeleter(new EnvironmentFrobber() {
1558                 public void doIt(Map<String,String> environ) {
1559                     Map.Entry<String,String> victim = null;
1560                     for (Map.Entry<String,String> e : environ.entrySet())
1561                         if (e.getKey().equals("Foo"))
1562                             victim = e;
1563                     if (victim != null)
1564                         environ.entrySet().remove(victim);}});
1565 
1566         testVariableDeleter(new EnvironmentFrobber() {
1567                 public void doIt(Map<String,String> environ) {
1568                     Iterator<String> it = environ.keySet().iterator();
1569                     while (it.hasNext()) {
1570                         String val = it.next();
1571                         if (val.equals("Foo"))
1572                             it.remove();}}});
1573 
1574         testVariableDeleter(new EnvironmentFrobber() {
1575                 public void doIt(Map<String,String> environ) {
1576                     Iterator<Map.Entry<String,String>> it
1577                         = environ.entrySet().iterator();
1578                     while (it.hasNext()) {
1579                         Map.Entry<String,String> e = it.next();
1580                         if (e.getKey().equals("Foo"))
1581                             it.remove();}}});
1582 
1583         testVariableDeleter(new EnvironmentFrobber() {
1584                 public void doIt(Map<String,String> environ) {
1585                     Iterator<String> it = environ.values().iterator();
1586                     while (it.hasNext()) {
1587                         String val = it.next();
1588                         if (val.equals("BAAR"))
1589                             it.remove();}}});
1590 
1591         //----------------------------------------------------------------
1592         // A surprisingly small number of ways to add an environment var.
1593         //----------------------------------------------------------------
1594         testVariableAdder(new EnvironmentFrobber() {
1595                 public void doIt(Map<String,String> environ) {
1596                     environ.put("Foo","Bahrein");}});
1597 
1598         //----------------------------------------------------------------
1599         // A few ways to modify an environment var.
1600         //----------------------------------------------------------------
1601         testVariableModifier(new EnvironmentFrobber() {
1602                 public void doIt(Map<String,String> environ) {
1603                     environ.put("Foo","NewValue");}});
1604 
1605         testVariableModifier(new EnvironmentFrobber() {
1606                 public void doIt(Map<String,String> environ) {
1607                     for (Map.Entry<String,String> e : environ.entrySet())
1608                         if (e.getKey().equals("Foo"))
1609                             e.setValue("NewValue");}});
1610 
1611         //----------------------------------------------------------------
1612         // Fiddle with environment sizes
1613         //----------------------------------------------------------------
1614         try {
1615             Map<String,String> environ = new ProcessBuilder().environment();
1616             int size = environ.size();
1617             checkSizes(environ, size);
1618 
1619             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1620             checkSizes(environ, size+1);
1621 
1622             // Check for environment independence
1623             new ProcessBuilder().environment().clear();
1624 
1625             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1626             checkSizes(environ, size+1);
1627 
1628             environ.remove("UnLiKeLYeNVIROmtNam");
1629             checkSizes(environ, size);
1630 
1631             environ.clear();
1632             checkSizes(environ, 0);
1633 
1634             environ.clear();
1635             checkSizes(environ, 0);
1636 
1637             environ = new ProcessBuilder().environment();
1638             environ.keySet().clear();
1639             checkSizes(environ, 0);
1640 
1641             environ = new ProcessBuilder().environment();
1642             environ.entrySet().clear();
1643             checkSizes(environ, 0);
1644 
1645             environ = new ProcessBuilder().environment();
1646             environ.values().clear();
1647             checkSizes(environ, 0);
1648         } catch (Throwable t) { unexpected(t); }
1649 
1650         //----------------------------------------------------------------
1651         // Check that various map invariants hold
1652         //----------------------------------------------------------------
1653         checkMapSanity(new ProcessBuilder().environment());
1654         checkMapSanity(System.getenv());
1655         checkMapEquality(new ProcessBuilder().environment(),
1656                          new ProcessBuilder().environment());
1657 
1658 
1659         //----------------------------------------------------------------
1660         // Check effects on external "env" command.
1661         //----------------------------------------------------------------
1662         try {
1663             Set<String> env1 = new HashSet<String>
1664                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1665 
1666             ProcessBuilder pb = new ProcessBuilder();
1667             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1668 
1669             Set<String> env2 = new HashSet<String>
1670                 (Arrays.asList(nativeEnv(pb).split("\n")));
1671 
1672             check(env2.size() == env1.size() + 1);
1673             env1.add("QwErTyUiOp=AsDfGhJk");
1674             check(env1.equals(env2));
1675         } catch (Throwable t) { unexpected(t); }
1676 
1677         //----------------------------------------------------------------
1678         // Test Runtime.exec(...envp...)
1679         // Check for sort order of environment variables on Windows.
1680         //----------------------------------------------------------------
1681         try {
1682             String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1683             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1684             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1685                             "+=+", "_=_", "~=~", systemRoot};
1686             String output = nativeEnv(envp);
1687             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1688             // On Windows, Java must keep the environment sorted.
1689             // Order is random on Unix, so this test does the sort.
1690             if (! Windows.is())
1691                 output = sortByLinesWindowsly(output);
1692             equal(output, expected);
1693         } catch (Throwable t) { unexpected(t); }
1694 
1695         //----------------------------------------------------------------
1696         // Test Runtime.exec(...envp...)
1697         // and check SystemRoot gets set automatically on Windows
1698         //----------------------------------------------------------------
1699         try {
1700             if (Windows.is()) {
1701                 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1702                 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1703                                 "+=+", "_=_", "~=~"};
1704                 String output = nativeEnv(envp);
1705                 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1706                 equal(output, expected);
1707             }
1708         } catch (Throwable t) { unexpected(t); }
1709 
1710         //----------------------------------------------------------------
1711         // System.getenv() must be consistent with System.getenv(String)
1712         //----------------------------------------------------------------
1713         try {
1714             for (Map.Entry<String,String> e : getenv().entrySet())
1715                 equal(getenv(e.getKey()), e.getValue());
1716         } catch (Throwable t) { unexpected(t); }
1717 
1718         //----------------------------------------------------------------
1719         // Fiddle with working directory in child
1720         //----------------------------------------------------------------
1721         try {
1722             String canonicalUserDir =
1723                 new File(System.getProperty("user.dir")).getCanonicalPath();
1724             String[] sdirs = new String[]
1725                 {".", "..", "/", "/bin",
1726                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1727                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1728             for (String sdir : sdirs) {
1729                 File dir = new File(sdir);
1730                 if (! (dir.isDirectory() && dir.exists()))
1731                     continue;
1732                 out.println("Testing directory " + dir);
1733                 //dir = new File(dir.getCanonicalPath());
1734 
1735                 ProcessBuilder pb = new ProcessBuilder();
1736                 equal(pb.directory(), null);
1737                 equal(pwdInChild(pb), canonicalUserDir);
1738 
1739                 pb.directory(dir);
1740                 equal(pb.directory(), dir);
1741                 equal(pwdInChild(pb), dir.getCanonicalPath());
1742 
1743                 pb.directory(null);
1744                 equal(pb.directory(), null);
1745                 equal(pwdInChild(pb), canonicalUserDir);
1746 
1747                 pb.directory(dir);
1748             }
1749         } catch (Throwable t) { unexpected(t); }
1750 
1751         //----------------------------------------------------------------
1752         // Working directory with Unicode in child
1753         //----------------------------------------------------------------
1754         try {
1755             if (UnicodeOS.is()) {
1756                 File dir = new File(System.getProperty("test.dir", "."),
1757                                     "ProcessBuilderDir\u4e00\u4e02");
1758                 try {
1759                     if (!dir.exists())
1760                         dir.mkdir();
1761                     out.println("Testing Unicode directory:" + dir);
1762                     ProcessBuilder pb = new ProcessBuilder();
1763                     pb.directory(dir);
1764                     equal(pwdInChild(pb), dir.getCanonicalPath());
1765                 } finally {
1766                     if (dir.exists())
1767                         dir.delete();
1768                 }
1769             }
1770         } catch (Throwable t) { unexpected(t); }
1771 
1772         //----------------------------------------------------------------
1773         // OOME in child allocating maximally sized array
1774         // Test for hotspot/jvmti bug 6850957
1775         //----------------------------------------------------------------
1776         try {
1777             List<String> list = new ArrayList<String>(javaChildArgs);
1778             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1779                                       javaExe));
1780             list.add("ArrayOOME");
1781             ProcessResults r = run(new ProcessBuilder(list));
1782             check(r.err().contains("java.lang.OutOfMemoryError:"));
1783             check(r.err().contains(javaExe));
1784             check(r.err().contains(System.getProperty("java.version")));
1785             equal(r.exitValue(), 1);
1786         } catch (Throwable t) { unexpected(t); }
1787 
1788         //----------------------------------------------------------------
1789         // Windows has tricky semi-case-insensitive semantics
1790         //----------------------------------------------------------------
1791         if (Windows.is())
1792             try {
1793                 out.println("Running case insensitve variable tests");
1794                 for (String[] namePair :
1795                          new String[][]
1796                     { new String[]{"PATH","PaTh"},
1797                       new String[]{"home","HOME"},
1798                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1799                     check((getenv(namePair[0]) == null &&
1800                            getenv(namePair[1]) == null)
1801                           ||
1802                           getenv(namePair[0]).equals(getenv(namePair[1])),
1803                           "Windows environment variables are not case insensitive");
1804                 }
1805             } catch (Throwable t) { unexpected(t); }
1806 
1807         //----------------------------------------------------------------
1808         // Test proper Unicode child environment transfer
1809         //----------------------------------------------------------------
1810         if (UnicodeOS.is())
1811             try {
1812                 ProcessBuilder pb = new ProcessBuilder();
1813                 pb.environment().put("\u1234","\u5678");
1814                 pb.environment().remove("PATH");
1815                 equal(getenvInChild1234(pb), "\u5678");
1816             } catch (Throwable t) { unexpected(t); }
1817 
1818 
1819         //----------------------------------------------------------------
1820         // Test Runtime.exec(...envp...) with envstrings with initial `='
1821         //----------------------------------------------------------------
1822         try {
1823             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1824             childArgs.add("System.getenv()");
1825             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1826             String[] envp;
1827             String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};
1828             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1829             if (Windows.is()) {
1830                 envp = envpWin;
1831             } else {
1832                 envp = envpOth;
1833             }
1834             Process p = Runtime.getRuntime().exec(cmdp, envp);
1835             String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";
1836             expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;
1837             String commandOutput = commandOutput(p);
1838             if (MacOSX.is()) {
1839                 commandOutput = removeMacExpectedVars(commandOutput);
1840             }
1841             if (AIX.is()) {
1842                 commandOutput = removeAixExpectedVars(commandOutput);
1843             }
1844             equal(commandOutput, expected);
1845             if (Windows.is()) {
1846                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1847                 pb.environment().clear();
1848                 pb.environment().put("SystemRoot", systemRoot);
1849                 pb.environment().put("=ExitValue", "3");
1850                 pb.environment().put("=C:", "\\");
1851                 equal(commandOutput(pb), expected);
1852             }
1853         } catch (Throwable t) { unexpected(t); }
1854 
1855         //----------------------------------------------------------------
1856         // Test Runtime.exec(...envp...) with envstrings without any `='
1857         //----------------------------------------------------------------
1858         try {
1859             String[] cmdp = {"echo"};
1860             String[] envp = {"Hello", "World"}; // Yuck!
1861             Process p = Runtime.getRuntime().exec(cmdp, envp);
1862             equal(commandOutput(p), "\n");
1863         } catch (Throwable t) { unexpected(t); }
1864 
1865         //----------------------------------------------------------------
1866         // Test Runtime.exec(...envp...) with envstrings containing NULs
1867         //----------------------------------------------------------------
1868         try {
1869             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1870             childArgs.add("System.getenv()");
1871             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1872             String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1873                              "FO\u0000=B\u0000R"};
1874             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1875                              "FO\u0000=B\u0000R"};
1876             String[] envp;
1877             if (Windows.is()) {
1878                 envp = envpWin;
1879             } else {
1880                 envp = envpOth;
1881             }
1882             System.out.println ("cmdp");
1883             for (int i=0; i<cmdp.length; i++) {
1884                 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);
1885             }
1886             System.out.println ("envp");
1887             for (int i=0; i<envp.length; i++) {
1888                 System.out.printf ("envp %d: %s\n", i, envp[i]);
1889             }
1890             Process p = Runtime.getRuntime().exec(cmdp, envp);
1891             String commandOutput = commandOutput(p);
1892             if (MacOSX.is()) {
1893                 commandOutput = removeMacExpectedVars(commandOutput);
1894             }
1895             if (AIX.is()) {
1896                 commandOutput = removeAixExpectedVars(commandOutput);
1897             }
1898             check(commandOutput.equals(Windows.is()
1899                     ? "LC_ALL=C,SystemRoot="+systemRoot+","
1900                     : AIX.is()
1901                             ? "LC_ALL=C,LIBPATH="+libpath+","
1902                             : "LC_ALL=C,"),
1903                   "Incorrect handling of envstrings containing NULs");
1904         } catch (Throwable t) { unexpected(t); }
1905 
1906         //----------------------------------------------------------------
1907         // Test the redirectErrorStream property
1908         //----------------------------------------------------------------
1909         try {
1910             ProcessBuilder pb = new ProcessBuilder();
1911             equal(pb.redirectErrorStream(), false);
1912             equal(pb.redirectErrorStream(true), pb);
1913             equal(pb.redirectErrorStream(), true);
1914             equal(pb.redirectErrorStream(false), pb);
1915             equal(pb.redirectErrorStream(), false);
1916         } catch (Throwable t) { unexpected(t); }
1917 
1918         try {
1919             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1920             childArgs.add("OutErr");
1921             ProcessBuilder pb = new ProcessBuilder(childArgs);
1922             {
1923                 ProcessResults r = run(pb);
1924                 equal(r.out(), "outout");
1925                 equal(r.err(), "errerr");
1926             }
1927             {
1928                 pb.redirectErrorStream(true);
1929                 ProcessResults r = run(pb);
1930                 equal(r.out(), "outerrouterr");
1931                 equal(r.err(), "");
1932             }
1933         } catch (Throwable t) { unexpected(t); }
1934 
1935         if (Unix.is()) {
1936             //----------------------------------------------------------------
1937             // We can find true and false when PATH is null
1938             //----------------------------------------------------------------
1939             try {
1940                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1941                 childArgs.add("null PATH");
1942                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1943                 pb.environment().remove("PATH");
1944                 ProcessResults r = run(pb);
1945                 equal(r.out(), "");
1946                 equal(r.err(), "");
1947                 equal(r.exitValue(), 0);
1948             } catch (Throwable t) { unexpected(t); }
1949 
1950             //----------------------------------------------------------------
1951             // PATH search algorithm on Unix
1952             //----------------------------------------------------------------
1953             try {
1954                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1955                 childArgs.add("PATH search algorithm");
1956                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1957                 pb.environment().put("PATH", "dir1:dir2:");
1958                 ProcessResults r = run(pb);
1959                 equal(r.out(), "");
1960                 equal(r.err(), "");
1961                 equal(r.exitValue(), True.exitValue());
1962             } catch (Throwable t) { unexpected(t); }
1963 
1964             //----------------------------------------------------------------
1965             // Parent's, not child's PATH is used
1966             //----------------------------------------------------------------
1967             try {
1968                 new File("suBdiR").mkdirs();
1969                 copy("/bin/true", "suBdiR/unliKely");
1970                 final ProcessBuilder pb =
1971                     new ProcessBuilder(new String[]{"unliKely"});
1972                 pb.environment().put("PATH", "suBdiR");
1973                 THROWS(IOException.class, () -> pb.start());
1974             } catch (Throwable t) { unexpected(t);
1975             } finally {
1976                 new File("suBdiR/unliKely").delete();
1977                 new File("suBdiR").delete();
1978             }
1979         }
1980 
1981         //----------------------------------------------------------------
1982         // Attempt to start bogus program ""
1983         //----------------------------------------------------------------
1984         try {
1985             new ProcessBuilder("").start();
1986             fail("Expected IOException not thrown");
1987         } catch (IOException e) {
1988             String m = e.getMessage();
1989             if (EnglishUnix.is() &&
1990                 ! matches(m, NO_SUCH_FILE_OR_DIR_ERROR_MSG))
1991                 unexpected(e);
1992         } catch (Throwable t) { unexpected(t); }
1993 
1994         //----------------------------------------------------------------
1995         // Check that attempt to execute program name with funny
1996         // characters throws an exception containing those characters.
1997         //----------------------------------------------------------------
1998         for (String programName : new String[] {"\u00f0", "\u01f0"})
1999             try {
2000                 new ProcessBuilder(programName).start();
2001                 fail("Expected IOException not thrown");
2002             } catch (IOException e) {
2003                 String m = e.getMessage();
2004                 Pattern p = Pattern.compile(programName);
2005                 if (! matches(m, programName)
2006                     || (EnglishUnix.is() &&
2007                         ! matches(m, NO_SUCH_FILE_OR_DIR_ERROR_MSG)))
2008                     unexpected(e);
2009             } catch (Throwable t) { unexpected(t); }
2010 
2011         //----------------------------------------------------------------
2012         // Attempt to start process in nonexistent directory fails.
2013         //----------------------------------------------------------------
2014         try {
2015             new ProcessBuilder("echo")
2016                 .directory(new File("UnLiKeLY"))
2017                 .start();
2018             fail("Expected IOException not thrown");
2019         } catch (IOException e) {
2020             String m = e.getMessage();
2021             if (! matches(m, "in directory")
2022                 || (EnglishUnix.is() &&
2023                     ! matches(m, NO_SUCH_FILE_OR_DIR_ERROR_MSG)))
2024                 unexpected(e);
2025         } catch (Throwable t) { unexpected(t); }
2026 
2027         //----------------------------------------------------------------
2028         // Attempt to write 4095 bytes to the pipe buffer without a
2029         // reader to drain it would deadlock, if not for the fact that
2030         // interprocess pipe buffers are at least 4096 bytes.
2031         //
2032         // Also, check that available reports all the bytes expected
2033         // in the pipe buffer, and that I/O operations do the expected
2034         // things.
2035         //----------------------------------------------------------------
2036         try {
2037             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2038             childArgs.add("print4095");
2039             final int SIZE = 4095;
2040             final Process p = new ProcessBuilder(childArgs).start();
2041             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
2042             p.waitFor();                                // Might hang!
2043             equal(SIZE, p.getInputStream().available());
2044             equal(SIZE, p.getErrorStream().available());
2045             THROWS(IOException.class,
2046                    () -> { p.getOutputStream().write((byte) '!');
2047                            p.getOutputStream().flush();});
2048 
2049             final byte[] bytes = new byte[SIZE + 1];
2050             equal(SIZE, p.getInputStream().read(bytes));
2051             for (int i = 0; i < SIZE; i++)
2052                 equal((byte) '!', bytes[i]);
2053             equal((byte) 0, bytes[SIZE]);
2054 
2055             equal(SIZE, p.getErrorStream().read(bytes));
2056             for (int i = 0; i < SIZE; i++)
2057                 equal((byte) 'E', bytes[i]);
2058             equal((byte) 0, bytes[SIZE]);
2059 
2060             equal(0, p.getInputStream().available());
2061             equal(0, p.getErrorStream().available());
2062             equal(-1, p.getErrorStream().read());
2063             equal(-1, p.getInputStream().read());
2064 
2065             equal(p.exitValue(), 5);
2066 
2067             p.getInputStream().close();
2068             p.getErrorStream().close();
2069             try { p.getOutputStream().close(); } catch (IOException flushFailed) { }
2070 
2071             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
2072             for (final InputStream in : streams) {
2073                 Fun[] ops = {
2074                     () -> in.read(),
2075                     () -> in.read(bytes),
2076                     () -> in.available()
2077                 };
2078                 for (Fun op : ops) {
2079                     try {
2080                         op.f();
2081                         fail();
2082                     } catch (IOException expected) {
2083                         check(expected.getMessage()
2084                               .matches("[Ss]tream [Cc]losed"));
2085                     }
2086                 }
2087             }
2088         } catch (Throwable t) { unexpected(t); }
2089 
2090         //----------------------------------------------------------------
2091         // Check that reads which are pending when Process.destroy is
2092         // called, get EOF, or IOException("Stream closed").
2093         //----------------------------------------------------------------
2094         try {
2095             final int cases = 4;
2096             for (int i = 0; i < cases; i++) {
2097                 final int action = i;
2098                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
2099                 childArgs.add("sleep");
2100                 final byte[] bytes = new byte[10];
2101                 final Process p = new ProcessBuilder(childArgs).start();
2102                 final CountDownLatch latch = new CountDownLatch(1);
2103                 final InputStream s;
2104                 switch (action & 0x1) {
2105                     case 0: s = p.getInputStream(); break;
2106                     case 1: s = p.getErrorStream(); break;
2107                     default: throw new Error();
2108                 }
2109                 final Thread thread = new Thread() {
2110                     public void run() {
2111                         try {
2112                             int r;
2113                             latch.countDown();
2114                             switch (action & 0x2) {
2115                                 case 0: r = s.read(); break;
2116                                 case 2: r = s.read(bytes); break;
2117                                 default: throw new Error();
2118                             }
2119                             if (r >= 0) {
2120                                 // The child sent unexpected output; print it to diagnose
2121                                 System.out.println("Unexpected child output:");
2122                                 if ((action & 0x2) == 0) {
2123                                     System.out.write(r);    // Single character
2124 
2125                                 } else {
2126                                     System.out.write(bytes, 0, r);
2127                                 }
2128                                 for (int c = s.read(); c >= 0; c = s.read())
2129                                     System.out.write(c);
2130                                 System.out.println("\nEND Child output.");
2131                             }
2132                             equal(-1, r);
2133                         } catch (IOException ioe) {
2134                             if (!ioe.getMessage().equals("Stream closed")) {
2135                                 // BufferedInputStream may throw IOE("Stream closed").
2136                                 unexpected(ioe);
2137                             }
2138                         } catch (Throwable t) { unexpected(t); }}};
2139 
2140                 thread.start();
2141                 latch.await();
2142                 Thread.sleep(10);
2143 
2144                 String os = System.getProperty("os.name");
2145                 if (os.equalsIgnoreCase("Solaris") ||
2146                     os.equalsIgnoreCase("SunOS"))
2147                 {
2148                     final Object deferred;
2149                     Class<?> c = s.getClass();
2150                     if (c.getName().equals(
2151                         "java.lang.ProcessImpl$DeferredCloseInputStream"))
2152                     {
2153                         deferred = s;
2154                     } else {
2155                         Field deferredField = p.getClass().
2156                             getDeclaredField("stdout_inner_stream");
2157                         deferredField.setAccessible(true);
2158                         deferred = deferredField.get(p);
2159                     }
2160                     Field useCountField = deferred.getClass().
2161                         getDeclaredField("useCount");
2162                     useCountField.setAccessible(true);
2163 
2164                     while (useCountField.getInt(deferred) <= 0) {
2165                         Thread.yield();
2166                     }
2167                 } else if (s instanceof BufferedInputStream) {
2168                     // Wait until after the s.read occurs in "thread" by
2169                     // checking when the input stream monitor is acquired
2170                     // (BufferedInputStream.read is synchronized)
2171                     while (!isLocked(s, 10)) {
2172                         Thread.sleep(100);
2173                     }
2174                 }
2175                 p.destroy();
2176                 thread.join();
2177             }
2178         } catch (Throwable t) { unexpected(t); }
2179 
2180         //----------------------------------------------------------------
2181         // Check that subprocesses which create subprocesses of their
2182         // own do not cause parent to hang waiting for file
2183         // descriptors to be closed.
2184         //----------------------------------------------------------------
2185         try {
2186             if (Unix.is()
2187                 && new File("/bin/bash").exists()
2188                 && new File("/bin/sleep").exists()) {
2189                 // Notice that we only destroy the process created by us (i.e.
2190                 // our child) but not our grandchild (i.e. '/bin/sleep'). So
2191                 // pay attention that the grandchild doesn't run too long to
2192                 // avoid polluting the process space with useless processes.
2193                 // Running the grandchild for 60s should be more than enough.
2194                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };
2195                 final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };
2196                 final ProcessBuilder pb = new ProcessBuilder(cmd);
2197                 final Process p = pb.start();
2198                 final InputStream stdout = p.getInputStream();
2199                 final InputStream stderr = p.getErrorStream();
2200                 final OutputStream stdin = p.getOutputStream();
2201                 final Thread reader = new Thread() {
2202                     public void run() {
2203                         try { stdout.read(); }
2204                         catch (IOException e) {
2205                             // Check that reader failed because stream was
2206                             // asynchronously closed.
2207                             // e.printStackTrace();
2208                             String msg = e.getMessage();
2209                             if (EnglishUnix.is() &&
2210                                 ! (msg.matches(".*Bad file.*") ||
2211                                         msg.matches(".*Stream closed.*")))
2212                                 unexpected(e);
2213                         }
2214                         catch (Throwable t) { unexpected(t); }}};
2215                 reader.setDaemon(true);
2216                 reader.start();
2217                 Thread.sleep(100);
2218                 p.destroy();
2219                 check(p.waitFor() != 0);
2220                 check(p.exitValue() != 0);
2221                 // Subprocess is now dead, but file descriptors remain open.
2222                 // Make sure the test will fail if we don't manage to close
2223                 // the open streams within 30 seconds. Notice that this time
2224                 // must be shorter than the sleep time of the grandchild.
2225                 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);
2226                 t.schedule(new TimerTask() {
2227                       public void run() {
2228                           fail("Subprocesses which create subprocesses of " +
2229                                "their own caused the parent to hang while " +
2230                                "waiting for file descriptors to be closed.");
2231                           System.exit(-1);
2232                       }
2233                   }, 30000);
2234                 stdout.close();
2235                 stderr.close();
2236                 stdin.close();
2237                 new ProcessBuilder(cmdkill).start();
2238                 // All streams successfully closed so we can cancel the timer.
2239                 t.cancel();
2240                 //----------------------------------------------------------
2241                 // There remain unsolved issues with asynchronous close.
2242                 // Here's a highly non-portable experiment to demonstrate:
2243                 //----------------------------------------------------------
2244                 if (Boolean.getBoolean("wakeupJeff!")) {
2245                     System.out.println("wakeupJeff!");
2246                     // Initialize signal handler for INTERRUPT_SIGNAL.
2247                     new FileInputStream("/bin/sleep").getChannel().close();
2248                     // Send INTERRUPT_SIGNAL to every thread in this java.
2249                     String[] wakeupJeff = {
2250                         "/bin/bash", "-c",
2251                         "/bin/ps --noheaders -Lfp $PPID | " +
2252                         "/usr/bin/perl -nale 'print $F[3]' | " +
2253                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
2254                         "/usr/bin/xargs kill -62"
2255                     };
2256                     new ProcessBuilder(wakeupJeff).start().waitFor();
2257                     // If wakeupJeff worked, reader probably got EBADF.
2258                     reader.join();
2259                 }
2260             }
2261 
2262             //----------------------------------------------------------------
2263             // Check the Process toString() method
2264             //----------------------------------------------------------------
2265             {
2266                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
2267                 childArgs.add("testIO");
2268                 ProcessBuilder pb = new ProcessBuilder(childArgs);
2269                 pb.redirectInput(Redirect.PIPE);
2270                 pb.redirectOutput(DISCARD);
2271                 pb.redirectError(DISCARD);
2272                 final Process p = pb.start();
2273                 // Child process waits until it gets input
2274                 String s = p.toString();
2275                 check(s.contains("not exited"));
2276                 check(s.contains("pid=" + p.pid() + ","));
2277 
2278                 new PrintStream(p.getOutputStream()).print("standard input");
2279                 p.getOutputStream().close();
2280 
2281                 // Check the toString after it exits
2282                 int exitValue = p.waitFor();
2283                 s = p.toString();
2284                 check(s.contains("pid=" + p.pid() + ","));
2285                 check(s.contains("exitValue=" + exitValue) &&
2286                         !s.contains("not exited"));
2287             }
2288         } catch (Throwable t) { unexpected(t); }
2289 
2290         //----------------------------------------------------------------
2291         // Attempt to start process with insufficient permissions fails.
2292         //----------------------------------------------------------------
2293         try {
2294             new File("emptyCommand").delete();
2295             new FileOutputStream("emptyCommand").close();
2296             new File("emptyCommand").setExecutable(false);
2297             new ProcessBuilder("./emptyCommand").start();
2298             fail("Expected IOException not thrown");
2299         } catch (IOException e) {
2300             new File("./emptyCommand").delete();
2301             String m = e.getMessage();
2302             if (EnglishUnix.is() &&
2303                 ! matches(m, PERMISSION_DENIED_ERROR_MSG))
2304                 unexpected(e);
2305         } catch (Throwable t) { unexpected(t); }
2306 
2307         new File("emptyCommand").delete();
2308 
2309         //----------------------------------------------------------------
2310         // Check for correct security permission behavior
2311         //----------------------------------------------------------------
2312         final Policy policy = new Policy();
2313         Policy.setPolicy(policy);
2314         System.setSecurityManager(new SecurityManager());
2315 
2316         try {
2317             // No permissions required to CREATE a ProcessBuilder
2318             policy.setPermissions(/* Nothing */);
2319             new ProcessBuilder("env").directory(null).directory();
2320             new ProcessBuilder("env").directory(new File("dir")).directory();
2321             new ProcessBuilder("env").command("??").command();
2322         } catch (Throwable t) { unexpected(t); }
2323 
2324         THROWS(SecurityException.class,
2325                () -> { policy.setPermissions(/* Nothing */);
2326                        System.getenv("foo");},
2327                () -> { policy.setPermissions(/* Nothing */);
2328                        System.getenv();},
2329                () -> { policy.setPermissions(/* Nothing */);
2330                        new ProcessBuilder("echo").start();},
2331                () -> { policy.setPermissions(/* Nothing */);
2332                        Runtime.getRuntime().exec("echo");},
2333                () -> { policy.setPermissions(
2334                                new RuntimePermission("getenv.bar"));
2335                        System.getenv("foo");});
2336 
2337         try {
2338             policy.setPermissions(new RuntimePermission("getenv.foo"));
2339             System.getenv("foo");
2340 
2341             policy.setPermissions(new RuntimePermission("getenv.*"));
2342             System.getenv("foo");
2343             System.getenv();
2344             new ProcessBuilder().environment();
2345         } catch (Throwable t) { unexpected(t); }
2346 
2347 
2348         final Permission execPermission
2349             = new FilePermission("<<ALL FILES>>", "execute");
2350 
2351         THROWS(SecurityException.class,
2352                () -> { // environment permission by itself insufficient
2353                        policy.setPermissions(new RuntimePermission("getenv.*"));
2354                        ProcessBuilder pb = new ProcessBuilder("env");
2355                        pb.environment().put("foo","bar");
2356                        pb.start();},
2357                () -> { // exec permission by itself insufficient
2358                        policy.setPermissions(execPermission);
2359                        ProcessBuilder pb = new ProcessBuilder("env");
2360                        pb.environment().put("foo","bar");
2361                        pb.start();});
2362 
2363         try {
2364             // Both permissions? OK.
2365             policy.setPermissions(new RuntimePermission("getenv.*"),
2366                                   execPermission);
2367             ProcessBuilder pb = new ProcessBuilder("env");
2368             pb.environment().put("foo","bar");
2369             Process p = pb.start();
2370             closeStreams(p);
2371         } catch (IOException e) { // OK
2372         } catch (Throwable t) { unexpected(t); }
2373 
2374         try {
2375             // Don't need environment permission unless READING environment
2376             policy.setPermissions(execPermission);
2377             Runtime.getRuntime().exec("env", new String[]{});
2378         } catch (IOException e) { // OK
2379         } catch (Throwable t) { unexpected(t); }
2380 
2381         try {
2382             // Don't need environment permission unless READING environment
2383             policy.setPermissions(execPermission);
2384             new ProcessBuilder("env").start();
2385         } catch (IOException e) { // OK
2386         } catch (Throwable t) { unexpected(t); }
2387 
2388         // Restore "normal" state without a security manager
2389         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2390         System.setSecurityManager(null);
2391 
2392         //----------------------------------------------------------------
2393         // Check that Process.isAlive() &
2394         // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
2395         //----------------------------------------------------------------
2396         try {
2397             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2398             childArgs.add("sleep");
2399             final Process p = new ProcessBuilder(childArgs).start();
2400             long start = System.nanoTime();
2401             if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
2402                 fail("Test failed: Process exited prematurely");
2403             }
2404             long end = System.nanoTime();
2405             // give waitFor(timeout) a wide berth (2s)
2406             System.out.printf(" waitFor process: delta: %d%n",(end - start) );
2407 
2408             if ((end - start) > TimeUnit.SECONDS.toNanos(2))
2409                 fail("Test failed: waitFor took too long (" + (end - start) + "ns)");
2410 
2411             p.destroy();
2412             p.waitFor();
2413 
2414             if (p.isAlive() ||
2415                 !p.waitFor(0, TimeUnit.MILLISECONDS))
2416             {
2417                 fail("Test failed: Process still alive - please terminate " +
2418                     p.toString() + " manually");
2419             }
2420         } catch (Throwable t) { unexpected(t); }
2421 
2422         //----------------------------------------------------------------
2423         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2424         // works as expected.
2425         //----------------------------------------------------------------
2426         try {
2427             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2428             childArgs.add("sleep");
2429             final Process p = new ProcessBuilder(childArgs).start();
2430             long start = System.nanoTime();
2431 
2432             p.waitFor(10, TimeUnit.MILLISECONDS);
2433 
2434             long end = System.nanoTime();
2435             if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
2436                 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
2437 
2438             p.destroy();
2439         } catch (Throwable t) { unexpected(t); }
2440 
2441         //----------------------------------------------------------------
2442         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2443         // interrupt works as expected, if interrupted while waiting.
2444         //----------------------------------------------------------------
2445         try {
2446             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2447             childArgs.add("sleep");
2448             final Process p = new ProcessBuilder(childArgs).start();
2449             final long start = System.nanoTime();
2450             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2451 
2452             final Thread thread = new Thread() {
2453                 public void run() {
2454                     try {
2455                         aboutToWaitFor.countDown();
2456                         Thread.currentThread().interrupt();
2457                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2458                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2459                     } catch (InterruptedException success) {
2460                     } catch (Throwable t) { unexpected(t); }
2461                 }
2462             };
2463 
2464             thread.start();
2465             aboutToWaitFor.await();
2466             thread.interrupt();
2467             thread.join(10L * 1000L);
2468             check(millisElapsedSince(start) < 10L * 1000L);
2469             check(!thread.isAlive());
2470             p.destroy();
2471         } catch (Throwable t) { unexpected(t); }
2472 
2473         //----------------------------------------------------------------
2474         // Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
2475         // interrupt works as expected, if interrupted while waiting.
2476         //----------------------------------------------------------------
2477         try {
2478             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2479             childArgs.add("sleep");
2480             final Process p = new ProcessBuilder(childArgs).start();
2481             final long start = System.nanoTime();
2482             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2483 
2484             final Thread thread = new Thread() {
2485                 public void run() {
2486                     try {
2487                         aboutToWaitFor.countDown();
2488                         Thread.currentThread().interrupt();
2489                         boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
2490                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2491                     } catch (InterruptedException success) {
2492                     } catch (Throwable t) { unexpected(t); }
2493                 }
2494             };
2495 
2496             thread.start();
2497             aboutToWaitFor.await();
2498             thread.interrupt();
2499             thread.join(10L * 1000L);
2500             check(millisElapsedSince(start) < 10L * 1000L);
2501             check(!thread.isAlive());
2502             p.destroy();
2503         } catch (Throwable t) { unexpected(t); }
2504 
2505         //----------------------------------------------------------------
2506         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2507         // interrupt works as expected, if interrupted before waiting.
2508         //----------------------------------------------------------------
2509         try {
2510             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2511             childArgs.add("sleep");
2512             final Process p = new ProcessBuilder(childArgs).start();
2513             final long start = System.nanoTime();
2514             final CountDownLatch threadStarted = new CountDownLatch(1);
2515 
2516             final Thread thread = new Thread() {
2517                 public void run() {
2518                     try {
2519                         threadStarted.countDown();
2520                         do { Thread.yield(); }
2521                         while (!Thread.currentThread().isInterrupted());
2522                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2523                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2524                     } catch (InterruptedException success) {
2525                     } catch (Throwable t) { unexpected(t); }
2526                 }
2527             };
2528 
2529             thread.start();
2530             threadStarted.await();
2531             thread.interrupt();
2532             thread.join(10L * 1000L);
2533             check(millisElapsedSince(start) < 10L * 1000L);
2534             check(!thread.isAlive());
2535             p.destroy();
2536         } catch (Throwable t) { unexpected(t); }
2537 
2538         //----------------------------------------------------------------
2539         // Check that Process.waitFor(timeout, null) throws NPE.
2540         //----------------------------------------------------------------
2541         try {
2542             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2543             childArgs.add("sleep");
2544             final Process p = new ProcessBuilder(childArgs).start();
2545             THROWS(NullPointerException.class,
2546                     () ->  p.waitFor(10L, null));
2547             THROWS(NullPointerException.class,
2548                     () ->  p.waitFor(0L, null));
2549             THROWS(NullPointerException.class,
2550                     () -> p.waitFor(-1L, null));
2551             // Terminate process and recheck after it exits
2552             p.destroy();
2553             p.waitFor();
2554             THROWS(NullPointerException.class,
2555                     () -> p.waitFor(10L, null));
2556             THROWS(NullPointerException.class,
2557                     () -> p.waitFor(0L, null));
2558             THROWS(NullPointerException.class,
2559                     () -> p.waitFor(-1L, null));
2560         } catch (Throwable t) { unexpected(t); }
2561 
2562         //----------------------------------------------------------------
2563         // Check that default implementation of Process.waitFor(timeout, null) throws NPE.
2564         //----------------------------------------------------------------
2565         try {
2566             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2567             childArgs.add("sleep");
2568             final Process proc = new ProcessBuilder(childArgs).start();
2569             final DelegatingProcess p = new DelegatingProcess(proc);
2570 
2571             THROWS(NullPointerException.class,
2572                     () ->  p.waitFor(10L, null));
2573             THROWS(NullPointerException.class,
2574                     () ->  p.waitFor(0L, null));
2575             THROWS(NullPointerException.class,
2576                     () ->  p.waitFor(-1L, null));
2577             // Terminate process and recheck after it exits
2578             p.destroy();
2579             p.waitFor();
2580             THROWS(NullPointerException.class,
2581                     () -> p.waitFor(10L, null));
2582             THROWS(NullPointerException.class,
2583                     () -> p.waitFor(0L, null));
2584             THROWS(NullPointerException.class,
2585                     () -> p.waitFor(-1L, null));
2586         } catch (Throwable t) { unexpected(t); }
2587 
2588         //----------------------------------------------------------------
2589         // Check the default implementation for
2590         // Process.waitFor(long, TimeUnit)
2591         //----------------------------------------------------------------
2592         try {
2593             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2594             childArgs.add("sleep");
2595             final Process proc = new ProcessBuilder(childArgs).start();
2596             DelegatingProcess p = new DelegatingProcess(proc);
2597             long start = System.nanoTime();
2598 
2599             p.waitFor(1000, TimeUnit.MILLISECONDS);
2600 
2601             long end = System.nanoTime();
2602             if ((end - start) < 500000000)
2603                 fail("Test failed: waitFor didn't take long enough");
2604 
2605             p.destroy();
2606 
2607             p.waitFor(1000, TimeUnit.MILLISECONDS);
2608         } catch (Throwable t) { unexpected(t); }
2609     }
2610 
2611     static void closeStreams(Process p) {
2612         try {
2613             p.getOutputStream().close();
2614             p.getInputStream().close();
2615             p.getErrorStream().close();
2616         } catch (Throwable t) { unexpected(t); }
2617     }
2618 
2619     //----------------------------------------------------------------
2620     // A Policy class designed to make permissions fiddling very easy.
2621     //----------------------------------------------------------------
2622     private static class Policy extends java.security.Policy {
2623         static final java.security.Policy DEFAULT_POLICY = java.security.Policy.getPolicy();
2624 
2625         private Permissions perms;
2626 
2627         public void setPermissions(Permission...permissions) {
2628             perms = new Permissions();
2629             for (Permission permission : permissions)
2630                 perms.add(permission);
2631         }
2632 
2633         public Policy() { setPermissions(/* Nothing */); }
2634 
2635         public PermissionCollection getPermissions(CodeSource cs) {
2636             return perms;
2637         }
2638 
2639         public PermissionCollection getPermissions(ProtectionDomain pd) {
2640             return perms;
2641         }
2642 
2643         public boolean implies(ProtectionDomain pd, Permission p) {
2644             return perms.implies(p) || DEFAULT_POLICY.implies(pd, p);
2645         }
2646 
2647         public void refresh() {}
2648     }
2649 
2650     private static class StreamAccumulator extends Thread {
2651         private final InputStream is;
2652         private final StringBuilder sb = new StringBuilder();
2653         private Throwable throwable = null;
2654 
2655         public String result () throws Throwable {
2656             if (throwable != null)
2657                 throw throwable;
2658             return sb.toString();
2659         }
2660 
2661         StreamAccumulator (InputStream is) {
2662             this.is = is;
2663         }
2664 
2665         public void run() {
2666             try {
2667                 Reader r = new InputStreamReader(is);
2668                 char[] buf = new char[4096];
2669                 int n;
2670                 while ((n = r.read(buf)) > 0) {
2671                     sb.append(buf,0,n);
2672                 }
2673             } catch (Throwable t) {
2674                 throwable = t;
2675             } finally {
2676                 try { is.close(); }
2677                 catch (Throwable t) { throwable = t; }
2678             }
2679         }
2680     }
2681 
2682     static ProcessResults run(ProcessBuilder pb) {
2683         try {
2684             return run(pb.start());
2685         } catch (Throwable t) { unexpected(t); return null; }
2686     }
2687 
2688     private static ProcessResults run(Process p) {
2689         Throwable throwable = null;
2690         int exitValue = -1;
2691         String out = "";
2692         String err = "";
2693 
2694         StreamAccumulator outAccumulator =
2695             new StreamAccumulator(p.getInputStream());
2696         StreamAccumulator errAccumulator =
2697             new StreamAccumulator(p.getErrorStream());
2698 
2699         try {
2700             outAccumulator.start();
2701             errAccumulator.start();
2702 
2703             exitValue = p.waitFor();
2704 
2705             outAccumulator.join();
2706             errAccumulator.join();
2707 
2708             out = outAccumulator.result();
2709             err = errAccumulator.result();
2710         } catch (Throwable t) {
2711             throwable = t;
2712         }
2713 
2714         return new ProcessResults(out, err, exitValue, throwable);
2715     }
2716 
2717     //----------------------------------------------------------------
2718     // Results of a command
2719     //----------------------------------------------------------------
2720     private static class ProcessResults {
2721         private final String out;
2722         private final String err;
2723         private final int exitValue;
2724         private final Throwable throwable;
2725 
2726         public ProcessResults(String out,
2727                               String err,
2728                               int exitValue,
2729                               Throwable throwable) {
2730             this.out = out;
2731             this.err = err;
2732             this.exitValue = exitValue;
2733             this.throwable = throwable;
2734         }
2735 
2736         public String out()          { return out; }
2737         public String err()          { return err; }
2738         public int exitValue()       { return exitValue; }
2739         public Throwable throwable() { return throwable; }
2740 
2741         public String toString() {
2742             StringBuilder sb = new StringBuilder();
2743             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2744                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2745                 .append("exitValue = " + exitValue + "\n");
2746             if (throwable != null)
2747                 sb.append(throwable.getStackTrace());
2748             return sb.toString();
2749         }
2750     }
2751 
2752     //--------------------- Infrastructure ---------------------------
2753     static volatile int passed = 0, failed = 0;
2754     static void pass() {passed++;}
2755     static void fail() {failed++; Thread.dumpStack();}
2756     static void fail(String msg) {System.err.println(msg); fail();}
2757     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2758     static void check(boolean cond) {if (cond) pass(); else fail();}
2759     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2760     static void equal(Object x, Object y) {
2761         if (x == null ? y == null : x.equals(y)) pass();
2762         else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");}
2763 
2764     public static void main(String[] args) throws Throwable {
2765         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2766         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2767         if (failed > 0) throw new AssertionError("Some tests failed");}
2768     interface Fun {void f() throws Throwable;}
2769     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2770         for (Fun f : fs)
2771             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2772             catch (Throwable t) {
2773                 if (k.isAssignableFrom(t.getClass())) pass();
2774                 else unexpected(t);}}
2775 
2776     static boolean isLocked(final Object monitor, final long millis) throws InterruptedException {
2777         return new Thread() {
2778             volatile boolean unlocked;
2779 
2780             @Override
2781             public void run() {
2782                 synchronized (monitor) { unlocked = true; }
2783             }
2784 
2785             boolean isLocked() throws InterruptedException {
2786                 start();
2787                 join(millis);
2788                 return !unlocked;
2789             }
2790         }.isLocked();
2791     }
2792 }