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