1 /*
   2  * Copyright (c) 2003, 2017, 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("/bin/true").exists());
 400                 check(new File("/bin/false").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("/bin/true", "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("/bin/true", "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("/bin/true", "./prog");
 464                             equal(run(pb).exitValue(),
 465                                   True.exitValue());
 466                             copy("/bin/false", "./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("/bin/true", "dir1/prog");
 522                         copy("/bin/false", "dir3/prog");
 523                         pb.environment().put("PATH","dir3");
 524                         equal(run(pb).exitValue(), True.exitValue());
 525                         copy("/bin/true", "dir3/prog");
 526                         copy("/bin/false", "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 UnicodeOS {
 624         public static boolean is() { return is; }
 625         private static final String osName = System.getProperty("os.name");
 626         private static final boolean is =
 627             // MacOS X would probably also qualify
 628             osName.startsWith("Windows")   &&
 629             ! osName.startsWith("Windows 9") &&
 630             ! osName.equals("Windows Me");
 631     }
 632 
 633     static class MacOSX {
 634         public static boolean is() { return is; }
 635         private static final String osName = System.getProperty("os.name");
 636         private static final boolean is = osName.contains("OS X");
 637     }
 638 
 639     static class True {
 640         public static int exitValue() { return 0; }
 641     }
 642 
 643     private static class False {
 644         public static int exitValue() { return exitValue; }
 645         private static final int exitValue = exitValue0();
 646         private static int exitValue0() {
 647             // /bin/false returns an *unspecified* non-zero number.
 648             try {
 649                 if (! Unix.is())
 650                     return -1;
 651                 else {
 652                     int rc = new ProcessBuilder("/bin/false")
 653                         .start().waitFor();
 654                     check(rc != 0);
 655                     return rc;
 656                 }
 657             } catch (Throwable t) { unexpected(t); return -1; }
 658         }
 659     }
 660 
 661     static class EnglishUnix {
 662         private static final Boolean is =
 663             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
 664 
 665         private static boolean isEnglish(String envvar) {
 666             String val = getenv(envvar);
 667             return (val == null) || val.matches("en.*") || val.matches("C");
 668         }
 669 
 670         /** Returns true if we can expect English OS error strings */
 671         static boolean is() { return is; }
 672     }
 673 
 674     static class DelegatingProcess extends Process {
 675         final Process p;
 676 
 677         DelegatingProcess(Process p) {
 678             this.p = p;
 679         }
 680 
 681         @Override
 682         public void destroy() {
 683             p.destroy();
 684         }
 685 
 686         @Override
 687         public int exitValue() {
 688             return p.exitValue();
 689         }
 690 
 691         @Override
 692         public int waitFor() throws InterruptedException {
 693             return p.waitFor();
 694         }
 695 
 696         @Override
 697         public OutputStream getOutputStream() {
 698             return p.getOutputStream();
 699         }
 700 
 701         @Override
 702         public InputStream getInputStream() {
 703             return p.getInputStream();
 704         }
 705 
 706         @Override
 707         public InputStream getErrorStream() {
 708             return p.getErrorStream();
 709         }
 710     }
 711 
 712     private static boolean matches(String str, String regex) {
 713         return Pattern.compile(regex).matcher(str).find();
 714     }
 715 
 716     private static String matchAndExtract(String str, String regex) {
 717         Matcher matcher = Pattern.compile(regex).matcher(str);
 718         if (matcher.find()) {
 719             return matcher.group();
 720         } else {
 721             return "";
 722         }
 723     }
 724 
 725     /* Only used for Mac OS X --
 726      * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
 727      * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
 728      * be set in Mac OS X.
 729      * Remove them both from the list of env variables
 730      */
 731     private static String removeMacExpectedVars(String vars) {
 732         // Check for __CF_USER_TEXT_ENCODING
 733         String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
 734                                             +cfUserTextEncoding+",","");
 735         // Check for JAVA_MAIN_CLASS_<pid>
 736         String javaMainClassStr
 737                 = matchAndExtract(cleanedVars,
 738                                     "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
 739         return cleanedVars.replace(javaMainClassStr,"");
 740     }
 741 
 742     /* Only used for AIX --
 743      * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.
 744      * Remove it from the list of env variables
 745      */
 746     private static String removeAixExpectedVars(String vars) {
 747         return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");
 748     }
 749 
 750     private static String sortByLinesWindowsly(String text) {
 751         String[] lines = text.split("\n");
 752         Arrays.sort(lines, new WindowsComparator());
 753         StringBuilder sb = new StringBuilder();
 754         for (String line : lines)
 755             sb.append(line).append("\n");
 756         return sb.toString();
 757     }
 758 
 759     private static void checkMapSanity(Map<String,String> map) {
 760         try {
 761             Set<String> keySet = map.keySet();
 762             Collection<String> values = map.values();
 763             Set<Map.Entry<String,String>> entrySet = map.entrySet();
 764 
 765             equal(entrySet.size(), keySet.size());
 766             equal(entrySet.size(), values.size());
 767 
 768             StringBuilder s1 = new StringBuilder();
 769             for (Map.Entry<String,String> e : entrySet)
 770                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
 771 
 772             StringBuilder s2 = new StringBuilder();
 773             for (String var : keySet)
 774                 s2.append(var + "=" + map.get(var) + "\n");
 775 
 776             equal(s1.toString(), s2.toString());
 777 
 778             Iterator<String> kIter = keySet.iterator();
 779             Iterator<String> vIter = values.iterator();
 780             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
 781 
 782             while (eIter.hasNext()) {
 783                 Map.Entry<String,String> entry = eIter.next();
 784                 String key   = kIter.next();
 785                 String value = vIter.next();
 786                 check(entrySet.contains(entry));
 787                 check(keySet.contains(key));
 788                 check(values.contains(value));
 789                 check(map.containsKey(key));
 790                 check(map.containsValue(value));
 791                 equal(entry.getKey(), key);
 792                 equal(entry.getValue(), value);
 793             }
 794             check(!kIter.hasNext() &&
 795                     !vIter.hasNext());
 796 
 797         } catch (Throwable t) { unexpected(t); }
 798     }
 799 
 800     private static void checkMapEquality(Map<String,String> map1,
 801                                          Map<String,String> map2) {
 802         try {
 803             equal(map1.size(), map2.size());
 804             equal(map1.isEmpty(), map2.isEmpty());
 805             for (String key : map1.keySet()) {
 806                 equal(map1.get(key), map2.get(key));
 807                 check(map2.keySet().contains(key));
 808             }
 809             equal(map1, map2);
 810             equal(map2, map1);
 811             equal(map1.entrySet(), map2.entrySet());
 812             equal(map2.entrySet(), map1.entrySet());
 813             equal(map1.keySet(), map2.keySet());
 814             equal(map2.keySet(), map1.keySet());
 815 
 816             equal(map1.hashCode(), map2.hashCode());
 817             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
 818             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
 819         } catch (Throwable t) { unexpected(t); }
 820     }
 821 
 822     static void checkRedirects(ProcessBuilder pb,
 823                                Redirect in, Redirect out, Redirect err) {
 824         equal(pb.redirectInput(), in);
 825         equal(pb.redirectOutput(), out);
 826         equal(pb.redirectError(), err);
 827     }
 828 
 829     static void redirectIO(ProcessBuilder pb,
 830                            Redirect in, Redirect out, Redirect err) {
 831         pb.redirectInput(in);
 832         pb.redirectOutput(out);
 833         pb.redirectError(err);
 834     }
 835 
 836     static void setFileContents(File file, String contents) {
 837         try {
 838             Writer w = new FileWriter(file);
 839             w.write(contents);
 840             w.close();
 841         } catch (Throwable t) { unexpected(t); }
 842     }
 843 
 844     static String fileContents(File file) {
 845         try {
 846             Reader r = new FileReader(file);
 847             StringBuilder sb = new StringBuilder();
 848             char[] buffer = new char[1024];
 849             int n;
 850             while ((n = r.read(buffer)) != -1)
 851                 sb.append(buffer,0,n);
 852             r.close();
 853             return new String(sb);
 854         } catch (Throwable t) { unexpected(t); return ""; }
 855     }
 856 
 857     static void testIORedirection() throws Throwable {
 858         final File ifile = new File("ifile");
 859         final File ofile = new File("ofile");
 860         final File efile = new File("efile");
 861         ifile.delete();
 862         ofile.delete();
 863         efile.delete();
 864 
 865         //----------------------------------------------------------------
 866         // Check mutual inequality of different types of Redirect
 867         //----------------------------------------------------------------
 868         Redirect[] redirects =
 869             { PIPE,
 870               INHERIT,
 871               DISCARD,
 872               Redirect.from(ifile),
 873               Redirect.to(ifile),
 874               Redirect.appendTo(ifile),
 875               Redirect.from(ofile),
 876               Redirect.to(ofile),
 877               Redirect.appendTo(ofile),
 878             };
 879         for (int i = 0; i < redirects.length; i++)
 880             for (int j = 0; j < redirects.length; j++)
 881                 equal(redirects[i].equals(redirects[j]), (i == j));
 882 
 883         //----------------------------------------------------------------
 884         // Check basic properties of different types of Redirect
 885         //----------------------------------------------------------------
 886         equal(PIPE.type(), Redirect.Type.PIPE);
 887         equal(PIPE.toString(), "PIPE");
 888         equal(PIPE.file(), null);
 889 
 890         equal(INHERIT.type(), Redirect.Type.INHERIT);
 891         equal(INHERIT.toString(), "INHERIT");
 892         equal(INHERIT.file(), null);
 893 
 894         equal(DISCARD.type(), Redirect.Type.WRITE);
 895         equal(DISCARD.toString(), "WRITE");
 896         equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));
 897 
 898         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
 899         equal(Redirect.from(ifile).toString(),
 900               "redirect to read from file \"ifile\"");
 901         equal(Redirect.from(ifile).file(), ifile);
 902         equal(Redirect.from(ifile),
 903               Redirect.from(ifile));
 904         equal(Redirect.from(ifile).hashCode(),
 905               Redirect.from(ifile).hashCode());
 906 
 907         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
 908         equal(Redirect.to(ofile).toString(),
 909               "redirect to write to file \"ofile\"");
 910         equal(Redirect.to(ofile).file(), ofile);
 911         equal(Redirect.to(ofile),
 912               Redirect.to(ofile));
 913         equal(Redirect.to(ofile).hashCode(),
 914               Redirect.to(ofile).hashCode());
 915 
 916         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
 917         equal(Redirect.appendTo(efile).toString(),
 918               "redirect to append to file \"efile\"");
 919         equal(Redirect.appendTo(efile).file(), efile);
 920         equal(Redirect.appendTo(efile),
 921               Redirect.appendTo(efile));
 922         equal(Redirect.appendTo(efile).hashCode(),
 923               Redirect.appendTo(efile).hashCode());
 924 
 925         //----------------------------------------------------------------
 926         // Check initial values of redirects
 927         //----------------------------------------------------------------
 928         List<String> childArgs = new ArrayList<String>(javaChildArgs);
 929         childArgs.add("testIO");
 930         final ProcessBuilder pb = new ProcessBuilder(childArgs);
 931         checkRedirects(pb, PIPE, PIPE, PIPE);
 932 
 933         //----------------------------------------------------------------
 934         // Check inheritIO
 935         //----------------------------------------------------------------
 936         pb.inheritIO();
 937         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
 938 
 939         //----------------------------------------------------------------
 940         // Check DISCARD for stdout,stderr
 941         //----------------------------------------------------------------
 942         redirectIO(pb, INHERIT, DISCARD, DISCARD);
 943         checkRedirects(pb, INHERIT, DISCARD, DISCARD);
 944 
 945         //----------------------------------------------------------------
 946         // Check setters and getters agree
 947         //----------------------------------------------------------------
 948         pb.redirectInput(ifile);
 949         equal(pb.redirectInput().file(), ifile);
 950         equal(pb.redirectInput(), Redirect.from(ifile));
 951 
 952         pb.redirectOutput(ofile);
 953         equal(pb.redirectOutput().file(), ofile);
 954         equal(pb.redirectOutput(), Redirect.to(ofile));
 955 
 956         pb.redirectError(efile);
 957         equal(pb.redirectError().file(), efile);
 958         equal(pb.redirectError(), Redirect.to(efile));
 959 
 960         THROWS(IllegalArgumentException.class,
 961                () -> pb.redirectInput(Redirect.to(ofile)),
 962                () -> pb.redirectOutput(Redirect.from(ifile)),
 963                () -> pb.redirectError(Redirect.from(ifile)),
 964                () -> pb.redirectInput(DISCARD));
 965 
 966         THROWS(NullPointerException.class,
 967                 () -> pb.redirectInput((File)null),
 968                 () -> pb.redirectOutput((File)null),
 969                 () -> pb.redirectError((File)null),
 970                 () -> pb.redirectInput((Redirect)null),
 971                 () -> pb.redirectOutput((Redirect)null),
 972                 () -> pb.redirectError((Redirect)null));
 973 
 974         THROWS(IOException.class,
 975                // Input file does not exist
 976                () -> pb.start());
 977         setFileContents(ifile, "standard input");
 978 
 979         //----------------------------------------------------------------
 980         // Writing to non-existent files
 981         //----------------------------------------------------------------
 982         {
 983             ProcessResults r = run(pb);
 984             equal(r.exitValue(), 0);
 985             equal(fileContents(ofile), "standard output");
 986             equal(fileContents(efile), "standard error");
 987             equal(r.out(), "");
 988             equal(r.err(), "");
 989             ofile.delete();
 990             efile.delete();
 991         }
 992 
 993         //----------------------------------------------------------------
 994         // Both redirectErrorStream + redirectError
 995         //----------------------------------------------------------------
 996         {
 997             pb.redirectErrorStream(true);
 998             ProcessResults r = run(pb);
 999             equal(r.exitValue(), 0);
1000             equal(fileContents(ofile),
1001                     "standard error" + "standard output");
1002             equal(fileContents(efile), "");
1003             equal(r.out(), "");
1004             equal(r.err(), "");
1005             ofile.delete();
1006             efile.delete();
1007         }
1008 
1009         //----------------------------------------------------------------
1010         // Appending to existing files
1011         //----------------------------------------------------------------
1012         {
1013             setFileContents(ofile, "ofile-contents");
1014             setFileContents(efile, "efile-contents");
1015             pb.redirectOutput(Redirect.appendTo(ofile));
1016             pb.redirectError(Redirect.appendTo(efile));
1017             pb.redirectErrorStream(false);
1018             ProcessResults r = run(pb);
1019             equal(r.exitValue(), 0);
1020             equal(fileContents(ofile),
1021                   "ofile-contents" + "standard output");
1022             equal(fileContents(efile),
1023                   "efile-contents" + "standard error");
1024             equal(r.out(), "");
1025             equal(r.err(), "");
1026             ofile.delete();
1027             efile.delete();
1028         }
1029 
1030         //----------------------------------------------------------------
1031         // Replacing existing files
1032         //----------------------------------------------------------------
1033         {
1034             setFileContents(ofile, "ofile-contents");
1035             setFileContents(efile, "efile-contents");
1036             pb.redirectOutput(ofile);
1037             pb.redirectError(Redirect.to(efile));
1038             ProcessResults r = run(pb);
1039             equal(r.exitValue(), 0);
1040             equal(fileContents(ofile), "standard output");
1041             equal(fileContents(efile), "standard error");
1042             equal(r.out(), "");
1043             equal(r.err(), "");
1044             ofile.delete();
1045             efile.delete();
1046         }
1047 
1048         //----------------------------------------------------------------
1049         // Appending twice to the same file?
1050         //----------------------------------------------------------------
1051         {
1052             setFileContents(ofile, "ofile-contents");
1053             setFileContents(efile, "efile-contents");
1054             Redirect appender = Redirect.appendTo(ofile);
1055             pb.redirectOutput(appender);
1056             pb.redirectError(appender);
1057             ProcessResults r = run(pb);
1058             equal(r.exitValue(), 0);
1059             equal(fileContents(ofile),
1060                   "ofile-contents" +
1061                   "standard error" +
1062                   "standard output");
1063             equal(fileContents(efile), "efile-contents");
1064             equal(r.out(), "");
1065             equal(r.err(), "");
1066             ifile.delete();
1067             ofile.delete();
1068             efile.delete();
1069         }
1070 
1071         //----------------------------------------------------------------
1072         // DISCARDing output
1073         //----------------------------------------------------------------
1074         {
1075             setFileContents(ifile, "standard input");
1076             pb.redirectOutput(DISCARD);
1077             pb.redirectError(DISCARD);
1078             ProcessResults r = run(pb);
1079             equal(r.exitValue(), 0);
1080             equal(r.out(), "");
1081             equal(r.err(), "");
1082         }
1083 
1084         //----------------------------------------------------------------
1085         // DISCARDing output and redirecting error
1086         //----------------------------------------------------------------
1087         {
1088             setFileContents(ifile, "standard input");
1089             setFileContents(ofile, "ofile-contents");
1090             setFileContents(efile, "efile-contents");
1091             pb.redirectOutput(DISCARD);
1092             pb.redirectError(efile);
1093             ProcessResults r = run(pb);
1094             equal(r.exitValue(), 0);
1095             equal(fileContents(ofile), "ofile-contents");
1096             equal(fileContents(efile), "standard error");
1097             equal(r.out(), "");
1098             equal(r.err(), "");
1099             ofile.delete();
1100             efile.delete();
1101         }
1102 
1103         //----------------------------------------------------------------
1104         // DISCARDing error and redirecting output
1105         //----------------------------------------------------------------
1106         {
1107             setFileContents(ifile, "standard input");
1108             setFileContents(ofile, "ofile-contents");
1109             setFileContents(efile, "efile-contents");
1110             pb.redirectOutput(ofile);
1111             pb.redirectError(DISCARD);
1112             ProcessResults r = run(pb);
1113             equal(r.exitValue(), 0);
1114             equal(fileContents(ofile), "standard output");
1115             equal(fileContents(efile), "efile-contents");
1116             equal(r.out(), "");
1117             equal(r.err(), "");
1118             ofile.delete();
1119             efile.delete();
1120         }
1121 
1122         //----------------------------------------------------------------
1123         // DISCARDing output and merging error into output
1124         //----------------------------------------------------------------
1125         {
1126             setFileContents(ifile, "standard input");
1127             setFileContents(ofile, "ofile-contents");
1128             setFileContents(efile, "efile-contents");
1129             pb.redirectOutput(DISCARD);
1130             pb.redirectErrorStream(true);
1131             pb.redirectError(efile);
1132             ProcessResults r = run(pb);
1133             equal(r.exitValue(), 0);
1134             equal(fileContents(ofile), "ofile-contents");   // untouched
1135             equal(fileContents(efile), "");                 // empty
1136             equal(r.out(), "");
1137             equal(r.err(), "");
1138             ifile.delete();
1139             ofile.delete();
1140             efile.delete();
1141             pb.redirectErrorStream(false);                  // reset for next test
1142         }
1143 
1144         //----------------------------------------------------------------
1145         // Testing INHERIT is harder.
1146         // Note that this requires __FOUR__ nested JVMs involved in one test,
1147         // if you count the harness JVM.
1148         //----------------------------------------------------------------
1149         for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
1150             redirectIO(pb, PIPE, PIPE, PIPE);
1151             List<String> command = pb.command();
1152             command.set(command.size() - 1, testName);
1153             Process p = pb.start();
1154             new PrintStream(p.getOutputStream()).print("standard input");
1155             p.getOutputStream().close();
1156             ProcessResults r = run(p);
1157             equal(r.exitValue(), 0);
1158             equal(r.out(), "standard output");
1159             equal(r.err(), "standard error");
1160         }
1161 
1162         //----------------------------------------------------------------
1163         // Test security implications of I/O redirection
1164         //----------------------------------------------------------------
1165 
1166         // Read access to current directory is always granted;
1167         // So create a tmpfile for input instead.
1168         final File tmpFile = File.createTempFile("Basic", "tmp");
1169         setFileContents(tmpFile, "standard input");
1170 
1171         final Policy policy = new Policy();
1172         Policy.setPolicy(policy);
1173         System.setSecurityManager(new SecurityManager());
1174         try {
1175             final Permission xPermission
1176                 = new FilePermission("<<ALL FILES>>", "execute");
1177             final Permission rxPermission
1178                 = new FilePermission("<<ALL FILES>>", "read,execute");
1179             final Permission wxPermission
1180                 = new FilePermission("<<ALL FILES>>", "write,execute");
1181             final Permission rwxPermission
1182                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
1183 
1184             THROWS(SecurityException.class,
1185                    () -> { policy.setPermissions(xPermission);
1186                            redirectIO(pb, from(tmpFile), PIPE, PIPE);
1187                            pb.start();},
1188                    () -> { policy.setPermissions(rxPermission);
1189                            redirectIO(pb, PIPE, to(ofile), PIPE);
1190                            pb.start();},
1191                    () -> { policy.setPermissions(rxPermission);
1192                            redirectIO(pb, PIPE, PIPE, to(efile));
1193                            pb.start();});
1194 
1195             {
1196                 policy.setPermissions(rxPermission);
1197                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
1198                 ProcessResults r = run(pb);
1199                 equal(r.out(), "standard output");
1200                 equal(r.err(), "standard error");
1201             }
1202 
1203             {
1204                 policy.setPermissions(wxPermission);
1205                 redirectIO(pb, PIPE, to(ofile), to(efile));
1206                 Process p = pb.start();
1207                 new PrintStream(p.getOutputStream()).print("standard input");
1208                 p.getOutputStream().close();
1209                 ProcessResults r = run(p);
1210                 policy.setPermissions(rwxPermission);
1211                 equal(fileContents(ofile), "standard output");
1212                 equal(fileContents(efile), "standard error");
1213             }
1214 
1215             {
1216                 policy.setPermissions(rwxPermission);
1217                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1218                 ProcessResults r = run(pb);
1219                 policy.setPermissions(rwxPermission);
1220                 equal(fileContents(ofile), "standard output");
1221                 equal(fileContents(efile), "standard error");
1222             }
1223 
1224         } finally {
1225             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1226             System.setSecurityManager(null);
1227             tmpFile.delete();
1228             ifile.delete();
1229             ofile.delete();
1230             efile.delete();
1231         }
1232     }
1233 
1234     static void checkProcessPid() {
1235         ProcessBuilder pb = new ProcessBuilder();
1236         List<String> list = new ArrayList<String>(javaChildArgs);
1237         list.add("pid");
1238         pb.command(list);
1239         try {
1240             Process p = pb.start();
1241             String s = commandOutput(p);
1242             long actualPid = Long.valueOf(s.trim());
1243             long expectedPid = p.pid();
1244             equal(actualPid, expectedPid);
1245         } catch (Throwable t) {
1246             unexpected(t);
1247         }
1248 
1249 
1250         // Test the default implementation of Process.getPid
1251         DelegatingProcess p = new DelegatingProcess(null);
1252         THROWS(UnsupportedOperationException.class,
1253                 () -> p.pid(),
1254                 () -> p.toHandle(),
1255                 () -> p.supportsNormalTermination(),
1256                 () -> p.children(),
1257                 () -> p.descendants());
1258 
1259     }
1260 
1261     private static void realMain(String[] args) throws Throwable {
1262         if (Windows.is())
1263             System.out.println("This appears to be a Windows system.");
1264         if (Unix.is())
1265             System.out.println("This appears to be a Unix system.");
1266         if (UnicodeOS.is())
1267             System.out.println("This appears to be a Unicode-based OS.");
1268 
1269         try { testIORedirection(); }
1270         catch (Throwable t) { unexpected(t); }
1271 
1272         //----------------------------------------------------------------
1273         // Basic tests for getPid()
1274         //----------------------------------------------------------------
1275         checkProcessPid();
1276 
1277         //----------------------------------------------------------------
1278         // Basic tests for setting, replacing and deleting envvars
1279         //----------------------------------------------------------------
1280         try {
1281             ProcessBuilder pb = new ProcessBuilder();
1282             Map<String,String> environ = pb.environment();
1283 
1284             // New env var
1285             environ.put("QUUX", "BAR");
1286             equal(environ.get("QUUX"), "BAR");
1287             equal(getenvInChild(pb,"QUUX"), "BAR");
1288 
1289             // Modify env var
1290             environ.put("QUUX","bear");
1291             equal(environ.get("QUUX"), "bear");
1292             equal(getenvInChild(pb,"QUUX"), "bear");
1293             checkMapSanity(environ);
1294 
1295             // Remove env var
1296             environ.remove("QUUX");
1297             equal(environ.get("QUUX"), null);
1298             equal(getenvInChild(pb,"QUUX"), "null");
1299             checkMapSanity(environ);
1300 
1301             // Remove non-existent env var
1302             environ.remove("QUUX");
1303             equal(environ.get("QUUX"), null);
1304             equal(getenvInChild(pb,"QUUX"), "null");
1305             checkMapSanity(environ);
1306         } catch (Throwable t) { unexpected(t); }
1307 
1308         //----------------------------------------------------------------
1309         // Pass Empty environment to child
1310         //----------------------------------------------------------------
1311         try {
1312             ProcessBuilder pb = new ProcessBuilder();
1313             pb.environment().clear();
1314             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1315             expected = AIX.is() ? "LIBPATH="+libpath+",": expected;
1316             if (Windows.is()) {
1317                 pb.environment().put("SystemRoot", systemRoot);
1318             }
1319             if (AIX.is()) {
1320                 pb.environment().put("LIBPATH", libpath);
1321             }
1322             String result = getenvInChild(pb);
1323             if (MacOSX.is()) {
1324                 result = removeMacExpectedVars(result);
1325             }
1326             if (AIX.is()) {
1327                 result = removeAixExpectedVars(result);
1328             }
1329             equal(result, expected);
1330         } catch (Throwable t) { unexpected(t); }
1331 
1332         //----------------------------------------------------------------
1333         // System.getenv() is read-only.
1334         //----------------------------------------------------------------
1335         THROWS(UnsupportedOperationException.class,
1336                () -> getenv().put("FOO","BAR"),
1337                () -> getenv().remove("PATH"),
1338                () -> getenv().keySet().remove("PATH"),
1339                () -> getenv().values().remove("someValue"));
1340 
1341         try {
1342             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1343             if (! c.isEmpty())
1344                 try {
1345                     c.iterator().next().setValue("foo");
1346                     fail("Expected UnsupportedOperationException not thrown");
1347                 } catch (UnsupportedOperationException e) {} // OK
1348         } catch (Throwable t) { unexpected(t); }
1349 
1350         //----------------------------------------------------------------
1351         // System.getenv() always returns the same object in our implementation.
1352         //----------------------------------------------------------------
1353         try {
1354             check(System.getenv() == System.getenv());
1355         } catch (Throwable t) { unexpected(t); }
1356 
1357         //----------------------------------------------------------------
1358         // You can't create an env var name containing "=",
1359         // or an env var name or value containing NUL.
1360         //----------------------------------------------------------------
1361         {
1362             final Map<String,String> m = new ProcessBuilder().environment();
1363             THROWS(IllegalArgumentException.class,
1364                    () -> m.put("FOO=","BAR"),
1365                    () -> m.put("FOO\u0000","BAR"),
1366                    () -> m.put("FOO","BAR\u0000"));
1367         }
1368 
1369         //----------------------------------------------------------------
1370         // Commands must never be null.
1371         //----------------------------------------------------------------
1372         THROWS(NullPointerException.class,
1373                () -> new ProcessBuilder((List<String>)null),
1374                () -> new ProcessBuilder().command((List<String>)null));
1375 
1376         //----------------------------------------------------------------
1377         // Put in a command; get the same one back out.
1378         //----------------------------------------------------------------
1379         try {
1380             List<String> command = new ArrayList<String>();
1381             ProcessBuilder pb = new ProcessBuilder(command);
1382             check(pb.command() == command);
1383             List<String> command2 = new ArrayList<String>(2);
1384             command2.add("foo");
1385             command2.add("bar");
1386             pb.command(command2);
1387             check(pb.command() == command2);
1388             pb.command("foo", "bar");
1389             check(pb.command() != command2 && pb.command().equals(command2));
1390             pb.command(command2);
1391             command2.add("baz");
1392             equal(pb.command().get(2), "baz");
1393         } catch (Throwable t) { unexpected(t); }
1394 
1395         //----------------------------------------------------------------
1396         // Commands must contain at least one element.
1397         //----------------------------------------------------------------
1398         THROWS(IndexOutOfBoundsException.class,
1399                () -> new ProcessBuilder().start(),
1400                () -> new ProcessBuilder(new ArrayList<String>()).start(),
1401                () -> Runtime.getRuntime().exec(new String[]{}));
1402 
1403         //----------------------------------------------------------------
1404         // Commands must not contain null elements at start() time.
1405         //----------------------------------------------------------------
1406         THROWS(NullPointerException.class,
1407                () -> new ProcessBuilder("foo",null,"bar").start(),
1408                () -> new ProcessBuilder((String)null).start(),
1409                () -> new ProcessBuilder(new String[]{null}).start(),
1410                () -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());
1411 
1412         //----------------------------------------------------------------
1413         // Command lists are growable.
1414         //----------------------------------------------------------------
1415         try {
1416             new ProcessBuilder().command().add("foo");
1417             new ProcessBuilder("bar").command().add("foo");
1418             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1419         } catch (Throwable t) { unexpected(t); }
1420 
1421         //----------------------------------------------------------------
1422         // Nulls in environment updates generate NullPointerException
1423         //----------------------------------------------------------------
1424         try {
1425             final Map<String,String> env = new ProcessBuilder().environment();
1426             THROWS(NullPointerException.class,
1427                    () -> env.put("foo",null),
1428                    () -> env.put(null,"foo"),
1429                    () -> env.remove(null),
1430                    () -> { for (Map.Entry<String,String> e : env.entrySet())
1431                                e.setValue(null);},
1432                    () -> Runtime.getRuntime().exec(new String[]{"foo"},
1433                                                    new String[]{null}));
1434         } catch (Throwable t) { unexpected(t); }
1435 
1436         //----------------------------------------------------------------
1437         // Non-String types in environment updates generate ClassCastException
1438         //----------------------------------------------------------------
1439         try {
1440             final Map<String,String> env = new ProcessBuilder().environment();
1441             THROWS(ClassCastException.class,
1442                    () -> env.remove(TRUE),
1443                    () -> env.keySet().remove(TRUE),
1444                    () -> env.values().remove(TRUE),
1445                    () -> env.entrySet().remove(TRUE));
1446         } catch (Throwable t) { unexpected(t); }
1447 
1448         //----------------------------------------------------------------
1449         // Check query operations on environment maps
1450         //----------------------------------------------------------------
1451         try {
1452             List<Map<String,String>> envs =
1453                 new ArrayList<Map<String,String>>(2);
1454             envs.add(System.getenv());
1455             envs.add(new ProcessBuilder().environment());
1456             for (final Map<String,String> env : envs) {
1457                 //----------------------------------------------------------------
1458                 // Nulls in environment queries are forbidden.
1459                 //----------------------------------------------------------------
1460                 THROWS(NullPointerException.class,
1461                        () -> getenv(null),
1462                        () -> env.get(null),
1463                        () -> env.containsKey(null),
1464                        () -> env.containsValue(null),
1465                        () -> env.keySet().contains(null),
1466                        () -> env.values().contains(null));
1467 
1468                 //----------------------------------------------------------------
1469                 // Non-String types in environment queries are forbidden.
1470                 //----------------------------------------------------------------
1471                 THROWS(ClassCastException.class,
1472                        () -> env.get(TRUE),
1473                        () -> env.containsKey(TRUE),
1474                        () -> env.containsValue(TRUE),
1475                        () -> env.keySet().contains(TRUE),
1476                        () -> env.values().contains(TRUE));
1477 
1478                 //----------------------------------------------------------------
1479                 // Illegal String values in environment queries are (grumble) OK
1480                 //----------------------------------------------------------------
1481                 equal(env.get("\u0000"), null);
1482                 check(! env.containsKey("\u0000"));
1483                 check(! env.containsValue("\u0000"));
1484                 check(! env.keySet().contains("\u0000"));
1485                 check(! env.values().contains("\u0000"));
1486             }
1487 
1488         } catch (Throwable t) { unexpected(t); }
1489 
1490         try {
1491             final Set<Map.Entry<String,String>> entrySet =
1492                 new ProcessBuilder().environment().entrySet();
1493             THROWS(NullPointerException.class,
1494                    () -> entrySet.contains(null));
1495             THROWS(ClassCastException.class,
1496                    () -> entrySet.contains(TRUE),
1497                    () -> entrySet.contains(
1498                              new SimpleImmutableEntry<Boolean,String>(TRUE,"")));
1499 
1500             check(! entrySet.contains
1501                   (new SimpleImmutableEntry<String,String>("", "")));
1502         } catch (Throwable t) { unexpected(t); }
1503 
1504         //----------------------------------------------------------------
1505         // Put in a directory; get the same one back out.
1506         //----------------------------------------------------------------
1507         try {
1508             ProcessBuilder pb = new ProcessBuilder();
1509             File foo = new File("foo");
1510             equal(pb.directory(), null);
1511             equal(pb.directory(foo).directory(), foo);
1512             equal(pb.directory(null).directory(), null);
1513         } catch (Throwable t) { unexpected(t); }
1514 
1515         //----------------------------------------------------------------
1516         // If round-trip conversion works, check envvar pass-through to child
1517         //----------------------------------------------------------------
1518         try {
1519             testEncoding("ASCII",   "xyzzy");
1520             testEncoding("Latin1",  "\u00f1\u00e1");
1521             testEncoding("Unicode", "\u22f1\u11e1");
1522         } catch (Throwable t) { unexpected(t); }
1523 
1524         //----------------------------------------------------------------
1525         // A surprisingly large number of ways to delete an environment var.
1526         //----------------------------------------------------------------
1527         testVariableDeleter(new EnvironmentFrobber() {
1528                 public void doIt(Map<String,String> environ) {
1529                     environ.remove("Foo");}});
1530 
1531         testVariableDeleter(new EnvironmentFrobber() {
1532                 public void doIt(Map<String,String> environ) {
1533                     environ.keySet().remove("Foo");}});
1534 
1535         testVariableDeleter(new EnvironmentFrobber() {
1536                 public void doIt(Map<String,String> environ) {
1537                     environ.values().remove("BAAR");}});
1538 
1539         testVariableDeleter(new EnvironmentFrobber() {
1540                 public void doIt(Map<String,String> environ) {
1541                     // Legally fabricate a ProcessEnvironment.StringEntry,
1542                     // even though it's private.
1543                     Map<String,String> environ2
1544                         = new ProcessBuilder().environment();
1545                     environ2.clear();
1546                     environ2.put("Foo","BAAR");
1547                     // Subtlety alert.
1548                     Map.Entry<String,String> e
1549                         = environ2.entrySet().iterator().next();
1550                     environ.entrySet().remove(e);}});
1551 
1552         testVariableDeleter(new EnvironmentFrobber() {
1553                 public void doIt(Map<String,String> environ) {
1554                     Map.Entry<String,String> victim = null;
1555                     for (Map.Entry<String,String> e : environ.entrySet())
1556                         if (e.getKey().equals("Foo"))
1557                             victim = e;
1558                     if (victim != null)
1559                         environ.entrySet().remove(victim);}});
1560 
1561         testVariableDeleter(new EnvironmentFrobber() {
1562                 public void doIt(Map<String,String> environ) {
1563                     Iterator<String> it = environ.keySet().iterator();
1564                     while (it.hasNext()) {
1565                         String val = it.next();
1566                         if (val.equals("Foo"))
1567                             it.remove();}}});
1568 
1569         testVariableDeleter(new EnvironmentFrobber() {
1570                 public void doIt(Map<String,String> environ) {
1571                     Iterator<Map.Entry<String,String>> it
1572                         = environ.entrySet().iterator();
1573                     while (it.hasNext()) {
1574                         Map.Entry<String,String> e = it.next();
1575                         if (e.getKey().equals("Foo"))
1576                             it.remove();}}});
1577 
1578         testVariableDeleter(new EnvironmentFrobber() {
1579                 public void doIt(Map<String,String> environ) {
1580                     Iterator<String> it = environ.values().iterator();
1581                     while (it.hasNext()) {
1582                         String val = it.next();
1583                         if (val.equals("BAAR"))
1584                             it.remove();}}});
1585 
1586         //----------------------------------------------------------------
1587         // A surprisingly small number of ways to add an environment var.
1588         //----------------------------------------------------------------
1589         testVariableAdder(new EnvironmentFrobber() {
1590                 public void doIt(Map<String,String> environ) {
1591                     environ.put("Foo","Bahrein");}});
1592 
1593         //----------------------------------------------------------------
1594         // A few ways to modify an environment var.
1595         //----------------------------------------------------------------
1596         testVariableModifier(new EnvironmentFrobber() {
1597                 public void doIt(Map<String,String> environ) {
1598                     environ.put("Foo","NewValue");}});
1599 
1600         testVariableModifier(new EnvironmentFrobber() {
1601                 public void doIt(Map<String,String> environ) {
1602                     for (Map.Entry<String,String> e : environ.entrySet())
1603                         if (e.getKey().equals("Foo"))
1604                             e.setValue("NewValue");}});
1605 
1606         //----------------------------------------------------------------
1607         // Fiddle with environment sizes
1608         //----------------------------------------------------------------
1609         try {
1610             Map<String,String> environ = new ProcessBuilder().environment();
1611             int size = environ.size();
1612             checkSizes(environ, size);
1613 
1614             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1615             checkSizes(environ, size+1);
1616 
1617             // Check for environment independence
1618             new ProcessBuilder().environment().clear();
1619 
1620             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1621             checkSizes(environ, size+1);
1622 
1623             environ.remove("UnLiKeLYeNVIROmtNam");
1624             checkSizes(environ, size);
1625 
1626             environ.clear();
1627             checkSizes(environ, 0);
1628 
1629             environ.clear();
1630             checkSizes(environ, 0);
1631 
1632             environ = new ProcessBuilder().environment();
1633             environ.keySet().clear();
1634             checkSizes(environ, 0);
1635 
1636             environ = new ProcessBuilder().environment();
1637             environ.entrySet().clear();
1638             checkSizes(environ, 0);
1639 
1640             environ = new ProcessBuilder().environment();
1641             environ.values().clear();
1642             checkSizes(environ, 0);
1643         } catch (Throwable t) { unexpected(t); }
1644 
1645         //----------------------------------------------------------------
1646         // Check that various map invariants hold
1647         //----------------------------------------------------------------
1648         checkMapSanity(new ProcessBuilder().environment());
1649         checkMapSanity(System.getenv());
1650         checkMapEquality(new ProcessBuilder().environment(),
1651                          new ProcessBuilder().environment());
1652 
1653 
1654         //----------------------------------------------------------------
1655         // Check effects on external "env" command.
1656         //----------------------------------------------------------------
1657         try {
1658             Set<String> env1 = new HashSet<String>
1659                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1660 
1661             ProcessBuilder pb = new ProcessBuilder();
1662             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1663 
1664             Set<String> env2 = new HashSet<String>
1665                 (Arrays.asList(nativeEnv(pb).split("\n")));
1666 
1667             check(env2.size() == env1.size() + 1);
1668             env1.add("QwErTyUiOp=AsDfGhJk");
1669             check(env1.equals(env2));
1670         } catch (Throwable t) { unexpected(t); }
1671 
1672         //----------------------------------------------------------------
1673         // Test Runtime.exec(...envp...)
1674         // Check for sort order of environment variables on Windows.
1675         //----------------------------------------------------------------
1676         try {
1677             String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1678             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1679             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1680                             "+=+", "_=_", "~=~", systemRoot};
1681             String output = nativeEnv(envp);
1682             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1683             // On Windows, Java must keep the environment sorted.
1684             // Order is random on Unix, so this test does the sort.
1685             if (! Windows.is())
1686                 output = sortByLinesWindowsly(output);
1687             equal(output, expected);
1688         } catch (Throwable t) { unexpected(t); }
1689 
1690         //----------------------------------------------------------------
1691         // Test Runtime.exec(...envp...)
1692         // and check SystemRoot gets set automatically on Windows
1693         //----------------------------------------------------------------
1694         try {
1695             if (Windows.is()) {
1696                 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1697                 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1698                                 "+=+", "_=_", "~=~"};
1699                 String output = nativeEnv(envp);
1700                 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1701                 equal(output, expected);
1702             }
1703         } catch (Throwable t) { unexpected(t); }
1704 
1705         //----------------------------------------------------------------
1706         // System.getenv() must be consistent with System.getenv(String)
1707         //----------------------------------------------------------------
1708         try {
1709             for (Map.Entry<String,String> e : getenv().entrySet())
1710                 equal(getenv(e.getKey()), e.getValue());
1711         } catch (Throwable t) { unexpected(t); }
1712 
1713         //----------------------------------------------------------------
1714         // Fiddle with working directory in child
1715         //----------------------------------------------------------------
1716         try {
1717             String canonicalUserDir =
1718                 new File(System.getProperty("user.dir")).getCanonicalPath();
1719             String[] sdirs = new String[]
1720                 {".", "..", "/", "/bin",
1721                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1722                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1723             for (String sdir : sdirs) {
1724                 File dir = new File(sdir);
1725                 if (! (dir.isDirectory() && dir.exists()))
1726                     continue;
1727                 out.println("Testing directory " + dir);
1728                 //dir = new File(dir.getCanonicalPath());
1729 
1730                 ProcessBuilder pb = new ProcessBuilder();
1731                 equal(pb.directory(), null);
1732                 equal(pwdInChild(pb), canonicalUserDir);
1733 
1734                 pb.directory(dir);
1735                 equal(pb.directory(), dir);
1736                 equal(pwdInChild(pb), dir.getCanonicalPath());
1737 
1738                 pb.directory(null);
1739                 equal(pb.directory(), null);
1740                 equal(pwdInChild(pb), canonicalUserDir);
1741 
1742                 pb.directory(dir);
1743             }
1744         } catch (Throwable t) { unexpected(t); }
1745 
1746         //----------------------------------------------------------------
1747         // Working directory with Unicode in child
1748         //----------------------------------------------------------------
1749         try {
1750             if (UnicodeOS.is()) {
1751                 File dir = new File(System.getProperty("test.dir", "."),
1752                                     "ProcessBuilderDir\u4e00\u4e02");
1753                 try {
1754                     if (!dir.exists())
1755                         dir.mkdir();
1756                     out.println("Testing Unicode directory:" + dir);
1757                     ProcessBuilder pb = new ProcessBuilder();
1758                     pb.directory(dir);
1759                     equal(pwdInChild(pb), dir.getCanonicalPath());
1760                 } finally {
1761                     if (dir.exists())
1762                         dir.delete();
1763                 }
1764             }
1765         } catch (Throwable t) { unexpected(t); }
1766 
1767         //----------------------------------------------------------------
1768         // OOME in child allocating maximally sized array
1769         // Test for hotspot/jvmti bug 6850957
1770         //----------------------------------------------------------------
1771         try {
1772             List<String> list = new ArrayList<String>(javaChildArgs);
1773             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1774                                       javaExe));
1775             list.add("ArrayOOME");
1776             ProcessResults r = run(new ProcessBuilder(list));
1777             check(r.err().contains("java.lang.OutOfMemoryError:"));
1778             check(r.err().contains(javaExe));
1779             check(r.err().contains(System.getProperty("java.version")));
1780             equal(r.exitValue(), 1);
1781         } catch (Throwable t) { unexpected(t); }
1782 
1783         //----------------------------------------------------------------
1784         // Windows has tricky semi-case-insensitive semantics
1785         //----------------------------------------------------------------
1786         if (Windows.is())
1787             try {
1788                 out.println("Running case insensitve variable tests");
1789                 for (String[] namePair :
1790                          new String[][]
1791                     { new String[]{"PATH","PaTh"},
1792                       new String[]{"home","HOME"},
1793                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1794                     check((getenv(namePair[0]) == null &&
1795                            getenv(namePair[1]) == null)
1796                           ||
1797                           getenv(namePair[0]).equals(getenv(namePair[1])),
1798                           "Windows environment variables are not case insensitive");
1799                 }
1800             } catch (Throwable t) { unexpected(t); }
1801 
1802         //----------------------------------------------------------------
1803         // Test proper Unicode child environment transfer
1804         //----------------------------------------------------------------
1805         if (UnicodeOS.is())
1806             try {
1807                 ProcessBuilder pb = new ProcessBuilder();
1808                 pb.environment().put("\u1234","\u5678");
1809                 pb.environment().remove("PATH");
1810                 equal(getenvInChild1234(pb), "\u5678");
1811             } catch (Throwable t) { unexpected(t); }
1812 
1813 
1814         //----------------------------------------------------------------
1815         // Test Runtime.exec(...envp...) with envstrings with initial `='
1816         //----------------------------------------------------------------
1817         try {
1818             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1819             childArgs.add("System.getenv()");
1820             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1821             String[] envp;
1822             String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};
1823             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1824             if (Windows.is()) {
1825                 envp = envpWin;
1826             } else {
1827                 envp = envpOth;
1828             }
1829             Process p = Runtime.getRuntime().exec(cmdp, envp);
1830             String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";
1831             expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;
1832             String commandOutput = commandOutput(p);
1833             if (MacOSX.is()) {
1834                 commandOutput = removeMacExpectedVars(commandOutput);
1835             }
1836             if (AIX.is()) {
1837                 commandOutput = removeAixExpectedVars(commandOutput);
1838             }
1839             equal(commandOutput, expected);
1840             if (Windows.is()) {
1841                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1842                 pb.environment().clear();
1843                 pb.environment().put("SystemRoot", systemRoot);
1844                 pb.environment().put("=ExitValue", "3");
1845                 pb.environment().put("=C:", "\\");
1846                 equal(commandOutput(pb), expected);
1847             }
1848         } catch (Throwable t) { unexpected(t); }
1849 
1850         //----------------------------------------------------------------
1851         // Test Runtime.exec(...envp...) with envstrings without any `='
1852         //----------------------------------------------------------------
1853         try {
1854             String[] cmdp = {"echo"};
1855             String[] envp = {"Hello", "World"}; // Yuck!
1856             Process p = Runtime.getRuntime().exec(cmdp, envp);
1857             equal(commandOutput(p), "\n");
1858         } catch (Throwable t) { unexpected(t); }
1859 
1860         //----------------------------------------------------------------
1861         // Test Runtime.exec(...envp...) with envstrings containing NULs
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[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1868                              "FO\u0000=B\u0000R"};
1869             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1870                              "FO\u0000=B\u0000R"};
1871             String[] envp;
1872             if (Windows.is()) {
1873                 envp = envpWin;
1874             } else {
1875                 envp = envpOth;
1876             }
1877             System.out.println ("cmdp");
1878             for (int i=0; i<cmdp.length; i++) {
1879                 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);
1880             }
1881             System.out.println ("envp");
1882             for (int i=0; i<envp.length; i++) {
1883                 System.out.printf ("envp %d: %s\n", i, envp[i]);
1884             }
1885             Process p = Runtime.getRuntime().exec(cmdp, envp);
1886             String commandOutput = commandOutput(p);
1887             if (MacOSX.is()) {
1888                 commandOutput = removeMacExpectedVars(commandOutput);
1889             }
1890             if (AIX.is()) {
1891                 commandOutput = removeAixExpectedVars(commandOutput);
1892             }
1893             check(commandOutput.equals(Windows.is()
1894                     ? "LC_ALL=C,SystemRoot="+systemRoot+","
1895                     : AIX.is()
1896                             ? "LC_ALL=C,LIBPATH="+libpath+","
1897                             : "LC_ALL=C,"),
1898                   "Incorrect handling of envstrings containing NULs");
1899         } catch (Throwable t) { unexpected(t); }
1900 
1901         //----------------------------------------------------------------
1902         // Test the redirectErrorStream property
1903         //----------------------------------------------------------------
1904         try {
1905             ProcessBuilder pb = new ProcessBuilder();
1906             equal(pb.redirectErrorStream(), false);
1907             equal(pb.redirectErrorStream(true), pb);
1908             equal(pb.redirectErrorStream(), true);
1909             equal(pb.redirectErrorStream(false), pb);
1910             equal(pb.redirectErrorStream(), false);
1911         } catch (Throwable t) { unexpected(t); }
1912 
1913         try {
1914             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1915             childArgs.add("OutErr");
1916             ProcessBuilder pb = new ProcessBuilder(childArgs);
1917             {
1918                 ProcessResults r = run(pb);
1919                 equal(r.out(), "outout");
1920                 equal(r.err(), "errerr");
1921             }
1922             {
1923                 pb.redirectErrorStream(true);
1924                 ProcessResults r = run(pb);
1925                 equal(r.out(), "outerrouterr");
1926                 equal(r.err(), "");
1927             }
1928         } catch (Throwable t) { unexpected(t); }
1929 
1930         if (Unix.is()) {
1931             //----------------------------------------------------------------
1932             // We can find true and false when PATH is null
1933             //----------------------------------------------------------------
1934             try {
1935                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1936                 childArgs.add("null PATH");
1937                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1938                 pb.environment().remove("PATH");
1939                 ProcessResults r = run(pb);
1940                 equal(r.out(), "");
1941                 equal(r.err(), "");
1942                 equal(r.exitValue(), 0);
1943             } catch (Throwable t) { unexpected(t); }
1944 
1945             //----------------------------------------------------------------
1946             // PATH search algorithm on Unix
1947             //----------------------------------------------------------------
1948             try {
1949                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1950                 childArgs.add("PATH search algorithm");
1951                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1952                 pb.environment().put("PATH", "dir1:dir2:");
1953                 ProcessResults r = run(pb);
1954                 equal(r.out(), "");
1955                 equal(r.err(), "");
1956                 equal(r.exitValue(), True.exitValue());
1957             } catch (Throwable t) { unexpected(t); }
1958 
1959             //----------------------------------------------------------------
1960             // Parent's, not child's PATH is used
1961             //----------------------------------------------------------------
1962             try {
1963                 new File("suBdiR").mkdirs();
1964                 copy("/bin/true", "suBdiR/unliKely");
1965                 final ProcessBuilder pb =
1966                     new ProcessBuilder(new String[]{"unliKely"});
1967                 pb.environment().put("PATH", "suBdiR");
1968                 THROWS(IOException.class, () -> pb.start());
1969             } catch (Throwable t) { unexpected(t);
1970             } finally {
1971                 new File("suBdiR/unliKely").delete();
1972                 new File("suBdiR").delete();
1973             }
1974         }
1975 
1976         //----------------------------------------------------------------
1977         // Attempt to start bogus program ""
1978         //----------------------------------------------------------------
1979         try {
1980             new ProcessBuilder("").start();
1981             fail("Expected IOException not thrown");
1982         } catch (IOException e) {
1983             String m = e.getMessage();
1984             if (EnglishUnix.is() &&
1985                 ! matches(m, "No such file or directory"))
1986                 unexpected(e);
1987         } catch (Throwable t) { unexpected(t); }
1988 
1989         //----------------------------------------------------------------
1990         // Check that attempt to execute program name with funny
1991         // characters throws an exception containing those characters.
1992         //----------------------------------------------------------------
1993         for (String programName : new String[] {"\u00f0", "\u01f0"})
1994             try {
1995                 new ProcessBuilder(programName).start();
1996                 fail("Expected IOException not thrown");
1997             } catch (IOException e) {
1998                 String m = e.getMessage();
1999                 Pattern p = Pattern.compile(programName);
2000                 if (! matches(m, programName)
2001                     || (EnglishUnix.is()
2002                         && ! matches(m, "No such file or directory")))
2003                     unexpected(e);
2004             } catch (Throwable t) { unexpected(t); }
2005 
2006         //----------------------------------------------------------------
2007         // Attempt to start process in nonexistent directory fails.
2008         //----------------------------------------------------------------
2009         try {
2010             new ProcessBuilder("echo")
2011                 .directory(new File("UnLiKeLY"))
2012                 .start();
2013             fail("Expected IOException not thrown");
2014         } catch (IOException e) {
2015             String m = e.getMessage();
2016             if (! matches(m, "in directory")
2017                 || (EnglishUnix.is() &&
2018                     ! matches(m, "No such file or directory")))
2019                 unexpected(e);
2020         } catch (Throwable t) { unexpected(t); }
2021 
2022         //----------------------------------------------------------------
2023         // Attempt to write 4095 bytes to the pipe buffer without a
2024         // reader to drain it would deadlock, if not for the fact that
2025         // interprocess pipe buffers are at least 4096 bytes.
2026         //
2027         // Also, check that available reports all the bytes expected
2028         // in the pipe buffer, and that I/O operations do the expected
2029         // things.
2030         //----------------------------------------------------------------
2031         try {
2032             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2033             childArgs.add("print4095");
2034             final int SIZE = 4095;
2035             final Process p = new ProcessBuilder(childArgs).start();
2036             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
2037             p.waitFor();                                // Might hang!
2038             equal(SIZE, p.getInputStream().available());
2039             equal(SIZE, p.getErrorStream().available());
2040             THROWS(IOException.class,
2041                    () -> { p.getOutputStream().write((byte) '!');
2042                            p.getOutputStream().flush();});
2043 
2044             final byte[] bytes = new byte[SIZE + 1];
2045             equal(SIZE, p.getInputStream().read(bytes));
2046             for (int i = 0; i < SIZE; i++)
2047                 equal((byte) '!', bytes[i]);
2048             equal((byte) 0, bytes[SIZE]);
2049 
2050             equal(SIZE, p.getErrorStream().read(bytes));
2051             for (int i = 0; i < SIZE; i++)
2052                 equal((byte) 'E', bytes[i]);
2053             equal((byte) 0, bytes[SIZE]);
2054 
2055             equal(0, p.getInputStream().available());
2056             equal(0, p.getErrorStream().available());
2057             equal(-1, p.getErrorStream().read());
2058             equal(-1, p.getInputStream().read());
2059 
2060             equal(p.exitValue(), 5);
2061 
2062             p.getInputStream().close();
2063             p.getErrorStream().close();
2064             try { p.getOutputStream().close(); } catch (IOException flushFailed) { }
2065 
2066             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
2067             for (final InputStream in : streams) {
2068                 Fun[] ops = {
2069                     () -> in.read(),
2070                     () -> in.read(bytes),
2071                     () -> in.available()
2072                 };
2073                 for (Fun op : ops) {
2074                     try {
2075                         op.f();
2076                         fail();
2077                     } catch (IOException expected) {
2078                         check(expected.getMessage()
2079                               .matches("[Ss]tream [Cc]losed"));
2080                     }
2081                 }
2082             }
2083         } catch (Throwable t) { unexpected(t); }
2084 
2085         //----------------------------------------------------------------
2086         // Check that reads which are pending when Process.destroy is
2087         // called, get EOF, not IOException("Stream closed").
2088         //----------------------------------------------------------------
2089         try {
2090             final int cases = 4;
2091             for (int i = 0; i < cases; i++) {
2092                 final int action = i;
2093                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
2094                 childArgs.add("sleep");
2095                 final byte[] bytes = new byte[10];
2096                 final Process p = new ProcessBuilder(childArgs).start();
2097                 final CountDownLatch latch = new CountDownLatch(1);
2098                 final InputStream s;
2099                 switch (action & 0x1) {
2100                     case 0: s = p.getInputStream(); break;
2101                     case 1: s = p.getErrorStream(); break;
2102                     default: throw new Error();
2103                 }
2104                 final Thread thread = new Thread() {
2105                     public void run() {
2106                         try {
2107                             int r;
2108                             latch.countDown();
2109                             switch (action & 0x2) {
2110                                 case 0: r = s.read(); break;
2111                                 case 2: r = s.read(bytes); break;
2112                                 default: throw new Error();
2113                             }
2114                             equal(-1, r);
2115                         } catch (Throwable t) { unexpected(t); }}};
2116 
2117                 thread.start();
2118                 latch.await();
2119                 Thread.sleep(10);
2120 
2121                 String os = System.getProperty("os.name");
2122                 if (os.equalsIgnoreCase("Solaris") ||
2123                     os.equalsIgnoreCase("SunOS"))
2124                 {
2125                     final Object deferred;
2126                     Class<?> c = s.getClass();
2127                     if (c.getName().equals(
2128                         "java.lang.ProcessImpl$DeferredCloseInputStream"))
2129                     {
2130                         deferred = s;
2131                     } else {
2132                         Field deferredField = p.getClass().
2133                             getDeclaredField("stdout_inner_stream");
2134                         deferredField.setAccessible(true);
2135                         deferred = deferredField.get(p);
2136                     }
2137                     Field useCountField = deferred.getClass().
2138                         getDeclaredField("useCount");
2139                     useCountField.setAccessible(true);
2140 
2141                     while (useCountField.getInt(deferred) <= 0) {
2142                         Thread.yield();
2143                     }
2144                 } else if (s instanceof BufferedInputStream) {
2145                     // Wait until after the s.read occurs in "thread" by
2146                     // checking when the input stream monitor is acquired
2147                     // (BufferedInputStream.read is synchronized)
2148                     while (!isLocked(s, 10)) {
2149                         Thread.sleep(100);
2150                     }
2151                 }
2152                 p.destroy();
2153                 thread.join();
2154             }
2155         } catch (Throwable t) { unexpected(t); }
2156 
2157         //----------------------------------------------------------------
2158         // Check that subprocesses which create subprocesses of their
2159         // own do not cause parent to hang waiting for file
2160         // descriptors to be closed.
2161         //----------------------------------------------------------------
2162         try {
2163             if (Unix.is()
2164                 && new File("/bin/bash").exists()
2165                 && new File("/bin/sleep").exists()) {
2166                 // Notice that we only destroy the process created by us (i.e.
2167                 // our child) but not our grandchild (i.e. '/bin/sleep'). So
2168                 // pay attention that the grandchild doesn't run too long to
2169                 // avoid polluting the process space with useless processes.
2170                 // Running the grandchild for 60s should be more than enough.
2171                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };
2172                 final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };
2173                 final ProcessBuilder pb = new ProcessBuilder(cmd);
2174                 final Process p = pb.start();
2175                 final InputStream stdout = p.getInputStream();
2176                 final InputStream stderr = p.getErrorStream();
2177                 final OutputStream stdin = p.getOutputStream();
2178                 final Thread reader = new Thread() {
2179                     public void run() {
2180                         try { stdout.read(); }
2181                         catch (IOException e) {
2182                             // Check that reader failed because stream was
2183                             // asynchronously closed.
2184                             // e.printStackTrace();
2185                             if (EnglishUnix.is() &&
2186                                 ! (e.getMessage().matches(".*Bad file.*")))
2187                                 unexpected(e);
2188                         }
2189                         catch (Throwable t) { unexpected(t); }}};
2190                 reader.setDaemon(true);
2191                 reader.start();
2192                 Thread.sleep(100);
2193                 p.destroy();
2194                 check(p.waitFor() != 0);
2195                 check(p.exitValue() != 0);
2196                 // Subprocess is now dead, but file descriptors remain open.
2197                 // Make sure the test will fail if we don't manage to close
2198                 // the open streams within 30 seconds. Notice that this time
2199                 // must be shorter than the sleep time of the grandchild.
2200                 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);
2201                 t.schedule(new TimerTask() {
2202                       public void run() {
2203                           fail("Subprocesses which create subprocesses of " +
2204                                "their own caused the parent to hang while " +
2205                                "waiting for file descriptors to be closed.");
2206                           System.exit(-1);
2207                       }
2208                   }, 30000);
2209                 stdout.close();
2210                 stderr.close();
2211                 stdin.close();
2212                 new ProcessBuilder(cmdkill).start();
2213                 // All streams successfully closed so we can cancel the timer.
2214                 t.cancel();
2215                 //----------------------------------------------------------
2216                 // There remain unsolved issues with asynchronous close.
2217                 // Here's a highly non-portable experiment to demonstrate:
2218                 //----------------------------------------------------------
2219                 if (Boolean.getBoolean("wakeupJeff!")) {
2220                     System.out.println("wakeupJeff!");
2221                     // Initialize signal handler for INTERRUPT_SIGNAL.
2222                     new FileInputStream("/bin/sleep").getChannel().close();
2223                     // Send INTERRUPT_SIGNAL to every thread in this java.
2224                     String[] wakeupJeff = {
2225                         "/bin/bash", "-c",
2226                         "/bin/ps --noheaders -Lfp $PPID | " +
2227                         "/usr/bin/perl -nale 'print $F[3]' | " +
2228                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
2229                         "/usr/bin/xargs kill -62"
2230                     };
2231                     new ProcessBuilder(wakeupJeff).start().waitFor();
2232                     // If wakeupJeff worked, reader probably got EBADF.
2233                     reader.join();
2234                 }
2235             }
2236 
2237             //----------------------------------------------------------------
2238             // Check the Process toString() method
2239             //----------------------------------------------------------------
2240             {
2241                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
2242                 childArgs.add("testIO");
2243                 ProcessBuilder pb = new ProcessBuilder(childArgs);
2244                 pb.redirectInput(Redirect.PIPE);
2245                 pb.redirectOutput(DISCARD);
2246                 pb.redirectError(DISCARD);
2247                 final Process p = pb.start();
2248                 // Child process waits until it gets input
2249                 String s = p.toString();
2250                 check(s.contains("not exited"));
2251                 check(s.contains("pid=" + p.pid() + ","));
2252 
2253                 new PrintStream(p.getOutputStream()).print("standard input");
2254                 p.getOutputStream().close();
2255 
2256                 // Check the toString after it exits
2257                 int exitValue = p.waitFor();
2258                 s = p.toString();
2259                 check(s.contains("pid=" + p.pid() + ","));
2260                 check(s.contains("exitValue=" + exitValue) &&
2261                         !s.contains("not exited"));
2262             }
2263         } catch (Throwable t) { unexpected(t); }
2264 
2265         //----------------------------------------------------------------
2266         // Attempt to start process with insufficient permissions fails.
2267         //----------------------------------------------------------------
2268         try {
2269             new File("emptyCommand").delete();
2270             new FileOutputStream("emptyCommand").close();
2271             new File("emptyCommand").setExecutable(false);
2272             new ProcessBuilder("./emptyCommand").start();
2273             fail("Expected IOException not thrown");
2274         } catch (IOException e) {
2275             new File("./emptyCommand").delete();
2276             String m = e.getMessage();
2277             if (EnglishUnix.is() &&
2278                 ! matches(m, "Permission denied"))
2279                 unexpected(e);
2280         } catch (Throwable t) { unexpected(t); }
2281 
2282         new File("emptyCommand").delete();
2283 
2284         //----------------------------------------------------------------
2285         // Check for correct security permission behavior
2286         //----------------------------------------------------------------
2287         final Policy policy = new Policy();
2288         Policy.setPolicy(policy);
2289         System.setSecurityManager(new SecurityManager());
2290 
2291         try {
2292             // No permissions required to CREATE a ProcessBuilder
2293             policy.setPermissions(/* Nothing */);
2294             new ProcessBuilder("env").directory(null).directory();
2295             new ProcessBuilder("env").directory(new File("dir")).directory();
2296             new ProcessBuilder("env").command("??").command();
2297         } catch (Throwable t) { unexpected(t); }
2298 
2299         THROWS(SecurityException.class,
2300                () -> { policy.setPermissions(/* Nothing */);
2301                        System.getenv("foo");},
2302                () -> { policy.setPermissions(/* Nothing */);
2303                        System.getenv();},
2304                () -> { policy.setPermissions(/* Nothing */);
2305                        new ProcessBuilder("echo").start();},
2306                () -> { policy.setPermissions(/* Nothing */);
2307                        Runtime.getRuntime().exec("echo");},
2308                () -> { policy.setPermissions(
2309                                new RuntimePermission("getenv.bar"));
2310                        System.getenv("foo");});
2311 
2312         try {
2313             policy.setPermissions(new RuntimePermission("getenv.foo"));
2314             System.getenv("foo");
2315 
2316             policy.setPermissions(new RuntimePermission("getenv.*"));
2317             System.getenv("foo");
2318             System.getenv();
2319             new ProcessBuilder().environment();
2320         } catch (Throwable t) { unexpected(t); }
2321 
2322 
2323         final Permission execPermission
2324             = new FilePermission("<<ALL FILES>>", "execute");
2325 
2326         THROWS(SecurityException.class,
2327                () -> { // environment permission by itself insufficient
2328                        policy.setPermissions(new RuntimePermission("getenv.*"));
2329                        ProcessBuilder pb = new ProcessBuilder("env");
2330                        pb.environment().put("foo","bar");
2331                        pb.start();},
2332                () -> { // exec permission by itself insufficient
2333                        policy.setPermissions(execPermission);
2334                        ProcessBuilder pb = new ProcessBuilder("env");
2335                        pb.environment().put("foo","bar");
2336                        pb.start();});
2337 
2338         try {
2339             // Both permissions? OK.
2340             policy.setPermissions(new RuntimePermission("getenv.*"),
2341                                   execPermission);
2342             ProcessBuilder pb = new ProcessBuilder("env");
2343             pb.environment().put("foo","bar");
2344             Process p = pb.start();
2345             closeStreams(p);
2346         } catch (IOException e) { // OK
2347         } catch (Throwable t) { unexpected(t); }
2348 
2349         try {
2350             // Don't need environment permission unless READING environment
2351             policy.setPermissions(execPermission);
2352             Runtime.getRuntime().exec("env", new String[]{});
2353         } catch (IOException e) { // OK
2354         } catch (Throwable t) { unexpected(t); }
2355 
2356         try {
2357             // Don't need environment permission unless READING environment
2358             policy.setPermissions(execPermission);
2359             new ProcessBuilder("env").start();
2360         } catch (IOException e) { // OK
2361         } catch (Throwable t) { unexpected(t); }
2362 
2363         // Restore "normal" state without a security manager
2364         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2365         System.setSecurityManager(null);
2366 
2367         //----------------------------------------------------------------
2368         // Check that Process.isAlive() &
2369         // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
2370         //----------------------------------------------------------------
2371         try {
2372             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2373             childArgs.add("sleep");
2374             final Process p = new ProcessBuilder(childArgs).start();
2375             long start = System.nanoTime();
2376             if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
2377                 fail("Test failed: Process exited prematurely");
2378             }
2379             long end = System.nanoTime();
2380             // give waitFor(timeout) a wide berth (2s)
2381             System.out.printf(" waitFor process: delta: %d%n",(end - start) );
2382 
2383             if ((end - start) > TimeUnit.SECONDS.toNanos(2))
2384                 fail("Test failed: waitFor took too long (" + (end - start) + "ns)");
2385 
2386             p.destroy();
2387             p.waitFor();
2388 
2389             if (p.isAlive() ||
2390                 !p.waitFor(0, TimeUnit.MILLISECONDS))
2391             {
2392                 fail("Test failed: Process still alive - please terminate " +
2393                     p.toString() + " manually");
2394             }
2395         } catch (Throwable t) { unexpected(t); }
2396 
2397         //----------------------------------------------------------------
2398         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2399         // works as expected.
2400         //----------------------------------------------------------------
2401         try {
2402             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2403             childArgs.add("sleep");
2404             final Process p = new ProcessBuilder(childArgs).start();
2405             long start = System.nanoTime();
2406 
2407             p.waitFor(10, TimeUnit.MILLISECONDS);
2408 
2409             long end = System.nanoTime();
2410             if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
2411                 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
2412 
2413             p.destroy();
2414         } catch (Throwable t) { unexpected(t); }
2415 
2416         //----------------------------------------------------------------
2417         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2418         // interrupt works as expected, if interrupted while waiting.
2419         //----------------------------------------------------------------
2420         try {
2421             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2422             childArgs.add("sleep");
2423             final Process p = new ProcessBuilder(childArgs).start();
2424             final long start = System.nanoTime();
2425             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2426 
2427             final Thread thread = new Thread() {
2428                 public void run() {
2429                     try {
2430                         aboutToWaitFor.countDown();
2431                         Thread.currentThread().interrupt();
2432                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2433                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2434                     } catch (InterruptedException success) {
2435                     } catch (Throwable t) { unexpected(t); }
2436                 }
2437             };
2438 
2439             thread.start();
2440             aboutToWaitFor.await();
2441             thread.interrupt();
2442             thread.join(10L * 1000L);
2443             check(millisElapsedSince(start) < 10L * 1000L);
2444             check(!thread.isAlive());
2445             p.destroy();
2446         } catch (Throwable t) { unexpected(t); }
2447 
2448         //----------------------------------------------------------------
2449         // Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
2450         // interrupt works as expected, if interrupted while waiting.
2451         //----------------------------------------------------------------
2452         try {
2453             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2454             childArgs.add("sleep");
2455             final Process p = new ProcessBuilder(childArgs).start();
2456             final long start = System.nanoTime();
2457             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2458 
2459             final Thread thread = new Thread() {
2460                 public void run() {
2461                     try {
2462                         aboutToWaitFor.countDown();
2463                         Thread.currentThread().interrupt();
2464                         boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
2465                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2466                     } catch (InterruptedException success) {
2467                     } catch (Throwable t) { unexpected(t); }
2468                 }
2469             };
2470 
2471             thread.start();
2472             aboutToWaitFor.await();
2473             thread.interrupt();
2474             thread.join(10L * 1000L);
2475             check(millisElapsedSince(start) < 10L * 1000L);
2476             check(!thread.isAlive());
2477             p.destroy();
2478         } catch (Throwable t) { unexpected(t); }
2479 
2480         //----------------------------------------------------------------
2481         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2482         // interrupt works as expected, if interrupted before waiting.
2483         //----------------------------------------------------------------
2484         try {
2485             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2486             childArgs.add("sleep");
2487             final Process p = new ProcessBuilder(childArgs).start();
2488             final long start = System.nanoTime();
2489             final CountDownLatch threadStarted = new CountDownLatch(1);
2490 
2491             final Thread thread = new Thread() {
2492                 public void run() {
2493                     try {
2494                         threadStarted.countDown();
2495                         do { Thread.yield(); }
2496                         while (!Thread.currentThread().isInterrupted());
2497                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2498                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2499                     } catch (InterruptedException success) {
2500                     } catch (Throwable t) { unexpected(t); }
2501                 }
2502             };
2503 
2504             thread.start();
2505             threadStarted.await();
2506             thread.interrupt();
2507             thread.join(10L * 1000L);
2508             check(millisElapsedSince(start) < 10L * 1000L);
2509             check(!thread.isAlive());
2510             p.destroy();
2511         } catch (Throwable t) { unexpected(t); }
2512 
2513         //----------------------------------------------------------------
2514         // Check that Process.waitFor(timeout, null) throws NPE.
2515         //----------------------------------------------------------------
2516         try {
2517             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2518             childArgs.add("sleep");
2519             final Process p = new ProcessBuilder(childArgs).start();
2520             THROWS(NullPointerException.class,
2521                     () ->  p.waitFor(10L, null));
2522             THROWS(NullPointerException.class,
2523                     () ->  p.waitFor(0L, null));
2524             THROWS(NullPointerException.class,
2525                     () -> p.waitFor(-1L, null));
2526             // Terminate process and recheck after it exits
2527             p.destroy();
2528             p.waitFor();
2529             THROWS(NullPointerException.class,
2530                     () -> p.waitFor(10L, null));
2531             THROWS(NullPointerException.class,
2532                     () -> p.waitFor(0L, null));
2533             THROWS(NullPointerException.class,
2534                     () -> p.waitFor(-1L, null));
2535         } catch (Throwable t) { unexpected(t); }
2536 
2537         //----------------------------------------------------------------
2538         // Check that default implementation of Process.waitFor(timeout, null) throws NPE.
2539         //----------------------------------------------------------------
2540         try {
2541             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2542             childArgs.add("sleep");
2543             final Process proc = new ProcessBuilder(childArgs).start();
2544             final DelegatingProcess p = new DelegatingProcess(proc);
2545 
2546             THROWS(NullPointerException.class,
2547                     () ->  p.waitFor(10L, null));
2548             THROWS(NullPointerException.class,
2549                     () ->  p.waitFor(0L, null));
2550             THROWS(NullPointerException.class,
2551                     () ->  p.waitFor(-1L, null));
2552             // Terminate process and recheck after it exits
2553             p.destroy();
2554             p.waitFor();
2555             THROWS(NullPointerException.class,
2556                     () -> p.waitFor(10L, null));
2557             THROWS(NullPointerException.class,
2558                     () -> p.waitFor(0L, null));
2559             THROWS(NullPointerException.class,
2560                     () -> p.waitFor(-1L, null));
2561         } catch (Throwable t) { unexpected(t); }
2562 
2563         //----------------------------------------------------------------
2564         // Check the default implementation for
2565         // Process.waitFor(long, TimeUnit)
2566         //----------------------------------------------------------------
2567         try {
2568             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2569             childArgs.add("sleep");
2570             final Process proc = new ProcessBuilder(childArgs).start();
2571             DelegatingProcess p = new DelegatingProcess(proc);
2572             long start = System.nanoTime();
2573 
2574             p.waitFor(1000, TimeUnit.MILLISECONDS);
2575 
2576             long end = System.nanoTime();
2577             if ((end - start) < 500000000)
2578                 fail("Test failed: waitFor didn't take long enough");
2579 
2580             p.destroy();
2581 
2582             p.waitFor(1000, TimeUnit.MILLISECONDS);
2583         } catch (Throwable t) { unexpected(t); }
2584     }
2585 
2586     static void closeStreams(Process p) {
2587         try {
2588             p.getOutputStream().close();
2589             p.getInputStream().close();
2590             p.getErrorStream().close();
2591         } catch (Throwable t) { unexpected(t); }
2592     }
2593 
2594     //----------------------------------------------------------------
2595     // A Policy class designed to make permissions fiddling very easy.
2596     //----------------------------------------------------------------
2597     private static class Policy extends java.security.Policy {
2598         private Permissions perms;
2599 
2600         public void setPermissions(Permission...permissions) {
2601             perms = new Permissions();
2602             for (Permission permission : permissions)
2603                 perms.add(permission);
2604         }
2605 
2606         public Policy() { setPermissions(/* Nothing */); }
2607 
2608         public PermissionCollection getPermissions(CodeSource cs) {
2609             return perms;
2610         }
2611 
2612         public PermissionCollection getPermissions(ProtectionDomain pd) {
2613             return perms;
2614         }
2615 
2616         public boolean implies(ProtectionDomain pd, Permission p) {
2617             return perms.implies(p);
2618         }
2619 
2620         public void refresh() {}
2621     }
2622 
2623     private static class StreamAccumulator extends Thread {
2624         private final InputStream is;
2625         private final StringBuilder sb = new StringBuilder();
2626         private Throwable throwable = null;
2627 
2628         public String result () throws Throwable {
2629             if (throwable != null)
2630                 throw throwable;
2631             return sb.toString();
2632         }
2633 
2634         StreamAccumulator (InputStream is) {
2635             this.is = is;
2636         }
2637 
2638         public void run() {
2639             try {
2640                 Reader r = new InputStreamReader(is);
2641                 char[] buf = new char[4096];
2642                 int n;
2643                 while ((n = r.read(buf)) > 0) {
2644                     sb.append(buf,0,n);
2645                 }
2646             } catch (Throwable t) {
2647                 throwable = t;
2648             } finally {
2649                 try { is.close(); }
2650                 catch (Throwable t) { throwable = t; }
2651             }
2652         }
2653     }
2654 
2655     static ProcessResults run(ProcessBuilder pb) {
2656         try {
2657             return run(pb.start());
2658         } catch (Throwable t) { unexpected(t); return null; }
2659     }
2660 
2661     private static ProcessResults run(Process p) {
2662         Throwable throwable = null;
2663         int exitValue = -1;
2664         String out = "";
2665         String err = "";
2666 
2667         StreamAccumulator outAccumulator =
2668             new StreamAccumulator(p.getInputStream());
2669         StreamAccumulator errAccumulator =
2670             new StreamAccumulator(p.getErrorStream());
2671 
2672         try {
2673             outAccumulator.start();
2674             errAccumulator.start();
2675 
2676             exitValue = p.waitFor();
2677 
2678             outAccumulator.join();
2679             errAccumulator.join();
2680 
2681             out = outAccumulator.result();
2682             err = errAccumulator.result();
2683         } catch (Throwable t) {
2684             throwable = t;
2685         }
2686 
2687         return new ProcessResults(out, err, exitValue, throwable);
2688     }
2689 
2690     //----------------------------------------------------------------
2691     // Results of a command
2692     //----------------------------------------------------------------
2693     private static class ProcessResults {
2694         private final String out;
2695         private final String err;
2696         private final int exitValue;
2697         private final Throwable throwable;
2698 
2699         public ProcessResults(String out,
2700                               String err,
2701                               int exitValue,
2702                               Throwable throwable) {
2703             this.out = out;
2704             this.err = err;
2705             this.exitValue = exitValue;
2706             this.throwable = throwable;
2707         }
2708 
2709         public String out()          { return out; }
2710         public String err()          { return err; }
2711         public int exitValue()       { return exitValue; }
2712         public Throwable throwable() { return throwable; }
2713 
2714         public String toString() {
2715             StringBuilder sb = new StringBuilder();
2716             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2717                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2718                 .append("exitValue = " + exitValue + "\n");
2719             if (throwable != null)
2720                 sb.append(throwable.getStackTrace());
2721             return sb.toString();
2722         }
2723     }
2724 
2725     //--------------------- Infrastructure ---------------------------
2726     static volatile int passed = 0, failed = 0;
2727     static void pass() {passed++;}
2728     static void fail() {failed++; Thread.dumpStack();}
2729     static void fail(String msg) {System.err.println(msg); fail();}
2730     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
2731     static void check(boolean cond) {if (cond) pass(); else fail();}
2732     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
2733     static void equal(Object x, Object y) {
2734         if (x == null ? y == null : x.equals(y)) pass();
2735         else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");}
2736 
2737     public static void main(String[] args) throws Throwable {
2738         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2739         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2740         if (failed > 0) throw new AssertionError("Some tests failed");}
2741     interface Fun {void f() throws Throwable;}
2742     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2743         for (Fun f : fs)
2744             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2745             catch (Throwable t) {
2746                 if (k.isAssignableFrom(t.getClass())) pass();
2747                 else unexpected(t);}}
2748 
2749     static boolean isLocked(final Object monitor, final long millis) throws InterruptedException {
2750         return new Thread() {
2751             volatile boolean unlocked;
2752 
2753             @Override
2754             public void run() {
2755                 synchronized (monitor) { unlocked = true; }
2756             }
2757 
2758             boolean isLocked() throws InterruptedException {
2759                 start();
2760                 join(millis);
2761                 return !unlocked;
2762             }
2763         }.isLocked();
2764     }
2765 }