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