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