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