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