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