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