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