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