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