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