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