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