1 /*
   2  * Copyright (c) 2003, 2010, 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
  30  * @summary Basic tests for Process and Environment Variable code
  31  * @run main/othervm/timeout=300 Basic
  32  * @author Martin Buchholz
  33  */
  34 
  35 import java.lang.ProcessBuilder.Redirect;
  36 import static java.lang.ProcessBuilder.Redirect.*;
  37 
  38 import java.io.*;
  39 import java.util.*;
  40 import java.util.concurrent.CountDownLatch;
  41 import java.security.*;
  42 import java.util.regex.Pattern;
  43 import static java.lang.System.getenv;
  44 import static java.lang.System.out;
  45 import static java.lang.Boolean.TRUE;
  46 import static java.util.AbstractMap.SimpleImmutableEntry;
  47 
  48 public class Basic {
  49 
  50     /* used for Windows only */
  51     static final String systemRoot = System.getenv("SystemRoot");
  52 
  53     private static String commandOutput(Reader r) throws Throwable {
  54         StringBuilder sb = new StringBuilder();
  55         int c;
  56         while ((c = r.read()) > 0)
  57             if (c != '\r')
  58                 sb.append((char) c);
  59         return sb.toString();
  60     }
  61 
  62     private static String commandOutput(Process p) throws Throwable {
  63         check(p.getInputStream()  == p.getInputStream());
  64         check(p.getOutputStream() == p.getOutputStream());
  65         check(p.getErrorStream()  == p.getErrorStream());
  66         Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");
  67         String output = commandOutput(r);
  68         equal(p.waitFor(), 0);
  69         equal(p.exitValue(), 0);
  70         return output;
  71     }
  72 
  73     private static String commandOutput(ProcessBuilder pb) {
  74         try {
  75             return commandOutput(pb.start());
  76         } catch (Throwable t) {
  77             String commandline = "";
  78             for (String arg : pb.command())
  79                 commandline += " " + arg;
  80             System.out.println("Exception trying to run process: " + commandline);
  81             unexpected(t);
  82             return "";
  83         }
  84     }
  85 
  86     private static String commandOutput(String...command) {
  87         try {
  88             return commandOutput(Runtime.getRuntime().exec(command));
  89         } catch (Throwable t) {
  90             String commandline = "";
  91             for (String arg : command)
  92                 commandline += " " + arg;
  93             System.out.println("Exception trying to run process: " + commandline);
  94             unexpected(t);
  95             return "";
  96         }
  97     }
  98 
  99     private static void checkCommandOutput(ProcessBuilder pb,
 100                                            String expected,
 101                                            String failureMsg) {
 102         String got = commandOutput(pb);
 103         check(got.equals(expected),
 104               failureMsg + "\n" +
 105               "Expected: \"" + expected + "\"\n" +
 106               "Got: \"" + got + "\"");
 107     }
 108 
 109     private static String absolutifyPath(String path) {
 110         StringBuilder sb = new StringBuilder();
 111         for (String file : path.split(File.pathSeparator)) {
 112             if (sb.length() != 0)
 113                 sb.append(File.pathSeparator);
 114             sb.append(new File(file).getAbsolutePath());
 115         }
 116         return sb.toString();
 117     }
 118 
 119     // compare windows-style, by canonicalizing to upper case,
 120     // not lower case as String.compareToIgnoreCase does
 121     private static class WindowsComparator
 122         implements Comparator<String> {
 123         public int compare(String x, String y) {
 124             return x.toUpperCase(Locale.US)
 125                 .compareTo(y.toUpperCase(Locale.US));
 126         }
 127     }
 128 
 129     private static String sortedLines(String lines) {
 130         String[] arr = lines.split("\n");
 131         List<String> ls = new ArrayList<String>();
 132         for (String s : arr)
 133             ls.add(s);
 134         Collections.sort(ls, new WindowsComparator());
 135         StringBuilder sb = new StringBuilder();
 136         for (String s : ls)
 137             sb.append(s + "\n");
 138         return sb.toString();
 139     }
 140 
 141     private static void compareLinesIgnoreCase(String lines1, String lines2) {
 142         if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {
 143             String dashes =
 144                 "-----------------------------------------------------";
 145             out.println(dashes);
 146             out.print(sortedLines(lines1));
 147             out.println(dashes);
 148             out.print(sortedLines(lines2));
 149             out.println(dashes);
 150             out.println("sizes: " + sortedLines(lines1).length() +
 151                         " " + sortedLines(lines2).length());
 152 
 153             fail("Sorted string contents differ");
 154         }
 155     }
 156 
 157     private static final Runtime runtime = Runtime.getRuntime();
 158 
 159     private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};
 160 
 161     private static String winEnvFilter(String env) {
 162         return env.replaceAll("\r", "")
 163             .replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");
 164     }
 165 
 166     private static String unixEnvProg() {
 167         return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"
 168             : "/bin/env";
 169     }
 170 
 171     private static String nativeEnv(String[] env) {
 172         try {
 173             if (Windows.is()) {
 174                 return winEnvFilter
 175                     (commandOutput(runtime.exec(winEnvCommand, env)));
 176             } else {
 177                 return commandOutput(runtime.exec(unixEnvProg(), env));
 178             }
 179         } catch (Throwable t) { throw new Error(t); }
 180     }
 181 
 182     private static String nativeEnv(ProcessBuilder pb) {
 183         try {
 184             if (Windows.is()) {
 185                 pb.command(winEnvCommand);
 186                 return winEnvFilter(commandOutput(pb));
 187             } else {
 188                 pb.command(new String[]{unixEnvProg()});
 189                 return commandOutput(pb);
 190             }
 191         } catch (Throwable t) { throw new Error(t); }
 192     }
 193 
 194     private static void checkSizes(Map<String,String> environ, int size) {
 195         try {
 196             equal(size, environ.size());
 197             equal(size, environ.entrySet().size());
 198             equal(size, environ.keySet().size());
 199             equal(size, environ.values().size());
 200 
 201             boolean isEmpty = (size == 0);
 202             equal(isEmpty, environ.isEmpty());
 203             equal(isEmpty, environ.entrySet().isEmpty());
 204             equal(isEmpty, environ.keySet().isEmpty());
 205             equal(isEmpty, environ.values().isEmpty());
 206         } catch (Throwable t) { unexpected(t); }
 207     }
 208 
 209     private interface EnvironmentFrobber {
 210         void doIt(Map<String,String> environ);
 211     }
 212 
 213     private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {
 214         try {
 215             Map<String,String> environ = new ProcessBuilder().environment();
 216             environ.put("Foo", "BAAR");
 217             fooDeleter.doIt(environ);
 218             equal(environ.get("Foo"), null);
 219             equal(environ.remove("Foo"), null);
 220         } catch (Throwable t) { unexpected(t); }
 221     }
 222 
 223     private static void testVariableAdder(EnvironmentFrobber fooAdder) {
 224         try {
 225             Map<String,String> environ = new ProcessBuilder().environment();
 226             environ.remove("Foo");
 227             fooAdder.doIt(environ);
 228             equal(environ.get("Foo"), "Bahrein");
 229         } catch (Throwable t) { unexpected(t); }
 230     }
 231 
 232     private static void testVariableModifier(EnvironmentFrobber fooModifier) {
 233         try {
 234             Map<String,String> environ = new ProcessBuilder().environment();
 235             environ.put("Foo","OldValue");
 236             fooModifier.doIt(environ);
 237             equal(environ.get("Foo"), "NewValue");
 238         } catch (Throwable t) { unexpected(t); }
 239     }
 240 
 241     private static void printUTF8(String s) throws IOException {
 242         out.write(s.getBytes("UTF-8"));
 243     }
 244 
 245     private static String getenvAsString(Map<String,String> environment) {
 246         StringBuilder sb = new StringBuilder();
 247         for (Map.Entry<String,String> e : environment.entrySet())
 248             // Ignore magic environment variables added by the launcher
 249             if (! e.getKey().equals("NLSPATH") &&
 250                 ! e.getKey().equals("XFILESEARCHPATH") &&
 251                 ! e.getKey().equals("LD_LIBRARY_PATH"))
 252                 sb.append(e.getKey())
 253                     .append('=')
 254                     .append(e.getValue())
 255                     .append(',');
 256         return sb.toString();
 257     }
 258 
 259     static void print4095(OutputStream s, byte b) throws Throwable {
 260         byte[] bytes = new byte[4095];
 261         Arrays.fill(bytes, b);
 262         s.write(bytes);         // Might hang!
 263     }
 264 
 265     static void checkPermissionDenied(ProcessBuilder pb) {
 266         try {
 267             pb.start();
 268             fail("Expected IOException not thrown");
 269         } catch (IOException e) {
 270             String m = e.getMessage();
 271             if (EnglishUnix.is() &&
 272                 ! matches(m, "Permission denied"))
 273                 unexpected(e);
 274         } catch (Throwable t) { unexpected(t); }
 275     }
 276 
 277     public static class JavaChild {
 278         public static void main(String args[]) throws Throwable {
 279             String action = args[0];
 280             if (action.equals("sleep")) {
 281                 Thread.sleep(10 * 60 * 1000L);
 282             } else if (action.equals("testIO")) {
 283                 String expected = "standard input";
 284                 char[] buf = new char[expected.length()+1];
 285                 int n = new InputStreamReader(System.in).read(buf,0,buf.length);
 286                 if (n != expected.length())
 287                     System.exit(5);
 288                 if (! new String(buf,0,n).equals(expected))
 289                     System.exit(5);
 290                 System.err.print("standard error");
 291                 System.out.print("standard output");
 292             } else if (action.equals("testInheritIO")) {
 293                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
 294                 childArgs.add("testIO");
 295                 ProcessBuilder pb = new ProcessBuilder(childArgs);
 296                 pb.inheritIO();
 297                 ProcessResults r = run(pb);
 298                 if (! r.out().equals(""))
 299                     System.exit(7);
 300                 if (! r.err().equals(""))
 301                     System.exit(8);
 302                 if (r.exitValue() != 0)
 303                     System.exit(9);
 304             } else if (action.equals("System.getenv(String)")) {
 305                 String val = System.getenv(args[1]);
 306                 printUTF8(val == null ? "null" : val);
 307             } else if (action.equals("System.getenv(\\u1234)")) {
 308                 String val = System.getenv("\u1234");
 309                 printUTF8(val == null ? "null" : val);
 310             } else if (action.equals("System.getenv()")) {
 311                 printUTF8(getenvAsString(System.getenv()));
 312             } else if (action.equals("ArrayOOME")) {
 313                 Object dummy;
 314                 switch(new Random().nextInt(3)) {
 315                 case 0: dummy = new Integer[Integer.MAX_VALUE]; break;
 316                 case 1: dummy = new double[Integer.MAX_VALUE];  break;
 317                 case 2: dummy = new byte[Integer.MAX_VALUE][];  break;
 318                 default: throw new InternalError();
 319                 }
 320             } else if (action.equals("pwd")) {
 321                 printUTF8(new File(System.getProperty("user.dir"))
 322                           .getCanonicalPath());
 323             } else if (action.equals("print4095")) {
 324                 print4095(System.out, (byte) '!');
 325                 print4095(System.err, (byte) 'E');
 326                 System.exit(5);
 327             } else if (action.equals("OutErr")) {
 328                 // You might think the system streams would be
 329                 // buffered, and in fact they are implemented using
 330                 // BufferedOutputStream, but each and every print
 331                 // causes immediate operating system I/O.
 332                 System.out.print("out");
 333                 System.err.print("err");
 334                 System.out.print("out");
 335                 System.err.print("err");
 336             } else if (action.equals("null PATH")) {
 337                 equal(System.getenv("PATH"), null);
 338                 check(new File("/bin/true").exists());
 339                 check(new File("/bin/false").exists());
 340                 ProcessBuilder pb1 = new ProcessBuilder();
 341                 ProcessBuilder pb2 = new ProcessBuilder();
 342                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
 343                 ProcessResults r;
 344 
 345                 for (final ProcessBuilder pb :
 346                          new ProcessBuilder[] {pb1, pb2}) {
 347                     pb.command("true");
 348                     equal(run(pb).exitValue(), True.exitValue());
 349 
 350                     pb.command("false");
 351                     equal(run(pb).exitValue(), False.exitValue());
 352                 }
 353 
 354                 if (failed != 0) throw new Error("null PATH");
 355             } else if (action.equals("PATH search algorithm")) {
 356                 equal(System.getenv("PATH"), "dir1:dir2:");
 357                 check(new File("/bin/true").exists());
 358                 check(new File("/bin/false").exists());
 359                 String[] cmd = {"prog"};
 360                 ProcessBuilder pb1 = new ProcessBuilder(cmd);
 361                 ProcessBuilder pb2 = new ProcessBuilder(cmd);
 362                 ProcessBuilder pb3 = new ProcessBuilder(cmd);
 363                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
 364                 pb3.environment().remove("PATH");
 365 
 366                 for (final ProcessBuilder pb :
 367                          new ProcessBuilder[] {pb1, pb2, pb3}) {
 368                     try {
 369                         // Not on PATH at all; directories don't exist
 370                         try {
 371                             pb.start();
 372                             fail("Expected IOException not thrown");
 373                         } catch (IOException e) {
 374                             String m = e.getMessage();
 375                             if (EnglishUnix.is() &&
 376                                 ! matches(m, "No such file"))
 377                                 unexpected(e);
 378                         } catch (Throwable t) { unexpected(t); }
 379 
 380                         // Not on PATH at all; directories exist
 381                         new File("dir1").mkdirs();
 382                         new File("dir2").mkdirs();
 383                         try {
 384                             pb.start();
 385                             fail("Expected IOException not thrown");
 386                         } catch (IOException e) {
 387                             String m = e.getMessage();
 388                             if (EnglishUnix.is() &&
 389                                 ! matches(m, "No such file"))
 390                                 unexpected(e);
 391                         } catch (Throwable t) { unexpected(t); }
 392 
 393                         // Can't execute a directory -- permission denied
 394                         // Report EACCES errno
 395                         new File("dir1/prog").mkdirs();
 396                         checkPermissionDenied(pb);
 397 
 398                         // continue searching if EACCES
 399                         copy("/bin/true", "dir2/prog");
 400                         equal(run(pb).exitValue(), True.exitValue());
 401                         new File("dir1/prog").delete();
 402                         new File("dir2/prog").delete();
 403 
 404                         new File("dir2/prog").mkdirs();
 405                         copy("/bin/true", "dir1/prog");
 406                         equal(run(pb).exitValue(), True.exitValue());
 407 
 408                         // Check empty PATH component means current directory.
 409                         //
 410                         // While we're here, let's test different kinds of
 411                         // Unix executables, and PATH vs explicit searching.
 412                         new File("dir1/prog").delete();
 413                         new File("dir2/prog").delete();
 414                         for (String[] command :
 415                                  new String[][] {
 416                                      new String[] {"./prog"},
 417                                      cmd}) {
 418                             pb.command(command);
 419                             File prog = new File("./prog");
 420                             // "Normal" binaries
 421                             copy("/bin/true", "./prog");
 422                             equal(run(pb).exitValue(),
 423                                   True.exitValue());
 424                             copy("/bin/false", "./prog");
 425                             equal(run(pb).exitValue(),
 426                                   False.exitValue());
 427                             prog.delete();
 428                             // Interpreter scripts with #!
 429                             setFileContents(prog, "#!/bin/true\n");
 430                             prog.setExecutable(true);
 431                             equal(run(pb).exitValue(),
 432                                   True.exitValue());
 433                             prog.delete();
 434                             setFileContents(prog, "#!/bin/false\n");
 435                             prog.setExecutable(true);
 436                             equal(run(pb).exitValue(),
 437                                   False.exitValue());
 438                             // Traditional shell scripts without #!
 439                             setFileContents(prog, "exec /bin/true\n");
 440                             prog.setExecutable(true);
 441                             equal(run(pb).exitValue(),
 442                                   True.exitValue());
 443                             prog.delete();
 444                             setFileContents(prog, "exec /bin/false\n");
 445                             prog.setExecutable(true);
 446                             equal(run(pb).exitValue(),
 447                                   False.exitValue());
 448                             prog.delete();
 449                         }
 450 
 451                         // Test Unix interpreter scripts
 452                         File dir1Prog = new File("dir1/prog");
 453                         dir1Prog.delete();
 454                         pb.command(new String[] {"prog", "world"});
 455                         setFileContents(dir1Prog, "#!/bin/echo hello\n");
 456                         checkPermissionDenied(pb);
 457                         dir1Prog.setExecutable(true);
 458                         equal(run(pb).out(), "hello dir1/prog world\n");
 459                         equal(run(pb).exitValue(), True.exitValue());
 460                         dir1Prog.delete();
 461                         pb.command(cmd);
 462 
 463                         // Test traditional shell scripts without #!
 464                         setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
 465                         pb.command(new String[] {"prog", "hello", "world"});
 466                         checkPermissionDenied(pb);
 467                         dir1Prog.setExecutable(true);
 468                         equal(run(pb).out(), "hello world\n");
 469                         equal(run(pb).exitValue(), True.exitValue());
 470                         dir1Prog.delete();
 471                         pb.command(cmd);
 472 
 473                         // If prog found on both parent and child's PATH,
 474                         // parent's is used.
 475                         new File("dir1/prog").delete();
 476                         new File("dir2/prog").delete();
 477                         new File("prog").delete();
 478                         new File("dir3").mkdirs();
 479                         copy("/bin/true", "dir1/prog");
 480                         copy("/bin/false", "dir3/prog");
 481                         pb.environment().put("PATH","dir3");
 482                         equal(run(pb).exitValue(), True.exitValue());
 483                         copy("/bin/true", "dir3/prog");
 484                         copy("/bin/false", "dir1/prog");
 485                         equal(run(pb).exitValue(), False.exitValue());
 486 
 487                     } finally {
 488                         // cleanup
 489                         new File("dir1/prog").delete();
 490                         new File("dir2/prog").delete();
 491                         new File("dir3/prog").delete();
 492                         new File("dir1").delete();
 493                         new File("dir2").delete();
 494                         new File("dir3").delete();
 495                         new File("prog").delete();
 496                     }
 497                 }
 498 
 499                 if (failed != 0) throw new Error("PATH search algorithm");
 500             }
 501             else throw new Error("JavaChild invocation error");
 502         }
 503     }
 504 
 505     private static void copy(String src, String dst) {
 506         system("/bin/cp", "-fp", src, dst);
 507     }
 508 
 509     private static void system(String... command) {
 510         try {
 511             ProcessBuilder pb = new ProcessBuilder(command);
 512             ProcessResults r = run(pb.start());
 513             equal(r.exitValue(), 0);
 514             equal(r.out(), "");
 515             equal(r.err(), "");
 516         } catch (Throwable t) { unexpected(t); }
 517     }
 518 
 519     private static String javaChildOutput(ProcessBuilder pb, String...args) {
 520         List<String> list = new ArrayList<String>(javaChildArgs);
 521         for (String arg : args)
 522             list.add(arg);
 523         pb.command(list);
 524         return commandOutput(pb);
 525     }
 526 
 527     private static String getenvInChild(ProcessBuilder pb) {
 528         return javaChildOutput(pb, "System.getenv()");
 529     }
 530 
 531     private static String getenvInChild1234(ProcessBuilder pb) {
 532         return javaChildOutput(pb, "System.getenv(\\u1234)");
 533     }
 534 
 535     private static String getenvInChild(ProcessBuilder pb, String name) {
 536         return javaChildOutput(pb, "System.getenv(String)", name);
 537     }
 538 
 539     private static String pwdInChild(ProcessBuilder pb) {
 540         return javaChildOutput(pb, "pwd");
 541     }
 542 
 543     private static final String javaExe =
 544         System.getProperty("java.home") +
 545         File.separator + "bin" + File.separator + "java";
 546 
 547     private static final String classpath =
 548         System.getProperty("java.class.path");
 549 
 550     private static final List<String> javaChildArgs =
 551         Arrays.asList(new String[]
 552             { javaExe, "-classpath", absolutifyPath(classpath),
 553               "Basic$JavaChild"});
 554 
 555     private static void testEncoding(String encoding, String tested) {
 556         try {
 557             // If round trip conversion works, should be able to set env vars
 558             // correctly in child.
 559             if (new String(tested.getBytes()).equals(tested)) {
 560                 out.println("Testing " + encoding + " environment values");
 561                 ProcessBuilder pb = new ProcessBuilder();
 562                 pb.environment().put("ASCIINAME",tested);
 563                 equal(getenvInChild(pb,"ASCIINAME"), tested);
 564             }
 565         } catch (Throwable t) { unexpected(t); }
 566     }
 567 
 568     static class Windows {
 569         public static boolean is() { return is; }
 570         private static final boolean is =
 571             System.getProperty("os.name").startsWith("Windows");
 572     }
 573 
 574     static class Unix {
 575         public static boolean is() { return is; }
 576         private static final boolean is =
 577             (! Windows.is() &&
 578              new File("/bin/sh").exists() &&
 579              new File("/bin/true").exists() &&
 580              new File("/bin/false").exists());
 581     }
 582 
 583     static class UnicodeOS {
 584         public static boolean is() { return is; }
 585         private static final String osName = System.getProperty("os.name");
 586         private static final boolean is =
 587             // MacOS X would probably also qualify
 588             osName.startsWith("Windows")   &&
 589             ! osName.startsWith("Windows 9") &&
 590             ! osName.equals("Windows Me");
 591     }
 592 
 593     static class True {
 594         public static int exitValue() { return 0; }
 595     }
 596 
 597     private static class False {
 598         public static int exitValue() { return exitValue; }
 599         private static final int exitValue = exitValue0();
 600         private static int exitValue0() {
 601             // /bin/false returns an *unspecified* non-zero number.
 602             try {
 603                 if (! Unix.is())
 604                     return -1;
 605                 else {
 606                     int rc = new ProcessBuilder("/bin/false")
 607                         .start().waitFor();
 608                     check(rc != 0);
 609                     return rc;
 610                 }
 611             } catch (Throwable t) { unexpected(t); return -1; }
 612         }
 613     }
 614 
 615     static class EnglishUnix {
 616         private final static Boolean is =
 617             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
 618 
 619         private static boolean isEnglish(String envvar) {
 620             String val = getenv(envvar);
 621             return (val == null) || val.matches("en.*");
 622         }
 623 
 624         /** Returns true if we can expect English OS error strings */
 625         static boolean is() { return is; }
 626     }
 627 
 628     private static boolean matches(String str, String regex) {
 629         return Pattern.compile(regex).matcher(str).find();
 630     }
 631 
 632     private static String sortByLinesWindowsly(String text) {
 633         String[] lines = text.split("\n");
 634         Arrays.sort(lines, new WindowsComparator());
 635         StringBuilder sb = new StringBuilder();
 636         for (String line : lines)
 637             sb.append(line).append("\n");
 638         return sb.toString();
 639     }
 640 
 641     private static void checkMapSanity(Map<String,String> map) {
 642         try {
 643             Set<String> keySet = map.keySet();
 644             Collection<String> values = map.values();
 645             Set<Map.Entry<String,String>> entrySet = map.entrySet();
 646 
 647             equal(entrySet.size(), keySet.size());
 648             equal(entrySet.size(), values.size());
 649 
 650             StringBuilder s1 = new StringBuilder();
 651             for (Map.Entry<String,String> e : entrySet)
 652                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
 653 
 654             StringBuilder s2 = new StringBuilder();
 655             for (String var : keySet)
 656                 s2.append(var + "=" + map.get(var) + "\n");
 657 
 658             equal(s1.toString(), s2.toString());
 659 
 660             Iterator<String> kIter = keySet.iterator();
 661             Iterator<String> vIter = values.iterator();
 662             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
 663 
 664             while (eIter.hasNext()) {
 665                 Map.Entry<String,String> entry = eIter.next();
 666                 String key   = kIter.next();
 667                 String value = vIter.next();
 668                 check(entrySet.contains(entry));
 669                 check(keySet.contains(key));
 670                 check(values.contains(value));
 671                 check(map.containsKey(key));
 672                 check(map.containsValue(value));
 673                 equal(entry.getKey(), key);
 674                 equal(entry.getValue(), value);
 675             }
 676             check(! kIter.hasNext() &&
 677                   ! vIter.hasNext());
 678 
 679         } catch (Throwable t) { unexpected(t); }
 680     }
 681 
 682     private static void checkMapEquality(Map<String,String> map1,
 683                                          Map<String,String> map2) {
 684         try {
 685             equal(map1.size(), map2.size());
 686             equal(map1.isEmpty(), map2.isEmpty());
 687             for (String key : map1.keySet()) {
 688                 equal(map1.get(key), map2.get(key));
 689                 check(map2.keySet().contains(key));
 690             }
 691             equal(map1, map2);
 692             equal(map2, map1);
 693             equal(map1.entrySet(), map2.entrySet());
 694             equal(map2.entrySet(), map1.entrySet());
 695             equal(map1.keySet(), map2.keySet());
 696             equal(map2.keySet(), map1.keySet());
 697 
 698             equal(map1.hashCode(), map2.hashCode());
 699             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
 700             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
 701         } catch (Throwable t) { unexpected(t); }
 702     }
 703 
 704     static void checkRedirects(ProcessBuilder pb,
 705                                Redirect in, Redirect out, Redirect err) {
 706         equal(pb.redirectInput(),  in);
 707         equal(pb.redirectOutput(), out);
 708         equal(pb.redirectError(),  err);
 709     }
 710 
 711     static void redirectIO(ProcessBuilder pb,
 712                            Redirect in, Redirect out, Redirect err) {
 713         pb.redirectInput(in);
 714         pb.redirectOutput(out);
 715         pb.redirectError(err);
 716     }
 717 
 718     static void setFileContents(File file, String contents) {
 719         try {
 720             Writer w = new FileWriter(file);
 721             w.write(contents);
 722             w.close();
 723         } catch (Throwable t) { unexpected(t); }
 724     }
 725 
 726     static String fileContents(File file) {
 727         try {
 728             Reader r = new FileReader(file);
 729             StringBuilder sb = new StringBuilder();
 730             char[] buffer = new char[1024];
 731             int n;
 732             while ((n = r.read(buffer)) != -1)
 733                 sb.append(buffer,0,n);
 734             r.close();
 735             return new String(sb);
 736         } catch (Throwable t) { unexpected(t); return ""; }
 737     }
 738 
 739     static void testIORedirection() throws Throwable {
 740         final File ifile = new File("ifile");
 741         final File ofile = new File("ofile");
 742         final File efile = new File("efile");
 743         ifile.delete();
 744         ofile.delete();
 745         efile.delete();
 746 
 747         //----------------------------------------------------------------
 748         // Check mutual inequality of different types of Redirect
 749         //----------------------------------------------------------------
 750         Redirect[] redirects =
 751             { PIPE,
 752               INHERIT,
 753               Redirect.from(ifile),
 754               Redirect.to(ifile),
 755               Redirect.appendTo(ifile),
 756               Redirect.from(ofile),
 757               Redirect.to(ofile),
 758               Redirect.appendTo(ofile),
 759             };
 760         for (int i = 0; i < redirects.length; i++)
 761             for (int j = 0; j < redirects.length; j++)
 762                 equal(redirects[i].equals(redirects[j]), (i == j));
 763 
 764         //----------------------------------------------------------------
 765         // Check basic properties of different types of Redirect
 766         //----------------------------------------------------------------
 767         equal(PIPE.type(), Redirect.Type.PIPE);
 768         equal(PIPE.toString(), "PIPE");
 769         equal(PIPE.file(), null);
 770 
 771         equal(INHERIT.type(), Redirect.Type.INHERIT);
 772         equal(INHERIT.toString(), "INHERIT");
 773         equal(INHERIT.file(), null);
 774 
 775         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
 776         equal(Redirect.from(ifile).toString(),
 777               "redirect to read from file \"ifile\"");
 778         equal(Redirect.from(ifile).file(), ifile);
 779         equal(Redirect.from(ifile),
 780               Redirect.from(ifile));
 781         equal(Redirect.from(ifile).hashCode(),
 782               Redirect.from(ifile).hashCode());
 783 
 784         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
 785         equal(Redirect.to(ofile).toString(),
 786               "redirect to write to file \"ofile\"");
 787         equal(Redirect.to(ofile).file(), ofile);
 788         equal(Redirect.to(ofile),
 789               Redirect.to(ofile));
 790         equal(Redirect.to(ofile).hashCode(),
 791               Redirect.to(ofile).hashCode());
 792 
 793         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
 794         equal(Redirect.appendTo(efile).toString(),
 795               "redirect to append to file \"efile\"");
 796         equal(Redirect.appendTo(efile).file(), efile);
 797         equal(Redirect.appendTo(efile),
 798               Redirect.appendTo(efile));
 799         equal(Redirect.appendTo(efile).hashCode(),
 800               Redirect.appendTo(efile).hashCode());
 801 
 802         //----------------------------------------------------------------
 803         // Check initial values of redirects
 804         //----------------------------------------------------------------
 805         List<String> childArgs = new ArrayList<String>(javaChildArgs);
 806         childArgs.add("testIO");
 807         final ProcessBuilder pb = new ProcessBuilder(childArgs);
 808         checkRedirects(pb, PIPE, PIPE, PIPE);
 809 
 810         //----------------------------------------------------------------
 811         // Check inheritIO
 812         //----------------------------------------------------------------
 813         pb.inheritIO();
 814         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 815 
 816         //----------------------------------------------------------------
 817         // Check setters and getters agree
 818         //----------------------------------------------------------------
 819         pb.redirectInput(ifile);
 820         equal(pb.redirectInput().file(), ifile);
 821         equal(pb.redirectInput(), Redirect.from(ifile));
 822 
 823         pb.redirectOutput(ofile);
 824         equal(pb.redirectOutput().file(), ofile);
 825         equal(pb.redirectOutput(), Redirect.to(ofile));
 826 
 827         pb.redirectError(efile);
 828         equal(pb.redirectError().file(), efile);
 829         equal(pb.redirectError(), Redirect.to(efile));
 830 
 831         THROWS(IllegalArgumentException.class,
 832             new Fun(){void f() {
 833                 pb.redirectInput(Redirect.to(ofile)); }},
 834             new Fun(){void f() {
 835                 pb.redirectInput(Redirect.appendTo(ofile)); }},
 836             new Fun(){void f() {
 837                 pb.redirectOutput(Redirect.from(ifile)); }},
 838             new Fun(){void f() {
 839                 pb.redirectError(Redirect.from(ifile)); }});
 840 
 841         THROWS(IOException.class,
 842                // Input file does not exist
 843                new Fun(){void f() throws Throwable { pb.start(); }});
 844         setFileContents(ifile, "standard input");
 845 
 846         //----------------------------------------------------------------
 847         // Writing to non-existent files
 848         //----------------------------------------------------------------
 849         {
 850             ProcessResults r = run(pb);
 851             equal(r.exitValue(), 0);
 852             equal(fileContents(ofile), "standard output");
 853             equal(fileContents(efile), "standard error");
 854             equal(r.out(), "");
 855             equal(r.err(), "");
 856             ofile.delete();
 857             efile.delete();
 858         }
 859 
 860         //----------------------------------------------------------------
 861         // Both redirectErrorStream + redirectError
 862         //----------------------------------------------------------------
 863         {
 864             pb.redirectErrorStream(true);
 865             ProcessResults r = run(pb);
 866             equal(r.exitValue(), 0);
 867             equal(fileContents(ofile),
 868                   "standard error" + "standard output");
 869             equal(fileContents(efile), "");
 870             equal(r.out(), "");
 871             equal(r.err(), "");
 872             ofile.delete();
 873             efile.delete();
 874         }
 875 
 876         //----------------------------------------------------------------
 877         // Appending to existing files
 878         //----------------------------------------------------------------
 879         {
 880             setFileContents(ofile, "ofile-contents");
 881             setFileContents(efile, "efile-contents");
 882             pb.redirectOutput(Redirect.appendTo(ofile));
 883             pb.redirectError(Redirect.appendTo(efile));
 884             pb.redirectErrorStream(false);
 885             ProcessResults r = run(pb);
 886             equal(r.exitValue(), 0);
 887             equal(fileContents(ofile),
 888                   "ofile-contents" + "standard output");
 889             equal(fileContents(efile),
 890                   "efile-contents" + "standard error");
 891             equal(r.out(), "");
 892             equal(r.err(), "");
 893             ofile.delete();
 894             efile.delete();
 895         }
 896 
 897         //----------------------------------------------------------------
 898         // Replacing existing files
 899         //----------------------------------------------------------------
 900         {
 901             setFileContents(ofile, "ofile-contents");
 902             setFileContents(efile, "efile-contents");
 903             pb.redirectOutput(ofile);
 904             pb.redirectError(Redirect.to(efile));
 905             ProcessResults r = run(pb);
 906             equal(r.exitValue(), 0);
 907             equal(fileContents(ofile), "standard output");
 908             equal(fileContents(efile), "standard error");
 909             equal(r.out(), "");
 910             equal(r.err(), "");
 911             ofile.delete();
 912             efile.delete();
 913         }
 914 
 915         //----------------------------------------------------------------
 916         // Appending twice to the same file?
 917         //----------------------------------------------------------------
 918         {
 919             setFileContents(ofile, "ofile-contents");
 920             setFileContents(efile, "efile-contents");
 921             Redirect appender = Redirect.appendTo(ofile);
 922             pb.redirectOutput(appender);
 923             pb.redirectError(appender);
 924             ProcessResults r = run(pb);
 925             equal(r.exitValue(), 0);
 926             equal(fileContents(ofile),
 927                   "ofile-contents" +
 928                   "standard error" +
 929                   "standard output");
 930             equal(fileContents(efile), "efile-contents");
 931             equal(r.out(), "");
 932             equal(r.err(), "");
 933             ifile.delete();
 934             ofile.delete();
 935             efile.delete();
 936         }
 937 
 938         //----------------------------------------------------------------
 939         // Testing INHERIT is harder.
 940         // Note that this requires __FOUR__ nested JVMs involved in one test,
 941         // if you count the harness JVM.
 942         //----------------------------------------------------------------
 943         {
 944             redirectIO(pb, PIPE, PIPE, PIPE);
 945             List<String> command = pb.command();
 946             command.set(command.size() - 1, "testInheritIO");
 947             Process p = pb.start();
 948             new PrintStream(p.getOutputStream()).print("standard input");
 949             p.getOutputStream().close();
 950             ProcessResults r = run(p);
 951             equal(r.exitValue(), 0);
 952             equal(r.out(), "standard output");
 953             equal(r.err(), "standard error");
 954         }
 955 
 956         //----------------------------------------------------------------
 957         // Test security implications of I/O redirection
 958         //----------------------------------------------------------------
 959 
 960         // Read access to current directory is always granted;
 961         // So create a tmpfile for input instead.
 962         final File tmpFile = File.createTempFile("Basic", "tmp");
 963         setFileContents(tmpFile, "standard input");
 964 
 965         final Policy policy = new Policy();
 966         Policy.setPolicy(policy);
 967         System.setSecurityManager(new SecurityManager());
 968         try {
 969             final Permission xPermission
 970                 = new FilePermission("<<ALL FILES>>", "execute");
 971             final Permission rxPermission
 972                 = new FilePermission("<<ALL FILES>>", "read,execute");
 973             final Permission wxPermission
 974                 = new FilePermission("<<ALL FILES>>", "write,execute");
 975             final Permission rwxPermission
 976                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
 977 
 978             THROWS(SecurityException.class,
 979                new Fun() { void f() throws IOException {
 980                    policy.setPermissions(xPermission);
 981                    redirectIO(pb, from(tmpFile), PIPE, PIPE);
 982                    pb.start();}},
 983                new Fun() { void f() throws IOException {
 984                    policy.setPermissions(rxPermission);
 985                    redirectIO(pb, PIPE, to(ofile), PIPE);
 986                    pb.start();}},
 987                new Fun() { void f() throws IOException {
 988                    policy.setPermissions(rxPermission);
 989                    redirectIO(pb, PIPE, PIPE, to(efile));
 990                    pb.start();}});
 991 
 992             {
 993                 policy.setPermissions(rxPermission);
 994                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
 995                 ProcessResults r = run(pb);
 996                 equal(r.out(), "standard output");
 997                 equal(r.err(), "standard error");
 998             }
 999 
1000             {
1001                 policy.setPermissions(wxPermission);
1002                 redirectIO(pb, PIPE, to(ofile), to(efile));
1003                 Process p = pb.start();
1004                 new PrintStream(p.getOutputStream()).print("standard input");
1005                 p.getOutputStream().close();
1006                 ProcessResults r = run(p);
1007                 policy.setPermissions(rwxPermission);
1008                 equal(fileContents(ofile), "standard output");
1009                 equal(fileContents(efile), "standard error");
1010             }
1011 
1012             {
1013                 policy.setPermissions(rwxPermission);
1014                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1015                 ProcessResults r = run(pb);
1016                 policy.setPermissions(rwxPermission);
1017                 equal(fileContents(ofile), "standard output");
1018                 equal(fileContents(efile), "standard error");
1019             }
1020 
1021         } finally {
1022             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1023             System.setSecurityManager(null);
1024             tmpFile.delete();
1025             ifile.delete();
1026             ofile.delete();
1027             efile.delete();
1028         }
1029     }
1030 
1031     private static void realMain(String[] args) throws Throwable {
1032         if (Windows.is())
1033             System.out.println("This appears to be a Windows system.");
1034         if (Unix.is())
1035             System.out.println("This appears to be a Unix system.");
1036         if (UnicodeOS.is())
1037             System.out.println("This appears to be a Unicode-based OS.");
1038 
1039         try { testIORedirection(); }
1040         catch (Throwable t) { unexpected(t); }
1041 
1042         //----------------------------------------------------------------
1043         // Basic tests for setting, replacing and deleting envvars
1044         //----------------------------------------------------------------
1045         try {
1046             ProcessBuilder pb = new ProcessBuilder();
1047             Map<String,String> environ = pb.environment();
1048 
1049             // New env var
1050             environ.put("QUUX", "BAR");
1051             equal(environ.get("QUUX"), "BAR");
1052             equal(getenvInChild(pb,"QUUX"), "BAR");
1053 
1054             // Modify env var
1055             environ.put("QUUX","bear");
1056             equal(environ.get("QUUX"), "bear");
1057             equal(getenvInChild(pb,"QUUX"), "bear");
1058             checkMapSanity(environ);
1059 
1060             // Remove env var
1061             environ.remove("QUUX");
1062             equal(environ.get("QUUX"), null);
1063             equal(getenvInChild(pb,"QUUX"), "null");
1064             checkMapSanity(environ);
1065 
1066             // Remove non-existent env var
1067             environ.remove("QUUX");
1068             equal(environ.get("QUUX"), null);
1069             equal(getenvInChild(pb,"QUUX"), "null");
1070             checkMapSanity(environ);
1071         } catch (Throwable t) { unexpected(t); }
1072 
1073         //----------------------------------------------------------------
1074         // Pass Empty environment to child
1075         //----------------------------------------------------------------
1076         try {
1077             ProcessBuilder pb = new ProcessBuilder();
1078             pb.environment().clear();
1079             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1080             if (Windows.is()) {
1081                 pb.environment().put("SystemRoot", systemRoot);
1082             }
1083             equal(getenvInChild(pb), expected);
1084         } catch (Throwable t) { unexpected(t); }
1085 
1086         //----------------------------------------------------------------
1087         // System.getenv() is read-only.
1088         //----------------------------------------------------------------
1089         THROWS(UnsupportedOperationException.class,
1090             new Fun(){void f(){ getenv().put("FOO","BAR");}},
1091             new Fun(){void f(){ getenv().remove("PATH");}},
1092             new Fun(){void f(){ getenv().keySet().remove("PATH");}},
1093             new Fun(){void f(){ getenv().values().remove("someValue");}});
1094 
1095         try {
1096             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1097             if (! c.isEmpty())
1098                 try {
1099                     c.iterator().next().setValue("foo");
1100                     fail("Expected UnsupportedOperationException not thrown");
1101                 } catch (UnsupportedOperationException e) {} // OK
1102         } catch (Throwable t) { unexpected(t); }
1103 
1104         //----------------------------------------------------------------
1105         // System.getenv() always returns the same object in our implementation.
1106         //----------------------------------------------------------------
1107         try {
1108             check(System.getenv() == System.getenv());
1109         } catch (Throwable t) { unexpected(t); }
1110 
1111         //----------------------------------------------------------------
1112         // You can't create an env var name containing "=",
1113         // or an env var name or value containing NUL.
1114         //----------------------------------------------------------------
1115         {
1116             final Map<String,String> m = new ProcessBuilder().environment();
1117             THROWS(IllegalArgumentException.class,
1118                 new Fun(){void f(){ m.put("FOO=","BAR");}},
1119                 new Fun(){void f(){ m.put("FOO\u0000","BAR");}},
1120                 new Fun(){void f(){ m.put("FOO","BAR\u0000");}});
1121         }
1122 
1123         //----------------------------------------------------------------
1124         // Commands must never be null.
1125         //----------------------------------------------------------------
1126         THROWS(NullPointerException.class,
1127                new Fun(){void f(){
1128                    new ProcessBuilder((List<String>)null);}},
1129                new Fun(){void f(){
1130                    new ProcessBuilder().command((List<String>)null);}});
1131 
1132         //----------------------------------------------------------------
1133         // Put in a command; get the same one back out.
1134         //----------------------------------------------------------------
1135         try {
1136             List<String> command = new ArrayList<String>();
1137             ProcessBuilder pb = new ProcessBuilder(command);
1138             check(pb.command() == command);
1139             List<String> command2 = new ArrayList<String>(2);
1140             command2.add("foo");
1141             command2.add("bar");
1142             pb.command(command2);
1143             check(pb.command() == command2);
1144             pb.command("foo", "bar");
1145             check(pb.command() != command2 && pb.command().equals(command2));
1146             pb.command(command2);
1147             command2.add("baz");
1148             equal(pb.command().get(2), "baz");
1149         } catch (Throwable t) { unexpected(t); }
1150 
1151         //----------------------------------------------------------------
1152         // Commands must contain at least one element.
1153         //----------------------------------------------------------------
1154         THROWS(IndexOutOfBoundsException.class,
1155             new Fun() { void f() throws IOException {
1156                 new ProcessBuilder().start();}},
1157             new Fun() { void f() throws IOException {
1158                 new ProcessBuilder(new ArrayList<String>()).start();}},
1159             new Fun() { void f() throws IOException {
1160                 Runtime.getRuntime().exec(new String[]{});}});
1161 
1162         //----------------------------------------------------------------
1163         // Commands must not contain null elements at start() time.
1164         //----------------------------------------------------------------
1165         THROWS(NullPointerException.class,
1166             new Fun() { void f() throws IOException {
1167                 new ProcessBuilder("foo",null,"bar").start();}},
1168             new Fun() { void f() throws IOException {
1169                 new ProcessBuilder((String)null).start();}},
1170             new Fun() { void f() throws IOException {
1171                 new ProcessBuilder(new String[]{null}).start();}},
1172             new Fun() { void f() throws IOException {
1173                 new ProcessBuilder(new String[]{"foo",null,"bar"}).start();}});
1174 
1175         //----------------------------------------------------------------
1176         // Command lists are growable.
1177         //----------------------------------------------------------------
1178         try {
1179             new ProcessBuilder().command().add("foo");
1180             new ProcessBuilder("bar").command().add("foo");
1181             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1182         } catch (Throwable t) { unexpected(t); }
1183 
1184         //----------------------------------------------------------------
1185         // Nulls in environment updates generate NullPointerException
1186         //----------------------------------------------------------------
1187         try {
1188             final Map<String,String> env = new ProcessBuilder().environment();
1189             THROWS(NullPointerException.class,
1190                 new Fun(){void f(){ env.put("foo",null);}},
1191                 new Fun(){void f(){ env.put(null,"foo");}},
1192                 new Fun(){void f(){ env.remove(null);}},
1193                 new Fun(){void f(){
1194                     for (Map.Entry<String,String> e : env.entrySet())
1195                         e.setValue(null);}},
1196                 new Fun() { void f() throws IOException {
1197                     Runtime.getRuntime().exec(new String[]{"foo"},
1198                                               new String[]{null});}});
1199         } catch (Throwable t) { unexpected(t); }
1200 
1201         //----------------------------------------------------------------
1202         // Non-String types in environment updates generate ClassCastException
1203         //----------------------------------------------------------------
1204         try {
1205             final Map<String,String> env = new ProcessBuilder().environment();
1206             THROWS(ClassCastException.class,
1207                 new Fun(){void f(){ env.remove(TRUE);}},
1208                 new Fun(){void f(){ env.keySet().remove(TRUE);}},
1209                 new Fun(){void f(){ env.values().remove(TRUE);}},
1210                 new Fun(){void f(){ env.entrySet().remove(TRUE);}});
1211         } catch (Throwable t) { unexpected(t); }
1212 
1213         //----------------------------------------------------------------
1214         // Check query operations on environment maps
1215         //----------------------------------------------------------------
1216         try {
1217             List<Map<String,String>> envs =
1218                 new ArrayList<Map<String,String>>(2);
1219             envs.add(System.getenv());
1220             envs.add(new ProcessBuilder().environment());
1221             for (final Map<String,String> env : envs) {
1222                 //----------------------------------------------------------------
1223                 // Nulls in environment queries are forbidden.
1224                 //----------------------------------------------------------------
1225                 THROWS(NullPointerException.class,
1226                     new Fun(){void f(){ getenv(null);}},
1227                     new Fun(){void f(){ env.get(null);}},
1228                     new Fun(){void f(){ env.containsKey(null);}},
1229                     new Fun(){void f(){ env.containsValue(null);}},
1230                     new Fun(){void f(){ env.keySet().contains(null);}},
1231                     new Fun(){void f(){ env.values().contains(null);}});
1232 
1233                 //----------------------------------------------------------------
1234                 // Non-String types in environment queries are forbidden.
1235                 //----------------------------------------------------------------
1236                 THROWS(ClassCastException.class,
1237                     new Fun(){void f(){ env.get(TRUE);}},
1238                     new Fun(){void f(){ env.containsKey(TRUE);}},
1239                     new Fun(){void f(){ env.containsValue(TRUE);}},
1240                     new Fun(){void f(){ env.keySet().contains(TRUE);}},
1241                     new Fun(){void f(){ env.values().contains(TRUE);}});
1242 
1243                 //----------------------------------------------------------------
1244                 // Illegal String values in environment queries are (grumble) OK
1245                 //----------------------------------------------------------------
1246                 equal(env.get("\u0000"), null);
1247                 check(! env.containsKey("\u0000"));
1248                 check(! env.containsValue("\u0000"));
1249                 check(! env.keySet().contains("\u0000"));
1250                 check(! env.values().contains("\u0000"));
1251             }
1252 
1253         } catch (Throwable t) { unexpected(t); }
1254 
1255         try {
1256             final Set<Map.Entry<String,String>> entrySet =
1257                 new ProcessBuilder().environment().entrySet();
1258             THROWS(NullPointerException.class,
1259                    new Fun(){void f(){ entrySet.contains(null);}});
1260             THROWS(ClassCastException.class,
1261                 new Fun(){void f(){ entrySet.contains(TRUE);}},
1262                 new Fun(){void f(){
1263                     entrySet.contains(
1264                         new SimpleImmutableEntry<Boolean,String>(TRUE,""));}});
1265 
1266             check(! entrySet.contains
1267                   (new SimpleImmutableEntry<String,String>("", "")));
1268         } catch (Throwable t) { unexpected(t); }
1269 
1270         //----------------------------------------------------------------
1271         // Put in a directory; get the same one back out.
1272         //----------------------------------------------------------------
1273         try {
1274             ProcessBuilder pb = new ProcessBuilder();
1275             File foo = new File("foo");
1276             equal(pb.directory(), null);
1277             equal(pb.directory(foo).directory(), foo);
1278             equal(pb.directory(null).directory(), null);
1279         } catch (Throwable t) { unexpected(t); }
1280 
1281         //----------------------------------------------------------------
1282         // If round-trip conversion works, check envvar pass-through to child
1283         //----------------------------------------------------------------
1284         try {
1285             testEncoding("ASCII",   "xyzzy");
1286             testEncoding("Latin1",  "\u00f1\u00e1");
1287             testEncoding("Unicode", "\u22f1\u11e1");
1288         } catch (Throwable t) { unexpected(t); }
1289 
1290         //----------------------------------------------------------------
1291         // A surprisingly large number of ways to delete an environment var.
1292         //----------------------------------------------------------------
1293         testVariableDeleter(new EnvironmentFrobber() {
1294                 public void doIt(Map<String,String> environ) {
1295                     environ.remove("Foo");}});
1296 
1297         testVariableDeleter(new EnvironmentFrobber() {
1298                 public void doIt(Map<String,String> environ) {
1299                     environ.keySet().remove("Foo");}});
1300 
1301         testVariableDeleter(new EnvironmentFrobber() {
1302                 public void doIt(Map<String,String> environ) {
1303                     environ.values().remove("BAAR");}});
1304 
1305         testVariableDeleter(new EnvironmentFrobber() {
1306                 public void doIt(Map<String,String> environ) {
1307                     // Legally fabricate a ProcessEnvironment.StringEntry,
1308                     // even though it's private.
1309                     Map<String,String> environ2
1310                         = new ProcessBuilder().environment();
1311                     environ2.clear();
1312                     environ2.put("Foo","BAAR");
1313                     // Subtlety alert.
1314                     Map.Entry<String,String> e
1315                         = environ2.entrySet().iterator().next();
1316                     environ.entrySet().remove(e);}});
1317 
1318         testVariableDeleter(new EnvironmentFrobber() {
1319                 public void doIt(Map<String,String> environ) {
1320                     Map.Entry<String,String> victim = null;
1321                     for (Map.Entry<String,String> e : environ.entrySet())
1322                         if (e.getKey().equals("Foo"))
1323                             victim = e;
1324                     if (victim != null)
1325                         environ.entrySet().remove(victim);}});
1326 
1327         testVariableDeleter(new EnvironmentFrobber() {
1328                 public void doIt(Map<String,String> environ) {
1329                     Iterator<String> it = environ.keySet().iterator();
1330                     while (it.hasNext()) {
1331                         String val = it.next();
1332                         if (val.equals("Foo"))
1333                             it.remove();}}});
1334 
1335         testVariableDeleter(new EnvironmentFrobber() {
1336                 public void doIt(Map<String,String> environ) {
1337                     Iterator<Map.Entry<String,String>> it
1338                         = environ.entrySet().iterator();
1339                     while (it.hasNext()) {
1340                         Map.Entry<String,String> e = it.next();
1341                         if (e.getKey().equals("Foo"))
1342                             it.remove();}}});
1343 
1344         testVariableDeleter(new EnvironmentFrobber() {
1345                 public void doIt(Map<String,String> environ) {
1346                     Iterator<String> it = environ.values().iterator();
1347                     while (it.hasNext()) {
1348                         String val = it.next();
1349                         if (val.equals("BAAR"))
1350                             it.remove();}}});
1351 
1352         //----------------------------------------------------------------
1353         // A surprisingly small number of ways to add an environment var.
1354         //----------------------------------------------------------------
1355         testVariableAdder(new EnvironmentFrobber() {
1356                 public void doIt(Map<String,String> environ) {
1357                     environ.put("Foo","Bahrein");}});
1358 
1359         //----------------------------------------------------------------
1360         // A few ways to modify an environment var.
1361         //----------------------------------------------------------------
1362         testVariableModifier(new EnvironmentFrobber() {
1363                 public void doIt(Map<String,String> environ) {
1364                     environ.put("Foo","NewValue");}});
1365 
1366         testVariableModifier(new EnvironmentFrobber() {
1367                 public void doIt(Map<String,String> environ) {
1368                     for (Map.Entry<String,String> e : environ.entrySet())
1369                         if (e.getKey().equals("Foo"))
1370                             e.setValue("NewValue");}});
1371 
1372         //----------------------------------------------------------------
1373         // Fiddle with environment sizes
1374         //----------------------------------------------------------------
1375         try {
1376             Map<String,String> environ = new ProcessBuilder().environment();
1377             int size = environ.size();
1378             checkSizes(environ, size);
1379 
1380             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1381             checkSizes(environ, size+1);
1382 
1383             // Check for environment independence
1384             new ProcessBuilder().environment().clear();
1385 
1386             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1387             checkSizes(environ, size+1);
1388 
1389             environ.remove("UnLiKeLYeNVIROmtNam");
1390             checkSizes(environ, size);
1391 
1392             environ.clear();
1393             checkSizes(environ, 0);
1394 
1395             environ.clear();
1396             checkSizes(environ, 0);
1397 
1398             environ = new ProcessBuilder().environment();
1399             environ.keySet().clear();
1400             checkSizes(environ, 0);
1401 
1402             environ = new ProcessBuilder().environment();
1403             environ.entrySet().clear();
1404             checkSizes(environ, 0);
1405 
1406             environ = new ProcessBuilder().environment();
1407             environ.values().clear();
1408             checkSizes(environ, 0);
1409         } catch (Throwable t) { unexpected(t); }
1410 
1411         //----------------------------------------------------------------
1412         // Check that various map invariants hold
1413         //----------------------------------------------------------------
1414         checkMapSanity(new ProcessBuilder().environment());
1415         checkMapSanity(System.getenv());
1416         checkMapEquality(new ProcessBuilder().environment(),
1417                          new ProcessBuilder().environment());
1418 
1419 
1420         //----------------------------------------------------------------
1421         // Check effects on external "env" command.
1422         //----------------------------------------------------------------
1423         try {
1424             Set<String> env1 = new HashSet<String>
1425                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1426 
1427             ProcessBuilder pb = new ProcessBuilder();
1428             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1429 
1430             Set<String> env2 = new HashSet<String>
1431                 (Arrays.asList(nativeEnv(pb).split("\n")));
1432 
1433             check(env2.size() == env1.size() + 1);
1434             env1.add("QwErTyUiOp=AsDfGhJk");
1435             check(env1.equals(env2));
1436         } catch (Throwable t) { unexpected(t); }
1437 
1438         //----------------------------------------------------------------
1439         // Test Runtime.exec(...envp...)
1440         // Check for sort order of environment variables on Windows.
1441         //----------------------------------------------------------------
1442         try {
1443             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1444             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1445                             "+=+", "_=_", "~=~"};
1446             String output = nativeEnv(envp);
1447             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n_=_\n~=~\n";
1448             // On Windows, Java must keep the environment sorted.
1449             // Order is random on Unix, so this test does the sort.
1450             if (! Windows.is())
1451                 output = sortByLinesWindowsly(output);
1452             equal(output, expected);
1453         } catch (Throwable t) { unexpected(t); }
1454 
1455         //----------------------------------------------------------------
1456         // System.getenv() must be consistent with System.getenv(String)
1457         //----------------------------------------------------------------
1458         try {
1459             for (Map.Entry<String,String> e : getenv().entrySet())
1460                 equal(getenv(e.getKey()), e.getValue());
1461         } catch (Throwable t) { unexpected(t); }
1462 
1463         //----------------------------------------------------------------
1464         // Fiddle with working directory in child
1465         //----------------------------------------------------------------
1466         try {
1467             String canonicalUserDir =
1468                 new File(System.getProperty("user.dir")).getCanonicalPath();
1469             String[] sdirs = new String[]
1470                 {".", "..", "/", "/bin",
1471                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1472                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1473             for (String sdir : sdirs) {
1474                 File dir = new File(sdir);
1475                 if (! (dir.isDirectory() && dir.exists()))
1476                     continue;
1477                 out.println("Testing directory " + dir);
1478                 //dir = new File(dir.getCanonicalPath());
1479 
1480                 ProcessBuilder pb = new ProcessBuilder();
1481                 equal(pb.directory(), null);
1482                 equal(pwdInChild(pb), canonicalUserDir);
1483 
1484                 pb.directory(dir);
1485                 equal(pb.directory(), dir);
1486                 equal(pwdInChild(pb), dir.getCanonicalPath());
1487 
1488                 pb.directory(null);
1489                 equal(pb.directory(), null);
1490                 equal(pwdInChild(pb), canonicalUserDir);
1491 
1492                 pb.directory(dir);
1493             }
1494         } catch (Throwable t) { unexpected(t); }
1495 
1496         //----------------------------------------------------------------
1497         // Working directory with Unicode in child
1498         //----------------------------------------------------------------
1499         try {
1500             if (UnicodeOS.is()) {
1501                 File dir = new File(System.getProperty("test.dir", "."),
1502                                     "ProcessBuilderDir\u4e00\u4e02");
1503                 try {
1504                     if (!dir.exists())
1505                         dir.mkdir();
1506                     out.println("Testing Unicode directory:" + dir);
1507                     ProcessBuilder pb = new ProcessBuilder();
1508                     pb.directory(dir);
1509                     equal(pwdInChild(pb), dir.getCanonicalPath());
1510                 } finally {
1511                     if (dir.exists())
1512                         dir.delete();
1513                 }
1514             }
1515         } catch (Throwable t) { unexpected(t); }
1516 
1517         //----------------------------------------------------------------
1518         // OOME in child allocating maximally sized array
1519         // Test for hotspot/jvmti bug 6850957
1520         //----------------------------------------------------------------
1521         try {
1522             List<String> list = new ArrayList<String>(javaChildArgs);
1523             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1524                                       javaExe));
1525             list.add("ArrayOOME");
1526             ProcessResults r = run(new ProcessBuilder(list));
1527             check(r.out().contains("java.lang.OutOfMemoryError:"));
1528             check(r.out().contains(javaExe));
1529             check(r.err().contains(System.getProperty("java.version")));
1530             equal(r.exitValue(), 1);
1531         } catch (Throwable t) { unexpected(t); }
1532 
1533         //----------------------------------------------------------------
1534         // Windows has tricky semi-case-insensitive semantics
1535         //----------------------------------------------------------------
1536         if (Windows.is())
1537             try {
1538                 out.println("Running case insensitve variable tests");
1539                 for (String[] namePair :
1540                          new String[][]
1541                     { new String[]{"PATH","PaTh"},
1542                       new String[]{"home","HOME"},
1543                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1544                     check((getenv(namePair[0]) == null &&
1545                            getenv(namePair[1]) == null)
1546                           ||
1547                           getenv(namePair[0]).equals(getenv(namePair[1])),
1548                           "Windows environment variables are not case insensitive");
1549                 }
1550             } catch (Throwable t) { unexpected(t); }
1551 
1552         //----------------------------------------------------------------
1553         // Test proper Unicode child environment transfer
1554         //----------------------------------------------------------------
1555         if (UnicodeOS.is())
1556             try {
1557                 ProcessBuilder pb = new ProcessBuilder();
1558                 pb.environment().put("\u1234","\u5678");
1559                 pb.environment().remove("PATH");
1560                 equal(getenvInChild1234(pb), "\u5678");
1561             } catch (Throwable t) { unexpected(t); }
1562 
1563 
1564         //----------------------------------------------------------------
1565         // Test Runtime.exec(...envp...) with envstrings with initial `='
1566         //----------------------------------------------------------------
1567         try {
1568             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1569             childArgs.add("System.getenv()");
1570             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1571             String[] envp;
1572             String[] envpWin = {"=ExitValue=3", "=C:=\\", "SystemRoot="+systemRoot};
1573             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1574             if (Windows.is()) {
1575                 envp = envpWin;
1576             } else {
1577                 envp = envpOth;
1578             }
1579             Process p = Runtime.getRuntime().exec(cmdp, envp);
1580             String expected = Windows.is() ? "=C:=\\,SystemRoot="+systemRoot+",=ExitValue=3," : "=C:=\\,";
1581             equal(commandOutput(p), expected);
1582             if (Windows.is()) {
1583                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1584                 pb.environment().clear();
1585                 pb.environment().put("SystemRoot", systemRoot);
1586                 pb.environment().put("=ExitValue", "3");
1587                 pb.environment().put("=C:", "\\");
1588                 equal(commandOutput(pb), expected);
1589             }
1590         } catch (Throwable t) { unexpected(t); }
1591 
1592         //----------------------------------------------------------------
1593         // Test Runtime.exec(...envp...) with envstrings without any `='
1594         //----------------------------------------------------------------
1595         try {
1596             String[] cmdp = {"echo"};
1597             String[] envp = {"Hello", "World"}; // Yuck!
1598             Process p = Runtime.getRuntime().exec(cmdp, envp);
1599             equal(commandOutput(p), "\n");
1600         } catch (Throwable t) { unexpected(t); }
1601 
1602         //----------------------------------------------------------------
1603         // Test Runtime.exec(...envp...) with envstrings containing NULs
1604         //----------------------------------------------------------------
1605         try {
1606             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1607             childArgs.add("System.getenv()");
1608             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1609             String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1610                              "FO\u0000=B\u0000R"};
1611             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1612                              "FO\u0000=B\u0000R"};
1613             String[] envp;
1614             if (Windows.is()) {
1615                 envp = envpWin;
1616             } else {
1617                 envp = envpOth;
1618             }
1619             Process p = Runtime.getRuntime().exec(cmdp, envp);
1620             check(commandOutput(p).equals(Windows.is() ? "SystemRoot="+systemRoot+",LC_ALL=C," : "LC_ALL=C,"),
1621                   "Incorrect handling of envstrings containing NULs");
1622         } catch (Throwable t) { unexpected(t); }
1623 
1624         //----------------------------------------------------------------
1625         // Test the redirectErrorStream property
1626         //----------------------------------------------------------------
1627         try {
1628             ProcessBuilder pb = new ProcessBuilder();
1629             equal(pb.redirectErrorStream(), false);
1630             equal(pb.redirectErrorStream(true), pb);
1631             equal(pb.redirectErrorStream(), true);
1632             equal(pb.redirectErrorStream(false), pb);
1633             equal(pb.redirectErrorStream(), false);
1634         } catch (Throwable t) { unexpected(t); }
1635 
1636         try {
1637             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1638             childArgs.add("OutErr");
1639             ProcessBuilder pb = new ProcessBuilder(childArgs);
1640             {
1641                 ProcessResults r = run(pb);
1642                 equal(r.out(), "outout");
1643                 equal(r.err(), "errerr");
1644             }
1645             {
1646                 pb.redirectErrorStream(true);
1647                 ProcessResults r = run(pb);
1648                 equal(r.out(), "outerrouterr");
1649                 equal(r.err(), "");
1650             }
1651         } catch (Throwable t) { unexpected(t); }
1652 
1653         if (Unix.is()) {
1654             //----------------------------------------------------------------
1655             // We can find true and false when PATH is null
1656             //----------------------------------------------------------------
1657             try {
1658                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1659                 childArgs.add("null PATH");
1660                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1661                 pb.environment().remove("PATH");
1662                 ProcessResults r = run(pb);
1663                 equal(r.out(), "");
1664                 equal(r.err(), "");
1665                 equal(r.exitValue(), 0);
1666             } catch (Throwable t) { unexpected(t); }
1667 
1668             //----------------------------------------------------------------
1669             // PATH search algorithm on Unix
1670             //----------------------------------------------------------------
1671             try {
1672                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1673                 childArgs.add("PATH search algorithm");
1674                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1675                 pb.environment().put("PATH", "dir1:dir2:");
1676                 ProcessResults r = run(pb);
1677                 equal(r.out(), "");
1678                 equal(r.err(), "");
1679                 equal(r.exitValue(), True.exitValue());
1680             } catch (Throwable t) { unexpected(t); }
1681 
1682             //----------------------------------------------------------------
1683             // Parent's, not child's PATH is used
1684             //----------------------------------------------------------------
1685             try {
1686                 new File("suBdiR").mkdirs();
1687                 copy("/bin/true", "suBdiR/unliKely");
1688                 final ProcessBuilder pb =
1689                     new ProcessBuilder(new String[]{"unliKely"});
1690                 pb.environment().put("PATH", "suBdiR");
1691                 THROWS(IOException.class,
1692                        new Fun() {void f() throws Throwable {pb.start();}});
1693             } catch (Throwable t) { unexpected(t);
1694             } finally {
1695                 new File("suBdiR/unliKely").delete();
1696                 new File("suBdiR").delete();
1697             }
1698         }
1699 
1700         //----------------------------------------------------------------
1701         // Attempt to start bogus program ""
1702         //----------------------------------------------------------------
1703         try {
1704             new ProcessBuilder("").start();
1705             fail("Expected IOException not thrown");
1706         } catch (IOException e) {
1707             String m = e.getMessage();
1708             if (EnglishUnix.is() &&
1709                 ! matches(m, "No such file or directory"))
1710                 unexpected(e);
1711         } catch (Throwable t) { unexpected(t); }
1712 
1713         //----------------------------------------------------------------
1714         // Check that attempt to execute program name with funny
1715         // characters throws an exception containing those characters.
1716         //----------------------------------------------------------------
1717         for (String programName : new String[] {"\u00f0", "\u01f0"})
1718             try {
1719                 new ProcessBuilder(programName).start();
1720                 fail("Expected IOException not thrown");
1721             } catch (IOException e) {
1722                 String m = e.getMessage();
1723                 Pattern p = Pattern.compile(programName);
1724                 if (! matches(m, programName)
1725                     || (EnglishUnix.is()
1726                         && ! matches(m, "No such file or directory")))
1727                     unexpected(e);
1728             } catch (Throwable t) { unexpected(t); }
1729 
1730         //----------------------------------------------------------------
1731         // Attempt to start process in nonexistent directory fails.
1732         //----------------------------------------------------------------
1733         try {
1734             new ProcessBuilder("echo")
1735                 .directory(new File("UnLiKeLY"))
1736                 .start();
1737             fail("Expected IOException not thrown");
1738         } catch (IOException e) {
1739             String m = e.getMessage();
1740             if (! matches(m, "in directory")
1741                 || (EnglishUnix.is() &&
1742                     ! matches(m, "No such file or directory")))
1743                 unexpected(e);
1744         } catch (Throwable t) { unexpected(t); }
1745 
1746         //----------------------------------------------------------------
1747         // Attempt to write 4095 bytes to the pipe buffer without a
1748         // reader to drain it would deadlock, if not for the fact that
1749         // interprocess pipe buffers are at least 4096 bytes.
1750         //
1751         // Also, check that available reports all the bytes expected
1752         // in the pipe buffer, and that I/O operations do the expected
1753         // things.
1754         //----------------------------------------------------------------
1755         try {
1756             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1757             childArgs.add("print4095");
1758             final int SIZE = 4095;
1759             final Process p = new ProcessBuilder(childArgs).start();
1760             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
1761             p.waitFor();                                // Might hang!
1762             equal(SIZE, p.getInputStream().available());
1763             equal(SIZE, p.getErrorStream().available());
1764             THROWS(IOException.class,
1765                    new Fun(){void f() throws IOException {
1766                        p.getOutputStream().write((byte) '!');
1767                        p.getOutputStream().flush();
1768                        }});
1769 
1770             final byte[] bytes = new byte[SIZE + 1];
1771             equal(SIZE, p.getInputStream().read(bytes));
1772             for (int i = 0; i < SIZE; i++)
1773                 equal((byte) '!', bytes[i]);
1774             equal((byte) 0, bytes[SIZE]);
1775 
1776             equal(SIZE, p.getErrorStream().read(bytes));
1777             for (int i = 0; i < SIZE; i++)
1778                 equal((byte) 'E', bytes[i]);
1779             equal((byte) 0, bytes[SIZE]);
1780 
1781             equal(0, p.getInputStream().available());
1782             equal(0, p.getErrorStream().available());
1783             equal(-1, p.getErrorStream().read());
1784             equal(-1, p.getInputStream().read());
1785 
1786             equal(p.exitValue(), 5);
1787 
1788             p.getInputStream().close();
1789             p.getErrorStream().close();
1790             p.getOutputStream().close();
1791 
1792             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
1793             for (final InputStream in : streams) {
1794                 Fun[] ops = {
1795                     new Fun(){void f() throws IOException {
1796                         in.read(); }},
1797                     new Fun(){void f() throws IOException {
1798                         in.read(bytes); }},
1799                     new Fun(){void f() throws IOException {
1800                         in.available(); }}
1801                 };
1802                 for (Fun op : ops) {
1803                     try {
1804                         op.f();
1805                         fail();
1806                     } catch (IOException expected) {
1807                         check(expected.getMessage()
1808                               .matches("[Ss]tream [Cc]losed"));
1809                     }
1810                 }
1811             }
1812         } catch (Throwable t) { unexpected(t); }
1813 
1814         //----------------------------------------------------------------
1815         // Check that reads which are pending when Process.destroy is
1816         // called, get EOF, not IOException("Stream closed").
1817         //----------------------------------------------------------------
1818         try {
1819             final int cases = 4;
1820             for (int i = 0; i < cases; i++) {
1821                 final int action = i;
1822                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1823                 childArgs.add("sleep");
1824                 final byte[] bytes = new byte[10];
1825                 final Process p = new ProcessBuilder(childArgs).start();
1826                 final CountDownLatch latch = new CountDownLatch(1);
1827                 final Thread thread = new Thread() {
1828                     public void run() {
1829                         try {
1830                             latch.countDown();
1831                             int r;
1832                             switch (action) {
1833                             case 0: r = p.getInputStream().read(); break;
1834                             case 1: r = p.getErrorStream().read(); break;
1835                             case 2: r = p.getInputStream().read(bytes); break;
1836                             case 3: r = p.getErrorStream().read(bytes); break;
1837                             default: throw new Error();
1838                             }
1839                             equal(-1, r);
1840                         } catch (Throwable t) { unexpected(t); }}};
1841 
1842                 thread.start();
1843                 latch.await();
1844                 Thread.sleep(10);
1845                 p.destroy();
1846                 thread.join();
1847             }
1848         } catch (Throwable t) { unexpected(t); }
1849 
1850         //----------------------------------------------------------------
1851         // Check that subprocesses which create subprocesses of their
1852         // own do not cause parent to hang waiting for file
1853         // descriptors to be closed.
1854         //----------------------------------------------------------------
1855         try {
1856             if (Unix.is()
1857                 && new File("/bin/bash").exists()
1858                 && new File("/bin/sleep").exists()) {
1859                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 6666)" };
1860                 final ProcessBuilder pb = new ProcessBuilder(cmd);
1861                 final Process p = pb.start();
1862                 final InputStream stdout = p.getInputStream();
1863                 final InputStream stderr = p.getErrorStream();
1864                 final OutputStream stdin = p.getOutputStream();
1865                 final Thread reader = new Thread() {
1866                     public void run() {
1867                         try { stdout.read(); }
1868                         catch (IOException e) {
1869                             // Check that reader failed because stream was
1870                             // asynchronously closed.
1871                             // e.printStackTrace();
1872                             if (EnglishUnix.is() &&
1873                                 ! (e.getMessage().matches(".*Bad file.*")))
1874                                 unexpected(e);
1875                         }
1876                         catch (Throwable t) { unexpected(t); }}};
1877                 reader.setDaemon(true);
1878                 reader.start();
1879                 Thread.sleep(100);
1880                 p.destroy();
1881                 // Subprocess is now dead, but file descriptors remain open.
1882                 check(p.waitFor() != 0);
1883                 check(p.exitValue() != 0);
1884                 stdout.close();
1885                 stderr.close();
1886                 stdin.close();
1887                 //----------------------------------------------------------
1888                 // There remain unsolved issues with asynchronous close.
1889                 // Here's a highly non-portable experiment to demonstrate:
1890                 //----------------------------------------------------------
1891                 if (Boolean.getBoolean("wakeupJeff!")) {
1892                     System.out.println("wakeupJeff!");
1893                     // Initialize signal handler for INTERRUPT_SIGNAL.
1894                     new FileInputStream("/bin/sleep").getChannel().close();
1895                     // Send INTERRUPT_SIGNAL to every thread in this java.
1896                     String[] wakeupJeff = {
1897                         "/bin/bash", "-c",
1898                         "/bin/ps --noheaders -Lfp $PPID | " +
1899                         "/usr/bin/perl -nale 'print $F[3]' | " +
1900                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
1901                         "/usr/bin/xargs kill -62"
1902                     };
1903                     new ProcessBuilder(wakeupJeff).start().waitFor();
1904                     // If wakeupJeff worked, reader probably got EBADF.
1905                     reader.join();
1906                 }
1907             }
1908         } catch (Throwable t) { unexpected(t); }
1909 
1910         //----------------------------------------------------------------
1911         // Attempt to start process with insufficient permissions fails.
1912         //----------------------------------------------------------------
1913         try {
1914             new File("emptyCommand").delete();
1915             new FileOutputStream("emptyCommand").close();
1916             new File("emptyCommand").setExecutable(false);
1917             new ProcessBuilder("./emptyCommand").start();
1918             fail("Expected IOException not thrown");
1919         } catch (IOException e) {
1920             new File("./emptyCommand").delete();
1921             String m = e.getMessage();
1922             if (EnglishUnix.is() &&
1923                 ! matches(m, "Permission denied"))
1924                 unexpected(e);
1925         } catch (Throwable t) { unexpected(t); }
1926 
1927         new File("emptyCommand").delete();
1928 
1929         //----------------------------------------------------------------
1930         // Check for correct security permission behavior
1931         //----------------------------------------------------------------
1932         final Policy policy = new Policy();
1933         Policy.setPolicy(policy);
1934         System.setSecurityManager(new SecurityManager());
1935 
1936         try {
1937             // No permissions required to CREATE a ProcessBuilder
1938             policy.setPermissions(/* Nothing */);
1939             new ProcessBuilder("env").directory(null).directory();
1940             new ProcessBuilder("env").directory(new File("dir")).directory();
1941             new ProcessBuilder("env").command("??").command();
1942         } catch (Throwable t) { unexpected(t); }
1943 
1944         THROWS(SecurityException.class,
1945             new Fun() { void f() throws IOException {
1946                 policy.setPermissions(/* Nothing */);
1947                 System.getenv("foo");}},
1948             new Fun() { void f() throws IOException {
1949                 policy.setPermissions(/* Nothing */);
1950                 System.getenv();}},
1951             new Fun() { void f() throws IOException {
1952                 policy.setPermissions(/* Nothing */);
1953                 new ProcessBuilder("echo").start();}},
1954             new Fun() { void f() throws IOException {
1955                 policy.setPermissions(/* Nothing */);
1956                 Runtime.getRuntime().exec("echo");}},
1957             new Fun() { void f() throws IOException {
1958                 policy.setPermissions(new RuntimePermission("getenv.bar"));
1959                 System.getenv("foo");}});
1960 
1961         try {
1962             policy.setPermissions(new RuntimePermission("getenv.foo"));
1963             System.getenv("foo");
1964 
1965             policy.setPermissions(new RuntimePermission("getenv.*"));
1966             System.getenv("foo");
1967             System.getenv();
1968             new ProcessBuilder().environment();
1969         } catch (Throwable t) { unexpected(t); }
1970 
1971 
1972         final Permission execPermission
1973             = new FilePermission("<<ALL FILES>>", "execute");
1974 
1975         THROWS(SecurityException.class,
1976             new Fun() { void f() throws IOException {
1977                 // environment permission by itself insufficient
1978                 policy.setPermissions(new RuntimePermission("getenv.*"));
1979                 ProcessBuilder pb = new ProcessBuilder("env");
1980                 pb.environment().put("foo","bar");
1981                 pb.start();}},
1982             new Fun() { void f() throws IOException {
1983                  // exec permission by itself insufficient
1984                  policy.setPermissions(execPermission);
1985                  ProcessBuilder pb = new ProcessBuilder("env");
1986                  pb.environment().put("foo","bar");
1987                  pb.start();}});
1988 
1989         try {
1990             // Both permissions? OK.
1991             policy.setPermissions(new RuntimePermission("getenv.*"),
1992                                   execPermission);
1993             ProcessBuilder pb = new ProcessBuilder("env");
1994             pb.environment().put("foo","bar");
1995             Process p = pb.start();
1996             closeStreams(p);
1997         } catch (IOException e) { // OK
1998         } catch (Throwable t) { unexpected(t); }
1999 
2000         try {
2001             // Don't need environment permission unless READING environment
2002             policy.setPermissions(execPermission);
2003             Runtime.getRuntime().exec("env", new String[]{});
2004         } catch (IOException e) { // OK
2005         } catch (Throwable t) { unexpected(t); }
2006 
2007         try {
2008             // Don't need environment permission unless READING environment
2009             policy.setPermissions(execPermission);
2010             new ProcessBuilder("env").start();
2011         } catch (IOException e) { // OK
2012         } catch (Throwable t) { unexpected(t); }
2013 
2014         // Restore "normal" state without a security manager
2015         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2016         System.setSecurityManager(null);
2017 
2018     }
2019 
2020     static void closeStreams(Process p) {
2021         try {
2022             p.getOutputStream().close();
2023             p.getInputStream().close();
2024             p.getErrorStream().close();
2025         } catch (Throwable t) { unexpected(t); }
2026     }
2027 
2028     //----------------------------------------------------------------
2029     // A Policy class designed to make permissions fiddling very easy.
2030     //----------------------------------------------------------------
2031     private static class Policy extends java.security.Policy {
2032         private Permissions perms;
2033 
2034         public void setPermissions(Permission...permissions) {
2035             perms = new Permissions();
2036             for (Permission permission : permissions)
2037                 perms.add(permission);
2038         }
2039 
2040         public Policy() { setPermissions(/* Nothing */); }
2041 
2042         public PermissionCollection getPermissions(CodeSource cs) {
2043             return perms;
2044         }
2045 
2046         public PermissionCollection getPermissions(ProtectionDomain pd) {
2047             return perms;
2048         }
2049 
2050         public boolean implies(ProtectionDomain pd, Permission p) {
2051             return perms.implies(p);
2052         }
2053 
2054         public void refresh() {}
2055     }
2056 
2057     private static class StreamAccumulator extends Thread {
2058         private final InputStream is;
2059         private final StringBuilder sb = new StringBuilder();
2060         private Throwable throwable = null;
2061 
2062         public String result () throws Throwable {
2063             if (throwable != null)
2064                 throw throwable;
2065             return sb.toString();
2066         }
2067 
2068         StreamAccumulator (InputStream is) {
2069             this.is = is;
2070         }
2071 
2072         public void run() {
2073             try {
2074                 Reader r = new InputStreamReader(is);
2075                 char[] buf = new char[4096];
2076                 int n;
2077                 while ((n = r.read(buf)) > 0) {
2078                     sb.append(buf,0,n);
2079                 }
2080             } catch (Throwable t) {
2081                 throwable = t;
2082             } finally {
2083                 try { is.close(); }
2084                 catch (Throwable t) { throwable = t; }
2085             }
2086         }
2087     }
2088 
2089     static ProcessResults run(ProcessBuilder pb) {
2090         try {
2091             return run(pb.start());
2092         } catch (Throwable t) { unexpected(t); return null; }
2093     }
2094 
2095     private static ProcessResults run(Process p) {
2096         Throwable throwable = null;
2097         int exitValue = -1;
2098         String out = "";
2099         String err = "";
2100 
2101         StreamAccumulator outAccumulator =
2102             new StreamAccumulator(p.getInputStream());
2103         StreamAccumulator errAccumulator =
2104             new StreamAccumulator(p.getErrorStream());
2105 
2106         try {
2107             outAccumulator.start();
2108             errAccumulator.start();
2109 
2110             exitValue = p.waitFor();
2111 
2112             outAccumulator.join();
2113             errAccumulator.join();
2114 
2115             out = outAccumulator.result();
2116             err = errAccumulator.result();
2117         } catch (Throwable t) {
2118             throwable = t;
2119         }
2120 
2121         return new ProcessResults(out, err, exitValue, throwable);
2122     }
2123 
2124     //----------------------------------------------------------------
2125     // Results of a command
2126     //----------------------------------------------------------------
2127     private static class ProcessResults {
2128         private final String out;
2129         private final String err;
2130         private final int exitValue;
2131         private final Throwable throwable;
2132 
2133         public ProcessResults(String out,
2134                               String err,
2135                               int exitValue,
2136                               Throwable throwable) {
2137             this.out = out;
2138             this.err = err;
2139             this.exitValue = exitValue;
2140             this.throwable = throwable;
2141         }
2142 
2143         public String out()          { return out; }
2144         public String err()          { return err; }
2145         public int exitValue()       { return exitValue; }
2146         public Throwable throwable() { return throwable; }
2147 
2148         public String toString() {
2149             StringBuilder sb = new StringBuilder();
2150             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2151                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2152                 .append("exitValue = " + exitValue + "\n");
2153             if (throwable != null)
2154                 sb.append(throwable.getStackTrace());
2155             return sb.toString();
2156         }
2157     }
2158 
2159     //--------------------- Infrastructure ---------------------------
2160     static volatile int passed = 0, failed = 0;
2161     static void pass() {passed++;}
2162     static void fail() {failed++; Thread.dumpStack();}
2163     static void fail(String msg) {System.out.println(msg); fail();}
2164     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2165     static void check(boolean cond) {if (cond) pass(); else fail();}
2166     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2167     static void equal(Object x, Object y) {
2168         if (x == null ? y == null : x.equals(y)) pass();
2169         else fail(x + " not equal to " + y);}
2170 
2171     public static void main(String[] args) throws Throwable {
2172         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2173         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2174         if (failed > 0) throw new AssertionError("Some tests failed");}
2175     private static abstract class Fun {abstract void f() throws Throwable;}
2176     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2177         for (Fun f : fs)
2178             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2179             catch (Throwable t) {
2180                 if (k.isAssignableFrom(t.getClass())) pass();
2181                 else unexpected(t);}}
2182 }