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