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