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(new String[]
 565             { javaExe, "-classpath", absolutifyPath(classpath),
 566               "Basic$JavaChild"});
 567 
 568     private static void testEncoding(String encoding, String tested) {
 569         try {
 570             // If round trip conversion works, should be able to set env vars
 571             // correctly in child.
 572             if (new String(tested.getBytes()).equals(tested)) {
 573                 out.println("Testing " + encoding + " environment values");
 574                 ProcessBuilder pb = new ProcessBuilder();
 575                 pb.environment().put("ASCIINAME",tested);
 576                 equal(getenvInChild(pb,"ASCIINAME"), tested);
 577             }
 578         } catch (Throwable t) { unexpected(t); }
 579     }
 580 
 581     static class Windows {
 582         public static boolean is() { return is; }
 583         private static final boolean is =
 584             System.getProperty("os.name").startsWith("Windows");
 585     }
 586 
 587     static class Unix {
 588         public static boolean is() { return is; }
 589         private static final boolean is =
 590             (! Windows.is() &&
 591              new File("/bin/sh").exists() &&
 592              new File("/bin/true").exists() &&
 593              new File("/bin/false").exists());
 594     }
 595 
 596     static class UnicodeOS {
 597         public static boolean is() { return is; }
 598         private static final String osName = System.getProperty("os.name");
 599         private static final boolean is =
 600             // MacOS X would probably also qualify
 601             osName.startsWith("Windows")   &&
 602             ! osName.startsWith("Windows 9") &&
 603             ! osName.equals("Windows Me");
 604     }
 605 
 606     static class MacOSX {
 607         public static boolean is() { return is; }
 608         private static final String osName = System.getProperty("os.name");
 609         private static final boolean is = osName.contains("OS X");
 610     }
 611 
 612     static class True {
 613         public static int exitValue() { return 0; }
 614     }
 615 
 616     private static class False {
 617         public static int exitValue() { return exitValue; }
 618         private static final int exitValue = exitValue0();
 619         private static int exitValue0() {
 620             // /bin/false returns an *unspecified* non-zero number.
 621             try {
 622                 if (! Unix.is())
 623                     return -1;
 624                 else {
 625                     int rc = new ProcessBuilder("/bin/false")
 626                         .start().waitFor();
 627                     check(rc != 0);
 628                     return rc;
 629                 }
 630             } catch (Throwable t) { unexpected(t); return -1; }
 631         }
 632     }
 633 
 634     static class EnglishUnix {
 635         private final static Boolean is =
 636             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
 637 
 638         private static boolean isEnglish(String envvar) {
 639             String val = getenv(envvar);
 640             return (val == null) || val.matches("en.*");
 641         }
 642 
 643         /** Returns true if we can expect English OS error strings */
 644         static boolean is() { return is; }
 645     }
 646 
 647     static class DelegatingProcess extends Process {
 648         final Process p;
 649 
 650         DelegatingProcess(Process p) {
 651             this.p = p;
 652         }
 653 
 654         @Override
 655         public void destroy() {
 656             p.destroy();
 657         }
 658 
 659         @Override
 660         public int exitValue() {
 661             return p.exitValue();
 662         }
 663 
 664         @Override
 665         public int waitFor() throws InterruptedException {
 666             return p.waitFor();
 667         }
 668 
 669         @Override
 670         public OutputStream getOutputStream() {
 671             return p.getOutputStream();
 672         }
 673 
 674         @Override
 675         public InputStream getInputStream() {
 676             return p.getInputStream();
 677         }
 678 
 679         @Override
 680         public InputStream getErrorStream() {
 681             return p.getErrorStream();
 682         }
 683     }
 684 
 685     private static boolean matches(String str, String regex) {
 686         return Pattern.compile(regex).matcher(str).find();
 687     }
 688 
 689     private static String matchAndExtract(String str, String regex) {
 690         Matcher matcher = Pattern.compile(regex).matcher(str);
 691         if (matcher.find()) {
 692             return matcher.group();
 693         } else {
 694             return "";
 695         }
 696     }
 697 
 698     /* Only used for Mac OS X --
 699      * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
 700      * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
 701      * be set in Mac OS X.
 702      * Remove them both from the list of env variables
 703      */
 704     private static String removeMacExpectedVars(String vars) {
 705         // Check for __CF_USER_TEXT_ENCODING
 706         String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
 707                                             +cfUserTextEncoding+",","");
 708         // Check for JAVA_MAIN_CLASS_<pid>
 709         String javaMainClassStr
 710                 = matchAndExtract(cleanedVars,
 711                                     "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
 712         return cleanedVars.replace(javaMainClassStr,"");
 713     }
 714 
 715     private static String sortByLinesWindowsly(String text) {
 716         String[] lines = text.split("\n");
 717         Arrays.sort(lines, new WindowsComparator());
 718         StringBuilder sb = new StringBuilder();
 719         for (String line : lines)
 720             sb.append(line).append("\n");
 721         return sb.toString();
 722     }
 723 
 724     private static void checkMapSanity(Map<String,String> map) {
 725         try {
 726             Set<String> keySet = map.keySet();
 727             Collection<String> values = map.values();
 728             Set<Map.Entry<String,String>> entrySet = map.entrySet();
 729 
 730             equal(entrySet.size(), keySet.size());
 731             equal(entrySet.size(), values.size());
 732 
 733             StringBuilder s1 = new StringBuilder();
 734             for (Map.Entry<String,String> e : entrySet)
 735                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
 736 
 737             StringBuilder s2 = new StringBuilder();
 738             for (String var : keySet)
 739                 s2.append(var + "=" + map.get(var) + "\n");
 740 
 741             equal(s1.toString(), s2.toString());
 742 
 743             Iterator<String> kIter = keySet.iterator();
 744             Iterator<String> vIter = values.iterator();
 745             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
 746 
 747             while (eIter.hasNext()) {
 748                 Map.Entry<String,String> entry = eIter.next();
 749                 String key   = kIter.next();
 750                 String value = vIter.next();
 751                 check(entrySet.contains(entry));
 752                 check(keySet.contains(key));
 753                 check(values.contains(value));
 754                 check(map.containsKey(key));
 755                 check(map.containsValue(value));
 756                 equal(entry.getKey(), key);
 757                 equal(entry.getValue(), value);
 758             }
 759             check(! kIter.hasNext() &&
 760                   ! vIter.hasNext());
 761 
 762         } catch (Throwable t) { unexpected(t); }
 763     }
 764 
 765     private static void checkMapEquality(Map<String,String> map1,
 766                                          Map<String,String> map2) {
 767         try {
 768             equal(map1.size(), map2.size());
 769             equal(map1.isEmpty(), map2.isEmpty());
 770             for (String key : map1.keySet()) {
 771                 equal(map1.get(key), map2.get(key));
 772                 check(map2.keySet().contains(key));
 773             }
 774             equal(map1, map2);
 775             equal(map2, map1);
 776             equal(map1.entrySet(), map2.entrySet());
 777             equal(map2.entrySet(), map1.entrySet());
 778             equal(map1.keySet(), map2.keySet());
 779             equal(map2.keySet(), map1.keySet());
 780 
 781             equal(map1.hashCode(), map2.hashCode());
 782             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
 783             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
 784         } catch (Throwable t) { unexpected(t); }
 785     }
 786 
 787     static void checkRedirects(ProcessBuilder pb,
 788                                Redirect in, Redirect out, Redirect err) {
 789         equal(pb.redirectInput(),  in);
 790         equal(pb.redirectOutput(), out);
 791         equal(pb.redirectError(),  err);
 792     }
 793 
 794     static void redirectIO(ProcessBuilder pb,
 795                            Redirect in, Redirect out, Redirect err) {
 796         pb.redirectInput(in);
 797         pb.redirectOutput(out);
 798         pb.redirectError(err);
 799     }
 800 
 801     static void setFileContents(File file, String contents) {
 802         try {
 803             Writer w = new FileWriter(file);
 804             w.write(contents);
 805             w.close();
 806         } catch (Throwable t) { unexpected(t); }
 807     }
 808 
 809     static String fileContents(File file) {
 810         try {
 811             Reader r = new FileReader(file);
 812             StringBuilder sb = new StringBuilder();
 813             char[] buffer = new char[1024];
 814             int n;
 815             while ((n = r.read(buffer)) != -1)
 816                 sb.append(buffer,0,n);
 817             r.close();
 818             return new String(sb);
 819         } catch (Throwable t) { unexpected(t); return ""; }
 820     }
 821 
 822     static void testIORedirection() throws Throwable {
 823         final File ifile = new File("ifile");
 824         final File ofile = new File("ofile");
 825         final File efile = new File("efile");
 826         ifile.delete();
 827         ofile.delete();
 828         efile.delete();
 829 
 830         //----------------------------------------------------------------
 831         // Check mutual inequality of different types of Redirect
 832         //----------------------------------------------------------------
 833         Redirect[] redirects =
 834             { PIPE,
 835               INHERIT,
 836               Redirect.from(ifile),
 837               Redirect.to(ifile),
 838               Redirect.appendTo(ifile),
 839               Redirect.from(ofile),
 840               Redirect.to(ofile),
 841               Redirect.appendTo(ofile),
 842             };
 843         for (int i = 0; i < redirects.length; i++)
 844             for (int j = 0; j < redirects.length; j++)
 845                 equal(redirects[i].equals(redirects[j]), (i == j));
 846 
 847         //----------------------------------------------------------------
 848         // Check basic properties of different types of Redirect
 849         //----------------------------------------------------------------
 850         equal(PIPE.type(), Redirect.Type.PIPE);
 851         equal(PIPE.toString(), "PIPE");
 852         equal(PIPE.file(), null);
 853 
 854         equal(INHERIT.type(), Redirect.Type.INHERIT);
 855         equal(INHERIT.toString(), "INHERIT");
 856         equal(INHERIT.file(), null);
 857 
 858         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
 859         equal(Redirect.from(ifile).toString(),
 860               "redirect to read from file \"ifile\"");
 861         equal(Redirect.from(ifile).file(), ifile);
 862         equal(Redirect.from(ifile),
 863               Redirect.from(ifile));
 864         equal(Redirect.from(ifile).hashCode(),
 865               Redirect.from(ifile).hashCode());
 866 
 867         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
 868         equal(Redirect.to(ofile).toString(),
 869               "redirect to write to file \"ofile\"");
 870         equal(Redirect.to(ofile).file(), ofile);
 871         equal(Redirect.to(ofile),
 872               Redirect.to(ofile));
 873         equal(Redirect.to(ofile).hashCode(),
 874               Redirect.to(ofile).hashCode());
 875 
 876         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
 877         equal(Redirect.appendTo(efile).toString(),
 878               "redirect to append to file \"efile\"");
 879         equal(Redirect.appendTo(efile).file(), efile);
 880         equal(Redirect.appendTo(efile),
 881               Redirect.appendTo(efile));
 882         equal(Redirect.appendTo(efile).hashCode(),
 883               Redirect.appendTo(efile).hashCode());
 884 
 885         //----------------------------------------------------------------
 886         // Check initial values of redirects
 887         //----------------------------------------------------------------
 888         List<String> childArgs = new ArrayList<String>(javaChildArgs);
 889         childArgs.add("testIO");
 890         final ProcessBuilder pb = new ProcessBuilder(childArgs);
 891         checkRedirects(pb, PIPE, PIPE, PIPE);
 892 
 893         //----------------------------------------------------------------
 894         // Check inheritIO
 895         //----------------------------------------------------------------
 896         pb.inheritIO();
 897         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 898 
 899         //----------------------------------------------------------------
 900         // Check setters and getters agree
 901         //----------------------------------------------------------------
 902         pb.redirectInput(ifile);
 903         equal(pb.redirectInput().file(), ifile);
 904         equal(pb.redirectInput(), Redirect.from(ifile));
 905 
 906         pb.redirectOutput(ofile);
 907         equal(pb.redirectOutput().file(), ofile);
 908         equal(pb.redirectOutput(), Redirect.to(ofile));
 909 
 910         pb.redirectError(efile);
 911         equal(pb.redirectError().file(), efile);
 912         equal(pb.redirectError(), Redirect.to(efile));
 913 
 914         THROWS(IllegalArgumentException.class,
 915             new Fun(){void f() {
 916                 pb.redirectInput(Redirect.to(ofile)); }},
 917             new Fun(){void f() {
 918                 pb.redirectInput(Redirect.appendTo(ofile)); }},
 919             new Fun(){void f() {
 920                 pb.redirectOutput(Redirect.from(ifile)); }},
 921             new Fun(){void f() {
 922                 pb.redirectError(Redirect.from(ifile)); }});
 923 
 924         THROWS(IOException.class,
 925                // Input file does not exist
 926                new Fun(){void f() throws Throwable { pb.start(); }});
 927         setFileContents(ifile, "standard input");
 928 
 929         //----------------------------------------------------------------
 930         // Writing to non-existent files
 931         //----------------------------------------------------------------
 932         {
 933             ProcessResults r = run(pb);
 934             equal(r.exitValue(), 0);
 935             equal(fileContents(ofile), "standard output");
 936             equal(fileContents(efile), "standard error");
 937             equal(r.out(), "");
 938             equal(r.err(), "");
 939             ofile.delete();
 940             efile.delete();
 941         }
 942 
 943         //----------------------------------------------------------------
 944         // Both redirectErrorStream + redirectError
 945         //----------------------------------------------------------------
 946         {
 947             pb.redirectErrorStream(true);
 948             ProcessResults r = run(pb);
 949             equal(r.exitValue(), 0);
 950             equal(fileContents(ofile),
 951                   "standard error" + "standard output");
 952             equal(fileContents(efile), "");
 953             equal(r.out(), "");
 954             equal(r.err(), "");
 955             ofile.delete();
 956             efile.delete();
 957         }
 958 
 959         //----------------------------------------------------------------
 960         // Appending to existing files
 961         //----------------------------------------------------------------
 962         {
 963             setFileContents(ofile, "ofile-contents");
 964             setFileContents(efile, "efile-contents");
 965             pb.redirectOutput(Redirect.appendTo(ofile));
 966             pb.redirectError(Redirect.appendTo(efile));
 967             pb.redirectErrorStream(false);
 968             ProcessResults r = run(pb);
 969             equal(r.exitValue(), 0);
 970             equal(fileContents(ofile),
 971                   "ofile-contents" + "standard output");
 972             equal(fileContents(efile),
 973                   "efile-contents" + "standard error");
 974             equal(r.out(), "");
 975             equal(r.err(), "");
 976             ofile.delete();
 977             efile.delete();
 978         }
 979 
 980         //----------------------------------------------------------------
 981         // Replacing existing files
 982         //----------------------------------------------------------------
 983         {
 984             setFileContents(ofile, "ofile-contents");
 985             setFileContents(efile, "efile-contents");
 986             pb.redirectOutput(ofile);
 987             pb.redirectError(Redirect.to(efile));
 988             ProcessResults r = run(pb);
 989             equal(r.exitValue(), 0);
 990             equal(fileContents(ofile), "standard output");
 991             equal(fileContents(efile), "standard error");
 992             equal(r.out(), "");
 993             equal(r.err(), "");
 994             ofile.delete();
 995             efile.delete();
 996         }
 997 
 998         //----------------------------------------------------------------
 999         // Appending twice to the same file?
1000         //----------------------------------------------------------------
1001         {
1002             setFileContents(ofile, "ofile-contents");
1003             setFileContents(efile, "efile-contents");
1004             Redirect appender = Redirect.appendTo(ofile);
1005             pb.redirectOutput(appender);
1006             pb.redirectError(appender);
1007             ProcessResults r = run(pb);
1008             equal(r.exitValue(), 0);
1009             equal(fileContents(ofile),
1010                   "ofile-contents" +
1011                   "standard error" +
1012                   "standard output");
1013             equal(fileContents(efile), "efile-contents");
1014             equal(r.out(), "");
1015             equal(r.err(), "");
1016             ifile.delete();
1017             ofile.delete();
1018             efile.delete();
1019         }
1020 
1021         //----------------------------------------------------------------
1022         // Testing INHERIT is harder.
1023         // Note that this requires __FOUR__ nested JVMs involved in one test,
1024         // if you count the harness JVM.
1025         //----------------------------------------------------------------
1026         for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
1027             redirectIO(pb, PIPE, PIPE, PIPE);
1028             List<String> command = pb.command();
1029             command.set(command.size() - 1, testName);
1030             Process p = pb.start();
1031             new PrintStream(p.getOutputStream()).print("standard input");
1032             p.getOutputStream().close();
1033             ProcessResults r = run(p);
1034             equal(r.exitValue(), 0);
1035             equal(r.out(), "standard output");
1036             equal(r.err(), "standard error");
1037         }
1038 
1039         //----------------------------------------------------------------
1040         // Test security implications of I/O redirection
1041         //----------------------------------------------------------------
1042 
1043         // Read access to current directory is always granted;
1044         // So create a tmpfile for input instead.
1045         final File tmpFile = File.createTempFile("Basic", "tmp");
1046         setFileContents(tmpFile, "standard input");
1047 
1048         final Policy policy = new Policy();
1049         Policy.setPolicy(policy);
1050         System.setSecurityManager(new SecurityManager());
1051         try {
1052             final Permission xPermission
1053                 = new FilePermission("<<ALL FILES>>", "execute");
1054             final Permission rxPermission
1055                 = new FilePermission("<<ALL FILES>>", "read,execute");
1056             final Permission wxPermission
1057                 = new FilePermission("<<ALL FILES>>", "write,execute");
1058             final Permission rwxPermission
1059                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
1060 
1061             THROWS(SecurityException.class,
1062                new Fun() { void f() throws IOException {
1063                    policy.setPermissions(xPermission);
1064                    redirectIO(pb, from(tmpFile), PIPE, PIPE);
1065                    pb.start();}},
1066                new Fun() { void f() throws IOException {
1067                    policy.setPermissions(rxPermission);
1068                    redirectIO(pb, PIPE, to(ofile), PIPE);
1069                    pb.start();}},
1070                new Fun() { void f() throws IOException {
1071                    policy.setPermissions(rxPermission);
1072                    redirectIO(pb, PIPE, PIPE, to(efile));
1073                    pb.start();}});
1074 
1075             {
1076                 policy.setPermissions(rxPermission);
1077                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
1078                 ProcessResults r = run(pb);
1079                 equal(r.out(), "standard output");
1080                 equal(r.err(), "standard error");
1081             }
1082 
1083             {
1084                 policy.setPermissions(wxPermission);
1085                 redirectIO(pb, PIPE, to(ofile), to(efile));
1086                 Process p = pb.start();
1087                 new PrintStream(p.getOutputStream()).print("standard input");
1088                 p.getOutputStream().close();
1089                 ProcessResults r = run(p);
1090                 policy.setPermissions(rwxPermission);
1091                 equal(fileContents(ofile), "standard output");
1092                 equal(fileContents(efile), "standard error");
1093             }
1094 
1095             {
1096                 policy.setPermissions(rwxPermission);
1097                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1098                 ProcessResults r = run(pb);
1099                 policy.setPermissions(rwxPermission);
1100                 equal(fileContents(ofile), "standard output");
1101                 equal(fileContents(efile), "standard error");
1102             }
1103 
1104         } finally {
1105             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1106             System.setSecurityManager(null);
1107             tmpFile.delete();
1108             ifile.delete();
1109             ofile.delete();
1110             efile.delete();
1111         }
1112     }
1113 
1114     private static void realMain(String[] args) throws Throwable {
1115         if (Windows.is())
1116             System.out.println("This appears to be a Windows system.");
1117         if (Unix.is())
1118             System.out.println("This appears to be a Unix system.");
1119         if (UnicodeOS.is())
1120             System.out.println("This appears to be a Unicode-based OS.");
1121 
1122         try { testIORedirection(); }
1123         catch (Throwable t) { unexpected(t); }
1124 
1125         //----------------------------------------------------------------
1126         // Basic tests for setting, replacing and deleting envvars
1127         //----------------------------------------------------------------
1128         try {
1129             ProcessBuilder pb = new ProcessBuilder();
1130             Map<String,String> environ = pb.environment();
1131 
1132             // New env var
1133             environ.put("QUUX", "BAR");
1134             equal(environ.get("QUUX"), "BAR");
1135             equal(getenvInChild(pb,"QUUX"), "BAR");
1136 
1137             // Modify env var
1138             environ.put("QUUX","bear");
1139             equal(environ.get("QUUX"), "bear");
1140             equal(getenvInChild(pb,"QUUX"), "bear");
1141             checkMapSanity(environ);
1142 
1143             // Remove env var
1144             environ.remove("QUUX");
1145             equal(environ.get("QUUX"), null);
1146             equal(getenvInChild(pb,"QUUX"), "null");
1147             checkMapSanity(environ);
1148 
1149             // Remove non-existent env var
1150             environ.remove("QUUX");
1151             equal(environ.get("QUUX"), null);
1152             equal(getenvInChild(pb,"QUUX"), "null");
1153             checkMapSanity(environ);
1154         } catch (Throwable t) { unexpected(t); }
1155 
1156         //----------------------------------------------------------------
1157         // Pass Empty environment to child
1158         //----------------------------------------------------------------
1159         try {
1160             ProcessBuilder pb = new ProcessBuilder();
1161             pb.environment().clear();
1162             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1163             if (Windows.is()) {
1164                 pb.environment().put("SystemRoot", systemRoot);
1165             }
1166             String result = getenvInChild(pb);
1167             if (MacOSX.is()) {
1168                 result = removeMacExpectedVars(result);
1169             }
1170             equal(result, expected);
1171         } catch (Throwable t) { unexpected(t); }
1172 
1173         //----------------------------------------------------------------
1174         // System.getenv() is read-only.
1175         //----------------------------------------------------------------
1176         THROWS(UnsupportedOperationException.class,
1177             new Fun(){void f(){ getenv().put("FOO","BAR");}},
1178             new Fun(){void f(){ getenv().remove("PATH");}},
1179             new Fun(){void f(){ getenv().keySet().remove("PATH");}},
1180             new Fun(){void f(){ getenv().values().remove("someValue");}});
1181 
1182         try {
1183             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1184             if (! c.isEmpty())
1185                 try {
1186                     c.iterator().next().setValue("foo");
1187                     fail("Expected UnsupportedOperationException not thrown");
1188                 } catch (UnsupportedOperationException e) {} // OK
1189         } catch (Throwable t) { unexpected(t); }
1190 
1191         //----------------------------------------------------------------
1192         // System.getenv() always returns the same object in our implementation.
1193         //----------------------------------------------------------------
1194         try {
1195             check(System.getenv() == System.getenv());
1196         } catch (Throwable t) { unexpected(t); }
1197 
1198         //----------------------------------------------------------------
1199         // You can't create an env var name containing "=",
1200         // or an env var name or value containing NUL.
1201         //----------------------------------------------------------------
1202         {
1203             final Map<String,String> m = new ProcessBuilder().environment();
1204             THROWS(IllegalArgumentException.class,
1205                 new Fun(){void f(){ m.put("FOO=","BAR");}},
1206                 new Fun(){void f(){ m.put("FOO\u0000","BAR");}},
1207                 new Fun(){void f(){ m.put("FOO","BAR\u0000");}});
1208         }
1209 
1210         //----------------------------------------------------------------
1211         // Commands must never be null.
1212         //----------------------------------------------------------------
1213         THROWS(NullPointerException.class,
1214                new Fun(){void f(){
1215                    new ProcessBuilder((List<String>)null);}},
1216                new Fun(){void f(){
1217                    new ProcessBuilder().command((List<String>)null);}});
1218 
1219         //----------------------------------------------------------------
1220         // Put in a command; get the same one back out.
1221         //----------------------------------------------------------------
1222         try {
1223             List<String> command = new ArrayList<String>();
1224             ProcessBuilder pb = new ProcessBuilder(command);
1225             check(pb.command() == command);
1226             List<String> command2 = new ArrayList<String>(2);
1227             command2.add("foo");
1228             command2.add("bar");
1229             pb.command(command2);
1230             check(pb.command() == command2);
1231             pb.command("foo", "bar");
1232             check(pb.command() != command2 && pb.command().equals(command2));
1233             pb.command(command2);
1234             command2.add("baz");
1235             equal(pb.command().get(2), "baz");
1236         } catch (Throwable t) { unexpected(t); }
1237 
1238         //----------------------------------------------------------------
1239         // Commands must contain at least one element.
1240         //----------------------------------------------------------------
1241         THROWS(IndexOutOfBoundsException.class,
1242             new Fun() { void f() throws IOException {
1243                 new ProcessBuilder().start();}},
1244             new Fun() { void f() throws IOException {
1245                 new ProcessBuilder(new ArrayList<String>()).start();}},
1246             new Fun() { void f() throws IOException {
1247                 Runtime.getRuntime().exec(new String[]{});}});
1248 
1249         //----------------------------------------------------------------
1250         // Commands must not contain null elements at start() time.
1251         //----------------------------------------------------------------
1252         THROWS(NullPointerException.class,
1253             new Fun() { void f() throws IOException {
1254                 new ProcessBuilder("foo",null,"bar").start();}},
1255             new Fun() { void f() throws IOException {
1256                 new ProcessBuilder((String)null).start();}},
1257             new Fun() { void f() throws IOException {
1258                 new ProcessBuilder(new String[]{null}).start();}},
1259             new Fun() { void f() throws IOException {
1260                 new ProcessBuilder(new String[]{"foo",null,"bar"}).start();}});
1261 
1262         //----------------------------------------------------------------
1263         // Command lists are growable.
1264         //----------------------------------------------------------------
1265         try {
1266             new ProcessBuilder().command().add("foo");
1267             new ProcessBuilder("bar").command().add("foo");
1268             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1269         } catch (Throwable t) { unexpected(t); }
1270 
1271         //----------------------------------------------------------------
1272         // Nulls in environment updates generate NullPointerException
1273         //----------------------------------------------------------------
1274         try {
1275             final Map<String,String> env = new ProcessBuilder().environment();
1276             THROWS(NullPointerException.class,
1277                 new Fun(){void f(){ env.put("foo",null);}},
1278                 new Fun(){void f(){ env.put(null,"foo");}},
1279                 new Fun(){void f(){ env.remove(null);}},
1280                 new Fun(){void f(){
1281                     for (Map.Entry<String,String> e : env.entrySet())
1282                         e.setValue(null);}},
1283                 new Fun() { void f() throws IOException {
1284                     Runtime.getRuntime().exec(new String[]{"foo"},
1285                                               new String[]{null});}});
1286         } catch (Throwable t) { unexpected(t); }
1287 
1288         //----------------------------------------------------------------
1289         // Non-String types in environment updates generate ClassCastException
1290         //----------------------------------------------------------------
1291         try {
1292             final Map<String,String> env = new ProcessBuilder().environment();
1293             THROWS(ClassCastException.class,
1294                 new Fun(){void f(){ env.remove(TRUE);}},
1295                 new Fun(){void f(){ env.keySet().remove(TRUE);}},
1296                 new Fun(){void f(){ env.values().remove(TRUE);}},
1297                 new Fun(){void f(){ env.entrySet().remove(TRUE);}});
1298         } catch (Throwable t) { unexpected(t); }
1299 
1300         //----------------------------------------------------------------
1301         // Check query operations on environment maps
1302         //----------------------------------------------------------------
1303         try {
1304             List<Map<String,String>> envs =
1305                 new ArrayList<Map<String,String>>(2);
1306             envs.add(System.getenv());
1307             envs.add(new ProcessBuilder().environment());
1308             for (final Map<String,String> env : envs) {
1309                 //----------------------------------------------------------------
1310                 // Nulls in environment queries are forbidden.
1311                 //----------------------------------------------------------------
1312                 THROWS(NullPointerException.class,
1313                     new Fun(){void f(){ getenv(null);}},
1314                     new Fun(){void f(){ env.get(null);}},
1315                     new Fun(){void f(){ env.containsKey(null);}},
1316                     new Fun(){void f(){ env.containsValue(null);}},
1317                     new Fun(){void f(){ env.keySet().contains(null);}},
1318                     new Fun(){void f(){ env.values().contains(null);}});
1319 
1320                 //----------------------------------------------------------------
1321                 // Non-String types in environment queries are forbidden.
1322                 //----------------------------------------------------------------
1323                 THROWS(ClassCastException.class,
1324                     new Fun(){void f(){ env.get(TRUE);}},
1325                     new Fun(){void f(){ env.containsKey(TRUE);}},
1326                     new Fun(){void f(){ env.containsValue(TRUE);}},
1327                     new Fun(){void f(){ env.keySet().contains(TRUE);}},
1328                     new Fun(){void f(){ env.values().contains(TRUE);}});
1329 
1330                 //----------------------------------------------------------------
1331                 // Illegal String values in environment queries are (grumble) OK
1332                 //----------------------------------------------------------------
1333                 equal(env.get("\u0000"), null);
1334                 check(! env.containsKey("\u0000"));
1335                 check(! env.containsValue("\u0000"));
1336                 check(! env.keySet().contains("\u0000"));
1337                 check(! env.values().contains("\u0000"));
1338             }
1339 
1340         } catch (Throwable t) { unexpected(t); }
1341 
1342         try {
1343             final Set<Map.Entry<String,String>> entrySet =
1344                 new ProcessBuilder().environment().entrySet();
1345             THROWS(NullPointerException.class,
1346                    new Fun(){void f(){ entrySet.contains(null);}});
1347             THROWS(ClassCastException.class,
1348                 new Fun(){void f(){ entrySet.contains(TRUE);}},
1349                 new Fun(){void f(){
1350                     entrySet.contains(
1351                         new SimpleImmutableEntry<Boolean,String>(TRUE,""));}});
1352 
1353             check(! entrySet.contains
1354                   (new SimpleImmutableEntry<String,String>("", "")));
1355         } catch (Throwable t) { unexpected(t); }
1356 
1357         //----------------------------------------------------------------
1358         // Put in a directory; get the same one back out.
1359         //----------------------------------------------------------------
1360         try {
1361             ProcessBuilder pb = new ProcessBuilder();
1362             File foo = new File("foo");
1363             equal(pb.directory(), null);
1364             equal(pb.directory(foo).directory(), foo);
1365             equal(pb.directory(null).directory(), null);
1366         } catch (Throwable t) { unexpected(t); }
1367 
1368         //----------------------------------------------------------------
1369         // If round-trip conversion works, check envvar pass-through to child
1370         //----------------------------------------------------------------
1371         try {
1372             testEncoding("ASCII",   "xyzzy");
1373             testEncoding("Latin1",  "\u00f1\u00e1");
1374             testEncoding("Unicode", "\u22f1\u11e1");
1375         } catch (Throwable t) { unexpected(t); }
1376 
1377         //----------------------------------------------------------------
1378         // A surprisingly large number of ways to delete an environment var.
1379         //----------------------------------------------------------------
1380         testVariableDeleter(new EnvironmentFrobber() {
1381                 public void doIt(Map<String,String> environ) {
1382                     environ.remove("Foo");}});
1383 
1384         testVariableDeleter(new EnvironmentFrobber() {
1385                 public void doIt(Map<String,String> environ) {
1386                     environ.keySet().remove("Foo");}});
1387 
1388         testVariableDeleter(new EnvironmentFrobber() {
1389                 public void doIt(Map<String,String> environ) {
1390                     environ.values().remove("BAAR");}});
1391 
1392         testVariableDeleter(new EnvironmentFrobber() {
1393                 public void doIt(Map<String,String> environ) {
1394                     // Legally fabricate a ProcessEnvironment.StringEntry,
1395                     // even though it's private.
1396                     Map<String,String> environ2
1397                         = new ProcessBuilder().environment();
1398                     environ2.clear();
1399                     environ2.put("Foo","BAAR");
1400                     // Subtlety alert.
1401                     Map.Entry<String,String> e
1402                         = environ2.entrySet().iterator().next();
1403                     environ.entrySet().remove(e);}});
1404 
1405         testVariableDeleter(new EnvironmentFrobber() {
1406                 public void doIt(Map<String,String> environ) {
1407                     Map.Entry<String,String> victim = null;
1408                     for (Map.Entry<String,String> e : environ.entrySet())
1409                         if (e.getKey().equals("Foo"))
1410                             victim = e;
1411                     if (victim != null)
1412                         environ.entrySet().remove(victim);}});
1413 
1414         testVariableDeleter(new EnvironmentFrobber() {
1415                 public void doIt(Map<String,String> environ) {
1416                     Iterator<String> it = environ.keySet().iterator();
1417                     while (it.hasNext()) {
1418                         String val = it.next();
1419                         if (val.equals("Foo"))
1420                             it.remove();}}});
1421 
1422         testVariableDeleter(new EnvironmentFrobber() {
1423                 public void doIt(Map<String,String> environ) {
1424                     Iterator<Map.Entry<String,String>> it
1425                         = environ.entrySet().iterator();
1426                     while (it.hasNext()) {
1427                         Map.Entry<String,String> e = it.next();
1428                         if (e.getKey().equals("Foo"))
1429                             it.remove();}}});
1430 
1431         testVariableDeleter(new EnvironmentFrobber() {
1432                 public void doIt(Map<String,String> environ) {
1433                     Iterator<String> it = environ.values().iterator();
1434                     while (it.hasNext()) {
1435                         String val = it.next();
1436                         if (val.equals("BAAR"))
1437                             it.remove();}}});
1438 
1439         //----------------------------------------------------------------
1440         // A surprisingly small number of ways to add an environment var.
1441         //----------------------------------------------------------------
1442         testVariableAdder(new EnvironmentFrobber() {
1443                 public void doIt(Map<String,String> environ) {
1444                     environ.put("Foo","Bahrein");}});
1445 
1446         //----------------------------------------------------------------
1447         // A few ways to modify an environment var.
1448         //----------------------------------------------------------------
1449         testVariableModifier(new EnvironmentFrobber() {
1450                 public void doIt(Map<String,String> environ) {
1451                     environ.put("Foo","NewValue");}});
1452 
1453         testVariableModifier(new EnvironmentFrobber() {
1454                 public void doIt(Map<String,String> environ) {
1455                     for (Map.Entry<String,String> e : environ.entrySet())
1456                         if (e.getKey().equals("Foo"))
1457                             e.setValue("NewValue");}});
1458 
1459         //----------------------------------------------------------------
1460         // Fiddle with environment sizes
1461         //----------------------------------------------------------------
1462         try {
1463             Map<String,String> environ = new ProcessBuilder().environment();
1464             int size = environ.size();
1465             checkSizes(environ, size);
1466 
1467             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1468             checkSizes(environ, size+1);
1469 
1470             // Check for environment independence
1471             new ProcessBuilder().environment().clear();
1472 
1473             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1474             checkSizes(environ, size+1);
1475 
1476             environ.remove("UnLiKeLYeNVIROmtNam");
1477             checkSizes(environ, size);
1478 
1479             environ.clear();
1480             checkSizes(environ, 0);
1481 
1482             environ.clear();
1483             checkSizes(environ, 0);
1484 
1485             environ = new ProcessBuilder().environment();
1486             environ.keySet().clear();
1487             checkSizes(environ, 0);
1488 
1489             environ = new ProcessBuilder().environment();
1490             environ.entrySet().clear();
1491             checkSizes(environ, 0);
1492 
1493             environ = new ProcessBuilder().environment();
1494             environ.values().clear();
1495             checkSizes(environ, 0);
1496         } catch (Throwable t) { unexpected(t); }
1497 
1498         //----------------------------------------------------------------
1499         // Check that various map invariants hold
1500         //----------------------------------------------------------------
1501         checkMapSanity(new ProcessBuilder().environment());
1502         checkMapSanity(System.getenv());
1503         checkMapEquality(new ProcessBuilder().environment(),
1504                          new ProcessBuilder().environment());
1505 
1506 
1507         //----------------------------------------------------------------
1508         // Check effects on external "env" command.
1509         //----------------------------------------------------------------
1510         try {
1511             Set<String> env1 = new HashSet<String>
1512                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1513 
1514             ProcessBuilder pb = new ProcessBuilder();
1515             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1516 
1517             Set<String> env2 = new HashSet<String>
1518                 (Arrays.asList(nativeEnv(pb).split("\n")));
1519 
1520             check(env2.size() == env1.size() + 1);
1521             env1.add("QwErTyUiOp=AsDfGhJk");
1522             check(env1.equals(env2));
1523         } catch (Throwable t) { unexpected(t); }
1524 
1525         //----------------------------------------------------------------
1526         // Test Runtime.exec(...envp...)
1527         // Check for sort order of environment variables on Windows.
1528         //----------------------------------------------------------------
1529         try {
1530             String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1531             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1532             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1533                             "+=+", "_=_", "~=~", systemRoot};
1534             String output = nativeEnv(envp);
1535             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1536             // On Windows, Java must keep the environment sorted.
1537             // Order is random on Unix, so this test does the sort.
1538             if (! Windows.is())
1539                 output = sortByLinesWindowsly(output);
1540             equal(output, expected);
1541         } catch (Throwable t) { unexpected(t); }
1542 
1543         //----------------------------------------------------------------
1544         // Test Runtime.exec(...envp...)
1545         // and check SystemRoot gets set automatically on Windows
1546         //----------------------------------------------------------------
1547         try {
1548             if (Windows.is()) {
1549                 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1550                 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1551                                 "+=+", "_=_", "~=~"};
1552                 String output = nativeEnv(envp);
1553                 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1554                 equal(output, expected);
1555             }
1556         } catch (Throwable t) { unexpected(t); }
1557 
1558         //----------------------------------------------------------------
1559         // System.getenv() must be consistent with System.getenv(String)
1560         //----------------------------------------------------------------
1561         try {
1562             for (Map.Entry<String,String> e : getenv().entrySet())
1563                 equal(getenv(e.getKey()), e.getValue());
1564         } catch (Throwable t) { unexpected(t); }
1565 
1566         //----------------------------------------------------------------
1567         // Fiddle with working directory in child
1568         //----------------------------------------------------------------
1569         try {
1570             String canonicalUserDir =
1571                 new File(System.getProperty("user.dir")).getCanonicalPath();
1572             String[] sdirs = new String[]
1573                 {".", "..", "/", "/bin",
1574                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1575                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1576             for (String sdir : sdirs) {
1577                 File dir = new File(sdir);
1578                 if (! (dir.isDirectory() && dir.exists()))
1579                     continue;
1580                 out.println("Testing directory " + dir);
1581                 //dir = new File(dir.getCanonicalPath());
1582 
1583                 ProcessBuilder pb = new ProcessBuilder();
1584                 equal(pb.directory(), null);
1585                 equal(pwdInChild(pb), canonicalUserDir);
1586 
1587                 pb.directory(dir);
1588                 equal(pb.directory(), dir);
1589                 equal(pwdInChild(pb), dir.getCanonicalPath());
1590 
1591                 pb.directory(null);
1592                 equal(pb.directory(), null);
1593                 equal(pwdInChild(pb), canonicalUserDir);
1594 
1595                 pb.directory(dir);
1596             }
1597         } catch (Throwable t) { unexpected(t); }
1598 
1599         //----------------------------------------------------------------
1600         // Working directory with Unicode in child
1601         //----------------------------------------------------------------
1602         try {
1603             if (UnicodeOS.is()) {
1604                 File dir = new File(System.getProperty("test.dir", "."),
1605                                     "ProcessBuilderDir\u4e00\u4e02");
1606                 try {
1607                     if (!dir.exists())
1608                         dir.mkdir();
1609                     out.println("Testing Unicode directory:" + dir);
1610                     ProcessBuilder pb = new ProcessBuilder();
1611                     pb.directory(dir);
1612                     equal(pwdInChild(pb), dir.getCanonicalPath());
1613                 } finally {
1614                     if (dir.exists())
1615                         dir.delete();
1616                 }
1617             }
1618         } catch (Throwable t) { unexpected(t); }
1619 
1620         //----------------------------------------------------------------
1621         // OOME in child allocating maximally sized array
1622         // Test for hotspot/jvmti bug 6850957
1623         //----------------------------------------------------------------
1624         try {
1625             List<String> list = new ArrayList<String>(javaChildArgs);
1626             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1627                                       javaExe));
1628             list.add("ArrayOOME");
1629             ProcessResults r = run(new ProcessBuilder(list));
1630             check(r.out().contains("java.lang.OutOfMemoryError:"));
1631             check(r.out().contains(javaExe));
1632             check(r.err().contains(System.getProperty("java.version")));
1633             equal(r.exitValue(), 1);
1634         } catch (Throwable t) { unexpected(t); }
1635 
1636         //----------------------------------------------------------------
1637         // Windows has tricky semi-case-insensitive semantics
1638         //----------------------------------------------------------------
1639         if (Windows.is())
1640             try {
1641                 out.println("Running case insensitve variable tests");
1642                 for (String[] namePair :
1643                          new String[][]
1644                     { new String[]{"PATH","PaTh"},
1645                       new String[]{"home","HOME"},
1646                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1647                     check((getenv(namePair[0]) == null &&
1648                            getenv(namePair[1]) == null)
1649                           ||
1650                           getenv(namePair[0]).equals(getenv(namePair[1])),
1651                           "Windows environment variables are not case insensitive");
1652                 }
1653             } catch (Throwable t) { unexpected(t); }
1654 
1655         //----------------------------------------------------------------
1656         // Test proper Unicode child environment transfer
1657         //----------------------------------------------------------------
1658         if (UnicodeOS.is())
1659             try {
1660                 ProcessBuilder pb = new ProcessBuilder();
1661                 pb.environment().put("\u1234","\u5678");
1662                 pb.environment().remove("PATH");
1663                 equal(getenvInChild1234(pb), "\u5678");
1664             } catch (Throwable t) { unexpected(t); }
1665 
1666 
1667         //----------------------------------------------------------------
1668         // Test Runtime.exec(...envp...) with envstrings with initial `='
1669         //----------------------------------------------------------------
1670         try {
1671             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1672             childArgs.add("System.getenv()");
1673             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1674             String[] envp;
1675             String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};
1676             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1677             if (Windows.is()) {
1678                 envp = envpWin;
1679             } else {
1680                 envp = envpOth;
1681             }
1682             Process p = Runtime.getRuntime().exec(cmdp, envp);
1683             String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";
1684             String commandOutput = commandOutput(p);
1685             if (MacOSX.is()) {
1686                 commandOutput = removeMacExpectedVars(commandOutput);
1687             }
1688             equal(commandOutput, expected);
1689             if (Windows.is()) {
1690                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1691                 pb.environment().clear();
1692                 pb.environment().put("SystemRoot", systemRoot);
1693                 pb.environment().put("=ExitValue", "3");
1694                 pb.environment().put("=C:", "\\");
1695                 equal(commandOutput(pb), expected);
1696             }
1697         } catch (Throwable t) { unexpected(t); }
1698 
1699         //----------------------------------------------------------------
1700         // Test Runtime.exec(...envp...) with envstrings without any `='
1701         //----------------------------------------------------------------
1702         try {
1703             String[] cmdp = {"echo"};
1704             String[] envp = {"Hello", "World"}; // Yuck!
1705             Process p = Runtime.getRuntime().exec(cmdp, envp);
1706             equal(commandOutput(p), "\n");
1707         } catch (Throwable t) { unexpected(t); }
1708 
1709         //----------------------------------------------------------------
1710         // Test Runtime.exec(...envp...) with envstrings containing NULs
1711         //----------------------------------------------------------------
1712         try {
1713             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1714             childArgs.add("System.getenv()");
1715             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1716             String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1717                              "FO\u0000=B\u0000R"};
1718             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1719                              "FO\u0000=B\u0000R"};
1720             String[] envp;
1721             if (Windows.is()) {
1722                 envp = envpWin;
1723             } else {
1724                 envp = envpOth;
1725             }
1726             System.out.println ("cmdp");
1727             for (int i=0; i<cmdp.length; i++) {
1728                 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);
1729             }
1730             System.out.println ("envp");
1731             for (int i=0; i<envp.length; i++) {
1732                 System.out.printf ("envp %d: %s\n", i, envp[i]);
1733             }
1734             Process p = Runtime.getRuntime().exec(cmdp, envp);
1735             String commandOutput = commandOutput(p);
1736             if (MacOSX.is()) {
1737                 commandOutput = removeMacExpectedVars(commandOutput);
1738             }
1739             check(commandOutput.equals(Windows.is()
1740                     ? "LC_ALL=C,SystemRoot="+systemRoot+","
1741                     : "LC_ALL=C,"),
1742                   "Incorrect handling of envstrings containing NULs");
1743         } catch (Throwable t) { unexpected(t); }
1744 
1745         //----------------------------------------------------------------
1746         // Test the redirectErrorStream property
1747         //----------------------------------------------------------------
1748         try {
1749             ProcessBuilder pb = new ProcessBuilder();
1750             equal(pb.redirectErrorStream(), false);
1751             equal(pb.redirectErrorStream(true), pb);
1752             equal(pb.redirectErrorStream(), true);
1753             equal(pb.redirectErrorStream(false), pb);
1754             equal(pb.redirectErrorStream(), false);
1755         } catch (Throwable t) { unexpected(t); }
1756 
1757         try {
1758             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1759             childArgs.add("OutErr");
1760             ProcessBuilder pb = new ProcessBuilder(childArgs);
1761             {
1762                 ProcessResults r = run(pb);
1763                 equal(r.out(), "outout");
1764                 equal(r.err(), "errerr");
1765             }
1766             {
1767                 pb.redirectErrorStream(true);
1768                 ProcessResults r = run(pb);
1769                 equal(r.out(), "outerrouterr");
1770                 equal(r.err(), "");
1771             }
1772         } catch (Throwable t) { unexpected(t); }
1773 
1774         if (Unix.is()) {
1775             //----------------------------------------------------------------
1776             // We can find true and false when PATH is null
1777             //----------------------------------------------------------------
1778             try {
1779                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1780                 childArgs.add("null PATH");
1781                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1782                 pb.environment().remove("PATH");
1783                 ProcessResults r = run(pb);
1784                 equal(r.out(), "");
1785                 equal(r.err(), "");
1786                 equal(r.exitValue(), 0);
1787             } catch (Throwable t) { unexpected(t); }
1788 
1789             //----------------------------------------------------------------
1790             // PATH search algorithm on Unix
1791             //----------------------------------------------------------------
1792             try {
1793                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1794                 childArgs.add("PATH search algorithm");
1795                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1796                 pb.environment().put("PATH", "dir1:dir2:");
1797                 ProcessResults r = run(pb);
1798                 equal(r.out(), "");
1799                 equal(r.err(), "");
1800                 equal(r.exitValue(), True.exitValue());
1801             } catch (Throwable t) { unexpected(t); }
1802 
1803             //----------------------------------------------------------------
1804             // Parent's, not child's PATH is used
1805             //----------------------------------------------------------------
1806             try {
1807                 new File("suBdiR").mkdirs();
1808                 copy("/bin/true", "suBdiR/unliKely");
1809                 final ProcessBuilder pb =
1810                     new ProcessBuilder(new String[]{"unliKely"});
1811                 pb.environment().put("PATH", "suBdiR");
1812                 THROWS(IOException.class,
1813                        new Fun() {void f() throws Throwable {pb.start();}});
1814             } catch (Throwable t) { unexpected(t);
1815             } finally {
1816                 new File("suBdiR/unliKely").delete();
1817                 new File("suBdiR").delete();
1818             }
1819         }
1820 
1821         //----------------------------------------------------------------
1822         // Attempt to start bogus program ""
1823         //----------------------------------------------------------------
1824         try {
1825             new ProcessBuilder("").start();
1826             fail("Expected IOException not thrown");
1827         } catch (IOException e) {
1828             String m = e.getMessage();
1829             if (EnglishUnix.is() &&
1830                 ! matches(m, "No such file or directory"))
1831                 unexpected(e);
1832         } catch (Throwable t) { unexpected(t); }
1833 
1834         //----------------------------------------------------------------
1835         // Check that attempt to execute program name with funny
1836         // characters throws an exception containing those characters.
1837         //----------------------------------------------------------------
1838         for (String programName : new String[] {"\u00f0", "\u01f0"})
1839             try {
1840                 new ProcessBuilder(programName).start();
1841                 fail("Expected IOException not thrown");
1842             } catch (IOException e) {
1843                 String m = e.getMessage();
1844                 Pattern p = Pattern.compile(programName);
1845                 if (! matches(m, programName)
1846                     || (EnglishUnix.is()
1847                         && ! matches(m, "No such file or directory")))
1848                     unexpected(e);
1849             } catch (Throwable t) { unexpected(t); }
1850 
1851         //----------------------------------------------------------------
1852         // Attempt to start process in nonexistent directory fails.
1853         //----------------------------------------------------------------
1854         try {
1855             new ProcessBuilder("echo")
1856                 .directory(new File("UnLiKeLY"))
1857                 .start();
1858             fail("Expected IOException not thrown");
1859         } catch (IOException e) {
1860             String m = e.getMessage();
1861             if (! matches(m, "in directory")
1862                 || (EnglishUnix.is() &&
1863                     ! matches(m, "No such file or directory")))
1864                 unexpected(e);
1865         } catch (Throwable t) { unexpected(t); }
1866 
1867         //----------------------------------------------------------------
1868         // Attempt to write 4095 bytes to the pipe buffer without a
1869         // reader to drain it would deadlock, if not for the fact that
1870         // interprocess pipe buffers are at least 4096 bytes.
1871         //
1872         // Also, check that available reports all the bytes expected
1873         // in the pipe buffer, and that I/O operations do the expected
1874         // things.
1875         //----------------------------------------------------------------
1876         try {
1877             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1878             childArgs.add("print4095");
1879             final int SIZE = 4095;
1880             final Process p = new ProcessBuilder(childArgs).start();
1881             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
1882             p.waitFor();                                // Might hang!
1883             equal(SIZE, p.getInputStream().available());
1884             equal(SIZE, p.getErrorStream().available());
1885             THROWS(IOException.class,
1886                    new Fun(){void f() throws IOException {
1887                        p.getOutputStream().write((byte) '!');
1888                        p.getOutputStream().flush();
1889                        }});
1890 
1891             final byte[] bytes = new byte[SIZE + 1];
1892             equal(SIZE, p.getInputStream().read(bytes));
1893             for (int i = 0; i < SIZE; i++)
1894                 equal((byte) '!', bytes[i]);
1895             equal((byte) 0, bytes[SIZE]);
1896 
1897             equal(SIZE, p.getErrorStream().read(bytes));
1898             for (int i = 0; i < SIZE; i++)
1899                 equal((byte) 'E', bytes[i]);
1900             equal((byte) 0, bytes[SIZE]);
1901 
1902             equal(0, p.getInputStream().available());
1903             equal(0, p.getErrorStream().available());
1904             equal(-1, p.getErrorStream().read());
1905             equal(-1, p.getInputStream().read());
1906 
1907             equal(p.exitValue(), 5);
1908 
1909             p.getInputStream().close();
1910             p.getErrorStream().close();
1911             try { p.getOutputStream().close(); } catch (IOException flushFailed) { }
1912 
1913             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
1914             for (final InputStream in : streams) {
1915                 Fun[] ops = {
1916                     new Fun(){void f() throws IOException {
1917                         in.read(); }},
1918                     new Fun(){void f() throws IOException {
1919                         in.read(bytes); }},
1920                     new Fun(){void f() throws IOException {
1921                         in.available(); }}
1922                 };
1923                 for (Fun op : ops) {
1924                     try {
1925                         op.f();
1926                         fail();
1927                     } catch (IOException expected) {
1928                         check(expected.getMessage()
1929                               .matches("[Ss]tream [Cc]losed"));
1930                     }
1931                 }
1932             }
1933         } catch (Throwable t) { unexpected(t); }
1934 
1935         //----------------------------------------------------------------
1936         // Check that reads which are pending when Process.destroy is
1937         // called, get EOF, not IOException("Stream closed").
1938         //----------------------------------------------------------------
1939         try {
1940             final int cases = 4;
1941             for (int i = 0; i < cases; i++) {
1942                 final int action = i;
1943                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1944                 childArgs.add("sleep");
1945                 final byte[] bytes = new byte[10];
1946                 final Process p = new ProcessBuilder(childArgs).start();
1947                 final CountDownLatch latch = new CountDownLatch(1);
1948                 final InputStream s;
1949                 switch (action & 0x1) {
1950                     case 0: s = p.getInputStream(); break;
1951                     case 1: s = p.getErrorStream(); break;
1952                     default: throw new Error();
1953                 }
1954                 final Thread thread = new Thread() {
1955                     public void run() {
1956                         try {
1957                             int r;
1958                             latch.countDown();
1959                             switch (action & 0x2) {
1960                                 case 0: r = s.read(); break;
1961                                 case 2: r = s.read(bytes); break;
1962                                 default: throw new Error();
1963                             }
1964                             equal(-1, r);
1965                         } catch (Throwable t) { unexpected(t); }}};
1966 
1967                 thread.start();
1968                 latch.await();
1969                 Thread.sleep(10);
1970 
1971                 String os = System.getProperty("os.name");
1972                 if (os.equalsIgnoreCase("Solaris") ||
1973                     os.equalsIgnoreCase("SunOS"))
1974                 {
1975                     final Object deferred;
1976                     Class<?> c = s.getClass();
1977                     if (c.getName().equals(
1978                         "java.lang.UNIXProcess$DeferredCloseInputStream"))
1979                     {
1980                         deferred = s;
1981                     } else {
1982                         Field deferredField = p.getClass().
1983                             getDeclaredField("stdout_inner_stream");
1984                         deferredField.setAccessible(true);
1985                         deferred = deferredField.get(p);
1986                     }
1987                     Field useCountField = deferred.getClass().
1988                         getDeclaredField("useCount");
1989                     useCountField.setAccessible(true);
1990 
1991                     while (useCountField.getInt(deferred) <= 0) {
1992                         Thread.yield();
1993                     }
1994                 } else if (s instanceof BufferedInputStream) {
1995                     Field f = Unsafe.class.getDeclaredField("theUnsafe");
1996                     f.setAccessible(true);
1997                     Unsafe unsafe = (Unsafe)f.get(null);
1998 
1999                     while (unsafe.tryMonitorEnter(s)) {
2000                         unsafe.monitorExit(s);
2001                         Thread.sleep(1);
2002                     }
2003                 }
2004                 p.destroy();
2005                 thread.join();
2006             }
2007         } catch (Throwable t) { unexpected(t); }
2008 
2009         //----------------------------------------------------------------
2010         // Check that subprocesses which create subprocesses of their
2011         // own do not cause parent to hang waiting for file
2012         // descriptors to be closed.
2013         //----------------------------------------------------------------
2014         try {
2015             if (Unix.is()
2016                 && new File("/bin/bash").exists()
2017                 && new File("/bin/sleep").exists()) {
2018                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 6666)" };
2019                 final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 6666\")" };
2020                 final ProcessBuilder pb = new ProcessBuilder(cmd);
2021                 final Process p = pb.start();
2022                 final InputStream stdout = p.getInputStream();
2023                 final InputStream stderr = p.getErrorStream();
2024                 final OutputStream stdin = p.getOutputStream();
2025                 final Thread reader = new Thread() {
2026                     public void run() {
2027                         try { stdout.read(); }
2028                         catch (IOException e) {
2029                             // Check that reader failed because stream was
2030                             // asynchronously closed.
2031                             // e.printStackTrace();
2032                             if (EnglishUnix.is() &&
2033                                 ! (e.getMessage().matches(".*Bad file.*")))
2034                                 unexpected(e);
2035                         }
2036                         catch (Throwable t) { unexpected(t); }}};
2037                 reader.setDaemon(true);
2038                 reader.start();
2039                 Thread.sleep(100);
2040                 p.destroy();
2041                 // Subprocess is now dead, but file descriptors remain open.
2042                 check(p.waitFor() != 0);
2043                 check(p.exitValue() != 0);
2044                 stdout.close();
2045                 stderr.close();
2046                 stdin.close();
2047                 new ProcessBuilder(cmdkill).start();
2048                 //----------------------------------------------------------
2049                 // There remain unsolved issues with asynchronous close.
2050                 // Here's a highly non-portable experiment to demonstrate:
2051                 //----------------------------------------------------------
2052                 if (Boolean.getBoolean("wakeupJeff!")) {
2053                     System.out.println("wakeupJeff!");
2054                     // Initialize signal handler for INTERRUPT_SIGNAL.
2055                     new FileInputStream("/bin/sleep").getChannel().close();
2056                     // Send INTERRUPT_SIGNAL to every thread in this java.
2057                     String[] wakeupJeff = {
2058                         "/bin/bash", "-c",
2059                         "/bin/ps --noheaders -Lfp $PPID | " +
2060                         "/usr/bin/perl -nale 'print $F[3]' | " +
2061                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
2062                         "/usr/bin/xargs kill -62"
2063                     };
2064                     new ProcessBuilder(wakeupJeff).start().waitFor();
2065                     // If wakeupJeff worked, reader probably got EBADF.
2066                     reader.join();
2067                 }
2068             }
2069         } catch (Throwable t) { unexpected(t); }
2070 
2071         //----------------------------------------------------------------
2072         // Attempt to start process with insufficient permissions fails.
2073         //----------------------------------------------------------------
2074         try {
2075             new File("emptyCommand").delete();
2076             new FileOutputStream("emptyCommand").close();
2077             new File("emptyCommand").setExecutable(false);
2078             new ProcessBuilder("./emptyCommand").start();
2079             fail("Expected IOException not thrown");
2080         } catch (IOException e) {
2081             new File("./emptyCommand").delete();
2082             String m = e.getMessage();
2083             if (EnglishUnix.is() &&
2084                 ! matches(m, "Permission denied"))
2085                 unexpected(e);
2086         } catch (Throwable t) { unexpected(t); }
2087 
2088         new File("emptyCommand").delete();
2089 
2090         //----------------------------------------------------------------
2091         // Check for correct security permission behavior
2092         //----------------------------------------------------------------
2093         final Policy policy = new Policy();
2094         Policy.setPolicy(policy);
2095         System.setSecurityManager(new SecurityManager());
2096 
2097         try {
2098             // No permissions required to CREATE a ProcessBuilder
2099             policy.setPermissions(/* Nothing */);
2100             new ProcessBuilder("env").directory(null).directory();
2101             new ProcessBuilder("env").directory(new File("dir")).directory();
2102             new ProcessBuilder("env").command("??").command();
2103         } catch (Throwable t) { unexpected(t); }
2104 
2105         THROWS(SecurityException.class,
2106             new Fun() { void f() throws IOException {
2107                 policy.setPermissions(/* Nothing */);
2108                 System.getenv("foo");}},
2109             new Fun() { void f() throws IOException {
2110                 policy.setPermissions(/* Nothing */);
2111                 System.getenv();}},
2112             new Fun() { void f() throws IOException {
2113                 policy.setPermissions(/* Nothing */);
2114                 new ProcessBuilder("echo").start();}},
2115             new Fun() { void f() throws IOException {
2116                 policy.setPermissions(/* Nothing */);
2117                 Runtime.getRuntime().exec("echo");}},
2118             new Fun() { void f() throws IOException {
2119                 policy.setPermissions(new RuntimePermission("getenv.bar"));
2120                 System.getenv("foo");}});
2121 
2122         try {
2123             policy.setPermissions(new RuntimePermission("getenv.foo"));
2124             System.getenv("foo");
2125 
2126             policy.setPermissions(new RuntimePermission("getenv.*"));
2127             System.getenv("foo");
2128             System.getenv();
2129             new ProcessBuilder().environment();
2130         } catch (Throwable t) { unexpected(t); }
2131 
2132 
2133         final Permission execPermission
2134             = new FilePermission("<<ALL FILES>>", "execute");
2135 
2136         THROWS(SecurityException.class,
2137             new Fun() { void f() throws IOException {
2138                 // environment permission by itself insufficient
2139                 policy.setPermissions(new RuntimePermission("getenv.*"));
2140                 ProcessBuilder pb = new ProcessBuilder("env");
2141                 pb.environment().put("foo","bar");
2142                 pb.start();}},
2143             new Fun() { void f() throws IOException {
2144                  // exec permission by itself insufficient
2145                  policy.setPermissions(execPermission);
2146                  ProcessBuilder pb = new ProcessBuilder("env");
2147                  pb.environment().put("foo","bar");
2148                  pb.start();}});
2149 
2150         try {
2151             // Both permissions? OK.
2152             policy.setPermissions(new RuntimePermission("getenv.*"),
2153                                   execPermission);
2154             ProcessBuilder pb = new ProcessBuilder("env");
2155             pb.environment().put("foo","bar");
2156             Process p = pb.start();
2157             closeStreams(p);
2158         } catch (IOException e) { // OK
2159         } catch (Throwable t) { unexpected(t); }
2160 
2161         try {
2162             // Don't need environment permission unless READING environment
2163             policy.setPermissions(execPermission);
2164             Runtime.getRuntime().exec("env", new String[]{});
2165         } catch (IOException e) { // OK
2166         } catch (Throwable t) { unexpected(t); }
2167 
2168         try {
2169             // Don't need environment permission unless READING environment
2170             policy.setPermissions(execPermission);
2171             new ProcessBuilder("env").start();
2172         } catch (IOException e) { // OK
2173         } catch (Throwable t) { unexpected(t); }
2174 
2175         // Restore "normal" state without a security manager
2176         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2177         System.setSecurityManager(null);
2178 
2179         //----------------------------------------------------------------
2180         // Check that Process.isAlive() &
2181         // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
2182         //----------------------------------------------------------------
2183         try {
2184             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2185             childArgs.add("sleep");
2186             final Process p = new ProcessBuilder(childArgs).start();
2187             long start = System.nanoTime();
2188             if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
2189                 fail("Test failed: Process exited prematurely");
2190             }
2191             long end = System.nanoTime();
2192             // give waitFor(timeout) a wide berth (100ms)
2193             if ((end - start) > 100000000)
2194                 fail("Test failed: waitFor took too long");
2195 
2196             p.destroy();
2197             p.waitFor();
2198 
2199             if (p.isAlive() ||
2200                 !p.waitFor(0, TimeUnit.MILLISECONDS))
2201             {
2202                 fail("Test failed: Process still alive - please terminate " +
2203                     p.toString() + " manually");
2204             }
2205         } catch (Throwable t) { unexpected(t); }
2206 
2207         //----------------------------------------------------------------
2208         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2209         // works as expected.
2210         //----------------------------------------------------------------
2211         try {
2212             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2213             childArgs.add("sleep");
2214             final Process p = new ProcessBuilder(childArgs).start();
2215             long start = System.nanoTime();
2216 
2217             p.waitFor(1000, TimeUnit.MILLISECONDS);
2218 
2219             long end = System.nanoTime();
2220             if ((end - start) < 500000000)
2221                 fail("Test failed: waitFor didn't take long enough");
2222 
2223             p.destroy();
2224 
2225             start = System.nanoTime();
2226             p.waitFor(1000, TimeUnit.MILLISECONDS);
2227             end = System.nanoTime();
2228             if ((end - start) > 900000000)
2229                 fail("Test failed: waitFor took too long on a dead process.");
2230         } catch (Throwable t) { unexpected(t); }
2231 
2232         //----------------------------------------------------------------
2233         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2234         // interrupt works as expected.
2235         //----------------------------------------------------------------
2236         try {
2237             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2238             childArgs.add("sleep");
2239             final Process p = new ProcessBuilder(childArgs).start();
2240             final long start = System.nanoTime();
2241             final CountDownLatch latch = new CountDownLatch(1);
2242 
2243             final Thread thread = new Thread() {
2244                 public void run() {
2245                     try {
2246                         try {
2247                             latch.countDown();
2248                             p.waitFor(30000, TimeUnit.MILLISECONDS);
2249                         } catch (InterruptedException e) {
2250                             return;
2251                         }
2252                         fail("waitFor() wasn't interrupted");
2253                     } catch (Throwable t) { unexpected(t); }}};
2254 
2255             thread.start();
2256             latch.await();
2257             Thread.sleep(1000);
2258             thread.interrupt();
2259             p.destroy();
2260         } catch (Throwable t) { unexpected(t); }
2261 
2262         //----------------------------------------------------------------
2263         // Check the default implementation for
2264         // Process.waitFor(long, TimeUnit)
2265         //----------------------------------------------------------------
2266         try {
2267             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2268             childArgs.add("sleep");
2269             final Process proc = new ProcessBuilder(childArgs).start();
2270             DelegatingProcess p = new DelegatingProcess(proc);
2271             long start = System.nanoTime();
2272 
2273             p.waitFor(1000, TimeUnit.MILLISECONDS);
2274 
2275             long end = System.nanoTime();
2276             if ((end - start) < 500000000)
2277                 fail("Test failed: waitFor didn't take long enough");
2278 
2279             p.destroy();
2280 
2281             p.waitFor(1000, TimeUnit.MILLISECONDS);
2282         } catch (Throwable t) { unexpected(t); }
2283     }
2284 
2285     static void closeStreams(Process p) {
2286         try {
2287             p.getOutputStream().close();
2288             p.getInputStream().close();
2289             p.getErrorStream().close();
2290         } catch (Throwable t) { unexpected(t); }
2291     }
2292 
2293     //----------------------------------------------------------------
2294     // A Policy class designed to make permissions fiddling very easy.
2295     //----------------------------------------------------------------
2296     private static class Policy extends java.security.Policy {
2297         private Permissions perms;
2298 
2299         public void setPermissions(Permission...permissions) {
2300             perms = new Permissions();
2301             for (Permission permission : permissions)
2302                 perms.add(permission);
2303         }
2304 
2305         public Policy() { setPermissions(/* Nothing */); }
2306 
2307         public PermissionCollection getPermissions(CodeSource cs) {
2308             return perms;
2309         }
2310 
2311         public PermissionCollection getPermissions(ProtectionDomain pd) {
2312             return perms;
2313         }
2314 
2315         public boolean implies(ProtectionDomain pd, Permission p) {
2316             return perms.implies(p);
2317         }
2318 
2319         public void refresh() {}
2320     }
2321 
2322     private static class StreamAccumulator extends Thread {
2323         private final InputStream is;
2324         private final StringBuilder sb = new StringBuilder();
2325         private Throwable throwable = null;
2326 
2327         public String result () throws Throwable {
2328             if (throwable != null)
2329                 throw throwable;
2330             return sb.toString();
2331         }
2332 
2333         StreamAccumulator (InputStream is) {
2334             this.is = is;
2335         }
2336 
2337         public void run() {
2338             try {
2339                 Reader r = new InputStreamReader(is);
2340                 char[] buf = new char[4096];
2341                 int n;
2342                 while ((n = r.read(buf)) > 0) {
2343                     sb.append(buf,0,n);
2344                 }
2345             } catch (Throwable t) {
2346                 throwable = t;
2347             } finally {
2348                 try { is.close(); }
2349                 catch (Throwable t) { throwable = t; }
2350             }
2351         }
2352     }
2353 
2354     static ProcessResults run(ProcessBuilder pb) {
2355         try {
2356             return run(pb.start());
2357         } catch (Throwable t) { unexpected(t); return null; }
2358     }
2359 
2360     private static ProcessResults run(Process p) {
2361         Throwable throwable = null;
2362         int exitValue = -1;
2363         String out = "";
2364         String err = "";
2365 
2366         StreamAccumulator outAccumulator =
2367             new StreamAccumulator(p.getInputStream());
2368         StreamAccumulator errAccumulator =
2369             new StreamAccumulator(p.getErrorStream());
2370 
2371         try {
2372             outAccumulator.start();
2373             errAccumulator.start();
2374 
2375             exitValue = p.waitFor();
2376 
2377             outAccumulator.join();
2378             errAccumulator.join();
2379 
2380             out = outAccumulator.result();
2381             err = errAccumulator.result();
2382         } catch (Throwable t) {
2383             throwable = t;
2384         }
2385 
2386         return new ProcessResults(out, err, exitValue, throwable);
2387     }
2388 
2389     //----------------------------------------------------------------
2390     // Results of a command
2391     //----------------------------------------------------------------
2392     private static class ProcessResults {
2393         private final String out;
2394         private final String err;
2395         private final int exitValue;
2396         private final Throwable throwable;
2397 
2398         public ProcessResults(String out,
2399                               String err,
2400                               int exitValue,
2401                               Throwable throwable) {
2402             this.out = out;
2403             this.err = err;
2404             this.exitValue = exitValue;
2405             this.throwable = throwable;
2406         }
2407 
2408         public String out()          { return out; }
2409         public String err()          { return err; }
2410         public int exitValue()       { return exitValue; }
2411         public Throwable throwable() { return throwable; }
2412 
2413         public String toString() {
2414             StringBuilder sb = new StringBuilder();
2415             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2416                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2417                 .append("exitValue = " + exitValue + "\n");
2418             if (throwable != null)
2419                 sb.append(throwable.getStackTrace());
2420             return sb.toString();
2421         }
2422     }
2423 
2424     //--------------------- Infrastructure ---------------------------
2425     static volatile int passed = 0, failed = 0;
2426     static void pass() {passed++;}
2427     static void fail() {failed++; Thread.dumpStack();}
2428     static void fail(String msg) {System.out.println(msg); fail();}
2429     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2430     static void check(boolean cond) {if (cond) pass(); else fail();}
2431     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2432     static void equal(Object x, Object y) {
2433         if (x == null ? y == null : x.equals(y)) pass();
2434         else fail(x + " not equal to " + y);}
2435 
2436     public static void main(String[] args) throws Throwable {
2437         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2438         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2439         if (failed > 0) throw new AssertionError("Some tests failed");}
2440     private static abstract class Fun {abstract void f() throws Throwable;}
2441     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2442         for (Fun f : fs)
2443             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2444             catch (Throwable t) {
2445                 if (k.isAssignableFrom(t.getClass())) pass();
2446                 else unexpected(t);}}
2447 }