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