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