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