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
  30  * @summary Basic tests for Process and Environment Variable code
  31  * @run main/othervm/timeout=300 Basic
  32  * @author Martin Buchholz
  33  */
  34 
  35 import java.lang.ProcessBuilder.Redirect;
  36 import static java.lang.ProcessBuilder.Redirect.*;
  37 
  38 import java.io.*;
  39 import java.util.*;
  40 import java.util.concurrent.CountDownLatch;

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











2093     }




2094 
































































2095     static void closeStreams(Process p) {
2096         try {
2097             p.getOutputStream().close();
2098             p.getInputStream().close();
2099             p.getErrorStream().close();
2100         } catch (Throwable t) { unexpected(t); }
2101     }
2102 
2103     //----------------------------------------------------------------
2104     // A Policy class designed to make permissions fiddling very easy.
2105     //----------------------------------------------------------------
2106     private static class Policy extends java.security.Policy {
2107         private Permissions perms;
2108 
2109         public void setPermissions(Permission...permissions) {
2110             perms = new Permissions();
2111             for (Permission permission : permissions)
2112                 perms.add(permission);
2113         }
2114 
2115         public Policy() { setPermissions(/* Nothing */); }
2116 
2117         public PermissionCollection getPermissions(CodeSource cs) {
2118             return perms;
2119         }
2120 
2121         public PermissionCollection getPermissions(ProtectionDomain pd) {
2122             return perms;
2123         }
2124 
2125         public boolean implies(ProtectionDomain pd, Permission p) {
2126             return perms.implies(p);
2127         }
2128 
2129         public void refresh() {}
2130     }
2131 
2132     private static class StreamAccumulator extends Thread {
2133         private final InputStream is;
2134         private final StringBuilder sb = new StringBuilder();
2135         private Throwable throwable = null;
2136 
2137         public String result () throws Throwable {
2138             if (throwable != null)
2139                 throw throwable;
2140             return sb.toString();
2141         }
2142 
2143         StreamAccumulator (InputStream is) {
2144             this.is = is;
2145         }
2146 
2147         public void run() {
2148             try {
2149                 Reader r = new InputStreamReader(is);
2150                 char[] buf = new char[4096];
2151                 int n;
2152                 while ((n = r.read(buf)) > 0) {
2153                     sb.append(buf,0,n);
2154                 }
2155             } catch (Throwable t) {
2156                 throwable = t;
2157             } finally {
2158                 try { is.close(); }
2159                 catch (Throwable t) { throwable = t; }
2160             }
2161         }
2162     }
2163 
2164     static ProcessResults run(ProcessBuilder pb) {
2165         try {
2166             return run(pb.start());
2167         } catch (Throwable t) { unexpected(t); return null; }
2168     }
2169 
2170     private static ProcessResults run(Process p) {
2171         Throwable throwable = null;
2172         int exitValue = -1;
2173         String out = "";
2174         String err = "";
2175 
2176         StreamAccumulator outAccumulator =
2177             new StreamAccumulator(p.getInputStream());
2178         StreamAccumulator errAccumulator =
2179             new StreamAccumulator(p.getErrorStream());
2180 
2181         try {
2182             outAccumulator.start();
2183             errAccumulator.start();
2184 
2185             exitValue = p.waitFor();
2186 
2187             outAccumulator.join();
2188             errAccumulator.join();
2189 
2190             out = outAccumulator.result();
2191             err = errAccumulator.result();
2192         } catch (Throwable t) {
2193             throwable = t;
2194         }
2195 
2196         return new ProcessResults(out, err, exitValue, throwable);
2197     }
2198 
2199     //----------------------------------------------------------------
2200     // Results of a command
2201     //----------------------------------------------------------------
2202     private static class ProcessResults {
2203         private final String out;
2204         private final String err;
2205         private final int exitValue;
2206         private final Throwable throwable;
2207 
2208         public ProcessResults(String out,
2209                               String err,
2210                               int exitValue,
2211                               Throwable throwable) {
2212             this.out = out;
2213             this.err = err;
2214             this.exitValue = exitValue;
2215             this.throwable = throwable;
2216         }
2217 
2218         public String out()          { return out; }
2219         public String err()          { return err; }
2220         public int exitValue()       { return exitValue; }
2221         public Throwable throwable() { return throwable; }
2222 
2223         public String toString() {
2224             StringBuilder sb = new StringBuilder();
2225             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2226                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2227                 .append("exitValue = " + exitValue + "\n");
2228             if (throwable != null)
2229                 sb.append(throwable.getStackTrace());
2230             return sb.toString();
2231         }
2232     }
2233 
2234     //--------------------- Infrastructure ---------------------------
2235     static volatile int passed = 0, failed = 0;
2236     static void pass() {passed++;}
2237     static void fail() {failed++; Thread.dumpStack();}
2238     static void fail(String msg) {System.out.println(msg); fail();}
2239     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2240     static void check(boolean cond) {if (cond) pass(); else fail();}
2241     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2242     static void equal(Object x, Object y) {
2243         if (x == null ? y == null : x.equals(y)) pass();
2244         else fail(x + " not equal to " + y);}
2245 
2246     public static void main(String[] args) throws Throwable {
2247         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2248         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2249         if (failed > 0) throw new AssertionError("Some tests failed");}
2250     private static abstract class Fun {abstract void f() throws Throwable;}
2251     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2252         for (Fun f : fs)
2253             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2254             catch (Throwable t) {
2255                 if (k.isAssignableFrom(t.getClass())) pass();
2256                 else unexpected(t);}}
2257 }
--- EOF ---