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